C2X adds a %b printf format to print integers in binary (analogous to
%x, including %#b printing a leading 0b on nonzero integers), with
recommended practice for a corresponding %B (where %#B uses 0B instead
of 0b) where that doesn't conflict with existing implementation
extensions.  See N2630 for details (accepted for C2X, not yet in the
latest working draft).  There is also a scanf %b format.

Add corresponding format checking support (%b accepted by -std=c2x
-Wformat -pedantic, %B considered an extension to be diagnosed with
-Wformat -pedantic).  glibc support for the printf formats has been
proposed at
<https://sourceware.org/pipermail/libc-alpha/2021-October/131764.html>
(scanf support to be done in a separate patch).

Note that this does not add any support for these formats to the code
for bounding the amount of output produces by a printf function,
although that would also be useful.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.  Applied to 
mainline.

gcc/c-family/
        * c-format.c (print_char_table): Add %b and %B formats.
        (scan_char_table): Add %b format.
        * c-format.h (T2X_UI, T2X_UL, T2X_ULL, T2X_US, T2X_UC, T2X_ST)
        (T2X_UPD, T2X_UIM): New macros.

gcc/testsuite/
        * gcc.dg/format/c11-printf-1.c, gcc.dg/format/c11-scanf-1.c,
        gcc.dg/format/c2x-printf-1.c, gcc.dg/format/c2x-scanf-1.c,
        gcc.dg/format/ext-9.c, gcc.dg/format/ext-10.c: New tests.

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index ca66c81f716..c27faf71676 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -712,11 +712,14 @@ static const format_char_info print_char_table[] =
   /* C99 conversion specifiers.  */
   { "F",   0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  
BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "",   
NULL },
   { "aA",  0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  
BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64,  TEX_D128 }, "-wp0 +#",   "",   
NULL },
+  /* C2X conversion specifiers.  */
+  { "b",   0, STD_C2X, { T2X_UI,  T2X_UC,  T2X_US,  T2X_UL,  T2X_ULL, TEX_ULL, 
T2X_ST,  T2X_UPD, T2X_UIM, BADLEN,  BADLEN,  BADLEN }, "-wp0#",     "i",  NULL 
},
   /* X/Open conversion specifiers.  */
   { "C",   0, STD_EXT, { TEX_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  
BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-w",        "",   NULL 
},
   { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  
BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "R",  NULL 
},
   /* GNU conversion specifiers.  */
   { "m",   0, STD_EXT, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  
BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "",   NULL 
},
+  { "B",   0, STD_EXT, { T2X_UI,  T2X_UC,  T2X_US,  T2X_UL,  T2X_ULL, TEX_ULL, 
T2X_ST,  T2X_UPD, T2X_UIM, BADLEN,  BADLEN,  BADLEN }, "-wp0#",     "i",  NULL 
},
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
@@ -876,6 +879,8 @@ static const format_char_info scan_char_table[] =
   /* C99 conversion specifiers.  */
   { "F",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  
BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "*w'",  "W",   NULL },
   { "aA",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD, 
 BADLEN,  BADLEN,  BADLEN,  TEX_D32,  TEX_D64,  TEX_D128 }, "*w'",  "W",   NULL 
},
+  /* C2X conversion specifiers.  */
+  { "b",     1, STD_C2X, { T2X_UI,  T2X_UC,  T2X_US,  T2X_UL,  T2X_ULL, 
TEX_ULL, T2X_ST,  T2X_UPD, T2X_UIM, BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   
NULL },
   /* X/Open conversion specifiers.  */
   { "C",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  
BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*mw",   "W",   
NULL },
   { "S",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  
BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*amw",  "W",   
NULL },
diff --git a/gcc/c-family/c-format.h b/gcc/c-family/c-format.h
index 2f926f4c8c1..2b5012ee3a9 100644
--- a/gcc/c-family/c-format.h
+++ b/gcc/c-family/c-format.h
@@ -278,13 +278,17 @@ struct format_kind_info
 #define T89_S  { STD_C89, NULL, T_S }
 #define T_UI   &unsigned_type_node
 #define T89_UI { STD_C89, NULL, T_UI }
+#define T2X_UI { STD_C2X, NULL, T_UI }
 #define T_UL   &long_unsigned_type_node
 #define T89_UL { STD_C89, NULL, T_UL }
+#define T2X_UL { STD_C2X, NULL, T_UL }
 #define T_ULL  &long_long_unsigned_type_node
 #define T9L_ULL        { STD_C9L, NULL, T_ULL }
+#define T2X_ULL        { STD_C2X, NULL, T_ULL }
 #define TEX_ULL        { STD_EXT, NULL, T_ULL }
 #define T_US   &short_unsigned_type_node
 #define T89_US { STD_C89, NULL, T_US }
+#define T2X_US { STD_C2X, NULL, T_US }
 #define T_F    &float_type_node
 #define T89_F  { STD_C89, NULL, T_F }
 #define T99_F  { STD_C99, NULL, T_F }
@@ -300,6 +304,7 @@ struct format_kind_info
 #define T99_SC { STD_C99, NULL, T_SC }
 #define T_UC   &unsigned_char_type_node
 #define T99_UC { STD_C99, NULL, T_UC }
+#define T2X_UC { STD_C2X, NULL, T_UC }
 #define T_V    &void_type_node
 #define T89_G   { STD_C89, NULL, &local_gimple_ptr_node }
 #define T_CGRAPH_NODE   { STD_C89, NULL, &local_cgraph_node_ptr_node }
@@ -314,16 +319,19 @@ struct format_kind_info
 #define TEX_WI { STD_EXT, "wint_t", T_WI }
 #define T_ST    &size_type_node
 #define T99_ST { STD_C99, "size_t", T_ST }
+#define T2X_ST { STD_C2X, "size_t", T_ST }
 #define T_SST   &signed_size_type_node
 #define T99_SST        { STD_C99, "signed size_t", T_SST }
 #define T_PD    &ptrdiff_type_node
 #define T99_PD { STD_C99, "ptrdiff_t", T_PD }
 #define T_UPD   &unsigned_ptrdiff_type_node
 #define T99_UPD        { STD_C99, "unsigned ptrdiff_t", T_UPD }
+#define T2X_UPD        { STD_C2X, "unsigned ptrdiff_t", T_UPD }
 #define T_IM    &intmax_type_node
 #define T99_IM { STD_C99, "intmax_t", T_IM }
 #define T_UIM   &uintmax_type_node
 #define T99_UIM        { STD_C99, "uintmax_t", T_UIM }
+#define T2X_UIM        { STD_C2X, "uintmax_t", T_UIM }
 #define T_D32   &dfloat32_type_node
 #define TEX_D32 { STD_EXT, "_Decimal32", T_D32 }
 #define T_D64   &dfloat64_type_node
diff --git a/gcc/testsuite/gcc.dg/format/c11-printf-1.c 
b/gcc/testsuite/gcc.dg/format/c11-printf-1.c
new file mode 100644
index 00000000000..7b8a9928098
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/c11-printf-1.c
@@ -0,0 +1,13 @@
+/* Test for printf formats: rejection of C2X (and C2X-recommended) formats in
+   pedantic mode.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic -Wformat" } */
+
+#include "format.h"
+
+void
+foo (int i)
+{
+  printf ("%b", i); /* { dg-warning "C" } */
+  printf ("%B", i); /* { dg-warning "C" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/c11-scanf-1.c 
b/gcc/testsuite/gcc.dg/format/c11-scanf-1.c
new file mode 100644
index 00000000000..d2b9bfb23b5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/c11-scanf-1.c
@@ -0,0 +1,11 @@
+/* Test for printf formats: rejection of C2X formats in pedantic mode.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic -Wformat" } */
+
+#include "format.h"
+
+void
+foo (unsigned int *uip)
+{
+  scanf ("%b", uip); /* { dg-warning "C" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/c2x-printf-1.c 
b/gcc/testsuite/gcc.dg/format/c2x-printf-1.c
new file mode 100644
index 00000000000..3ae7713ff05
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/c2x-printf-1.c
@@ -0,0 +1,26 @@
+/* Test for printf formats.  Formats using C2X features.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic -Wformat" } */
+
+#include "format.h"
+
+void
+foo (unsigned int u, unsigned short us, unsigned char uc, unsigned long ul,
+     unsigned long long ull, uintmax_t uj, size_t z, unsigned_ptrdiff_t ut)
+{
+  /* Use of %b with each length modifier and other valid features.  */
+  printf ("%b %hb %hhb %lb %llb %jb %zb %tb\n", u, us, uc, ul, ull, uj, z, ut);
+  printf ("%*.*llb\n", 1, 2, ull);
+  printf ("%-b\n", u);
+  printf ("%#b\n", u);
+  printf ("%08b\n", u);
+  /* Flags valid on signed conversions only.  */
+  printf ("%+b\n", u); /* { dg-warning "flag" } */
+  printf ("% b\n", u); /* { dg-warning "flag" } */
+  /* Flags ignored in certain combinations.  */
+  printf ("%-08b\n", u); /* { dg-warning "ignored" } */
+  printf ("%08.5b\n", u); /* { dg-warning "ignored" } */
+  /* Use of 'L' and 'q' for long long is an extension.  */
+  printf ("%Lb", ull); /* { dg-warning "does not support" } */
+  printf ("%qb", ull); /* { dg-warning "does not support" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/c2x-scanf-1.c 
b/gcc/testsuite/gcc.dg/format/c2x-scanf-1.c
new file mode 100644
index 00000000000..f46a7152769
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/c2x-scanf-1.c
@@ -0,0 +1,17 @@
+/* Test for scanf formats.  Formats using C2X features.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic -Wformat" } */
+
+#include "format.h"
+
+void
+foo (unsigned int *uip, unsigned short int *uhp, unsigned char *uhhp,
+     unsigned long int *ulp, unsigned long long *ullp, uintmax_t *ujp,
+     size_t *zp, unsigned_ptrdiff_t *utp)
+{
+  scanf ("%*b");
+  scanf ("%2b", uip);
+  scanf ("%hb%hhb%lb%llb%jb%zb%tb", uhp, uhhp, ulp, ullp, ujp, zp, utp);
+  scanf ("%Lb", ullp); /* { dg-warning "does not support" } */
+  scanf ("%qb", ullp); /* { dg-warning "does not support" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/ext-10.c 
b/gcc/testsuite/gcc.dg/format/ext-10.c
new file mode 100644
index 00000000000..370ea86b42e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/ext-10.c
@@ -0,0 +1,13 @@
+/* Test for scanf format extensions using formats from C2X.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x -Wformat" } */
+
+#include "format.h"
+
+void
+foo (u_quad_t *uqp, unsigned long long int *ullp)
+{
+  /* Deprecated length modifiers with %b.  */
+  scanf ("%qb", uqp);
+  scanf ("%Lb", ullp);
+}
diff --git a/gcc/testsuite/gcc.dg/format/ext-9.c 
b/gcc/testsuite/gcc.dg/format/ext-9.c
new file mode 100644
index 00000000000..15f59e21dbe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/ext-9.c
@@ -0,0 +1,29 @@
+/* Test for printf format extensions using formats from or recommended by
+   C2X.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x -Wformat" } */
+
+#include "format.h"
+
+void
+foo (u_quad_t uq, unsigned int u, unsigned short us, unsigned char uc,
+     unsigned long ul, unsigned long long ull, uintmax_t uj, size_t z,
+     unsigned_ptrdiff_t ut)
+{
+  /* Deprecated length modifiers with %b and %B.  */
+  printf ("%qb%qB", uq, uq);
+  printf ("%Lb%LB", ull, ull);
+  printf ("%Zb%ZB", z, z);
+  /* Use of %B in cases valid for %b.  */
+  printf ("%B %hB %hhB %lB %llB %jB %zB %tB\n", u, us, uc, ul, ull, uj, z, ut);
+  printf ("%*.*llB\n", 1, 2, ull);
+  printf ("%-B\n", u);
+  printf ("%#B\n", u);
+  printf ("%08B\n", u);
+  /* Flags valid on signed conversions only.  */
+  printf ("%+B\n", u); /* { dg-warning "flag" } */
+  printf ("% B\n", u); /* { dg-warning "flag" } */
+  /* Flags ignored in certain combinations.  */
+  printf ("%-08B\n", u); /* { dg-warning "ignored" } */
+  printf ("%08.5B\n", u); /* { dg-warning "ignored" } */
+}

-- 
Joseph S. Myers
jos...@codesourcery.com

Reply via email to