Hi!

As I mentioned in my stdckdint.h mail, I think having __ prefixed
keywords for the typeof_unqual keyword which can be used in earlier
language modes can be useful, not all code can be switched to C23
right away.

The following patch implements that.  It keeps the non-C23 behavior
for it for the _Noreturn functions to stay compatible with how
__typeof__ behaves.

I think we don't need it for C++, in C++ we have standard
traits to remove qualifiers etc.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2023-06-12  Jakub Jelinek  <ja...@redhat.com>

gcc/
        * doc/extend.texi (Typeof): Document typeof_unqual
        and __typeof_unqual__.
gcc/c-family/
        * c-common.cc (c_common_reswords): Add __typeof_unqual
        and __typeof_unqual__ spellings of typeof_unqual.
gcc/c/
        * c-parser.cc (c_parser_typeof_specifier): Handle
        __typeof_unqual and __typeof_unqual__ as !is_std.
gcc/testsuite/
        * gcc.dg/c11-typeof-2.c: New test.
        * gcc.dg/c11-typeof-3.c: New test.
        * gcc.dg/gnu11-typeof-3.c: New test.
        * gcc.dg/gnu11-typeof-4.c: New test.

--- gcc/c-family/c-common.cc.jj 2023-05-20 15:31:09.070663296 +0200
+++ gcc/c-family/c-common.cc    2023-06-10 19:20:11.106910976 +0200
@@ -420,6 +420,8 @@ const struct c_common_resword c_common_r
   { "__transaction_cancel", RID_TRANSACTION_CANCEL, 0 },
   { "__typeof",                RID_TYPEOF,     0 },
   { "__typeof__",      RID_TYPEOF,     0 },
+  { "__typeof_unqual", RID_TYPEOF_UNQUAL, D_CONLY },
+  { "__typeof_unqual__", RID_TYPEOF_UNQUAL, D_CONLY },
   { "__volatile",      RID_VOLATILE,   0 },
   { "__volatile__",    RID_VOLATILE,   0 },
   { "__GIMPLE",                RID_GIMPLE,     D_CONLY },
--- gcc/c/c-parser.cc.jj        2023-06-06 20:02:35.587211846 +0200
+++ gcc/c/c-parser.cc   2023-06-10 19:22:15.577205685 +0200
@@ -4126,7 +4126,8 @@ c_parser_typeof_specifier (c_parser *par
     {
       gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF_UNQUAL));
       is_unqual = true;
-      is_std = true;
+      tree spelling = c_parser_peek_token (parser)->value;
+      is_std = strcmp (IDENTIFIER_POINTER (spelling), "typeof_unqual") == 0;
     }
   c_parser_consume_token (parser);
   c_inhibit_evaluation_warnings++;
--- gcc/doc/extend.texi.jj      2023-06-06 20:02:35.643211062 +0200
+++ gcc/doc/extend.texi 2023-06-10 19:58:26.197478291 +0200
@@ -843,6 +843,13 @@ Thus, @code{array (pointer (char), 4)} i
 pointers to @code{char}.
 @end itemize
 
+The ISO C2X operator @code{typeof_unqual} is available in ISO C2X mode
+and its result is the non-atomic unqualified version of what @code{typeof}
+operator returns.  Alternate spelling @code{__typeof_unqual__} is
+available in all C modes and provides non-atomic unqualified version of
+what @code{__typeof__} operator returns.
+@xref{Alternate Keywords}.
+
 In GNU C, but not GNU C++, you may also declare the type of a variable
 as @code{__auto_type}.  In that case, the declaration must declare
 only one variable, whose declarator must just be an identifier, the
--- gcc/testsuite/gcc.dg/c11-typeof-2.c.jj      2023-06-10 19:27:38.675779747 
+0200
+++ gcc/testsuite/gcc.dg/c11-typeof-2.c 2023-06-10 19:42:27.450606301 +0200
@@ -0,0 +1,177 @@
+/* Test GNU extensions __typeof__ and __typeof_unqual__.  Valid code.  */
+/* { dg-do run } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+int i;
+extern __typeof__ (i) i;
+extern __typeof (int) i;
+extern __typeof_unqual__ (i) i;
+extern __typeof_unqual (int) i;
+
+volatile int vi;
+extern __typeof__ (volatile int) vi;
+extern __typeof (vi) vi;
+
+extern __typeof_unqual__ (volatile int) i;
+extern __typeof_unqual__ (vi) i;
+extern __typeof__ ((const int) vi) i;
+extern __typeof ((volatile int) vi) i;
+
+const int ci;
+extern __typeof (const int) ci;
+extern __typeof (ci) ci;
+
+extern __typeof_unqual (const int) i;
+extern __typeof_unqual (ci) i;
+extern __typeof__ ((const int) ci) i;
+extern __typeof__ (+ci) i;
+extern __typeof (0, ci) i;
+extern __typeof__ (1 ? ci : ci) i;
+extern __typeof (0) i;
+
+const int fci (void);
+extern __typeof__ (fci ()) i;
+
+_Atomic int ai;
+extern __typeof (_Atomic int) ai;
+extern __typeof__ (_Atomic (int)) ai;
+extern __typeof (ai) ai;
+
+extern __typeof_unqual__ (_Atomic int) i;
+extern __typeof_unqual (_Atomic (int)) i;
+extern __typeof_unqual__ (ai) i;
+extern __typeof (+ai) i;
+extern __typeof__ ((_Atomic int) ai) i;
+extern __typeof__ (0, ai) i;
+extern __typeof (1 ? ai : ai) i;
+
+_Atomic int fai (void);
+extern __typeof__ (fai ()) i;
+
+_Atomic const volatile int acvi;
+extern __typeof (int volatile const _Atomic) acvi;
+extern __typeof (acvi) acvi;
+extern const _Atomic volatile __typeof (acvi) acvi;
+extern _Atomic volatile __typeof__ (ci) acvi;
+extern _Atomic const __typeof (vi) acvi;
+extern const __typeof__ (ai) volatile acvi;
+
+extern __typeof_unqual (acvi) i;
+extern __typeof_unqual__ (__typeof (acvi)) i;
+extern __typeof_unqual (_Atomic __typeof_unqual__ (acvi)) i;
+
+extern _Atomic __typeof_unqual (acvi) ai;
+
+char c;
+volatile char vc;
+volatile char *pvc;
+volatile char *const cpvc;
+const char *pcc;
+const char *volatile vpcc;
+__typeof (*vpcc) cc;
+
+extern __typeof__ (*cpvc) vc;
+extern __typeof_unqual (*cpvc) c;
+extern __typeof_unqual__ (cpvc) pvc;
+extern __typeof_unqual__ (vpcc) pcc;
+extern const char cc;
+
+extern __typeof (++vi) i;
+extern __typeof (++ai) i;
+extern __typeof__ (--vi) i;
+extern __typeof (--ai) i;
+extern __typeof__ (vi++) i;
+extern __typeof__ (ai++) i;
+extern __typeof (vi--) i;
+extern __typeof__ (ai--) i;
+
+_Bool b;
+volatile _Bool vb;
+_Atomic _Bool ab;
+extern __typeof__ (++vb) b;
+extern __typeof__ (++ab) b;
+extern __typeof (--vb) b;
+extern __typeof__ (--ab) b;
+extern __typeof (vb++) b;
+extern __typeof (ab++) b;
+extern __typeof__ (vb--) b;
+extern __typeof (ab--) b;
+
+extern __typeof__ (vc = 1) c;
+extern __typeof__ (vpcc = 0) pcc;
+extern __typeof (ai *= 2) i;
+
+int s = sizeof (__typeof__ (int (*)[++i]));
+
+void *vp;
+
+extern void abort (void);
+extern void exit (int);
+
+extern int only_used_in_typeof;
+
+static int not_defined (void);
+
+__typeof (i)
+main (__typeof (*vp))
+{
+  volatile __typeof__ (only_used_in_typeof) ii = 2;
+  if (ii != 2)
+    abort ();
+  const __typeof__ (not_defined ()) jj = 3;
+  if (jj != 3)
+    abort ();
+  unsigned int u = 1;
+  __typeof__ (u) u2 = 0;
+  __typeof (int (*)[++u2]) p = 0;
+  if (u2 != 1)
+    abort ();
+  if (sizeof (*p) != sizeof (int))
+    abort ();
+  __typeof_unqual (int (*)[++u2]) q = 0;
+  if (u2 != 2)
+    abort ();
+  if (sizeof (*q) != 2 * sizeof (int))
+    abort ();
+  if (sizeof (*p) != sizeof (int))
+    abort ();
+  __typeof (++u2) u3 = 1;
+  if (u2 != u + u3)
+    abort ();
+  __typeof_unqual__ (++u2) u4 = 2;
+  if (u2 != u4)
+    abort ();
+  u = sizeof (__typeof__ (int (*)[++u2]));
+  if (u2 != 2)
+    abort ();
+  u = sizeof (__typeof_unqual (int (*)[++u2]));
+  if (u2 != 2)
+    abort ();
+  __typeof ((int (*)[++u2]) 0) q2;
+  if (u2 != 3)
+    abort ();
+  __typeof ((void) 0, (int (*)[++u2]) 0) q3;
+  if (u2 != 4)
+    abort ();
+  __typeof__ ((int (*)[++u2]) 0, 0) q4;
+  if (u2 != 4)
+    abort ();
+  __typeof_unqual ((int (*)[++u2]) 0) q5;
+  if (u2 != 5)
+    abort ();
+  __typeof_unqual__ ((void) 0, (int (*)[++u2]) 0) q6;
+  if (u2 != 6)
+    abort ();
+  __typeof_unqual__ ((int (*)[++u2]) 0, 0) q7;
+  if (u2 != 6)
+    abort ();
+  int a1[6], a2[6];
+  int (*pa)[u2] = &a1;
+  __typeof (pa = &a2) pp;
+  if (pa != &a2)
+    abort ();
+  __typeof_unqual (pa = &a1) pp2;
+  if (pa != &a1)
+    abort ();
+  exit (0);
+}
--- gcc/testsuite/gcc.dg/c11-typeof-3.c.jj      2023-06-10 19:27:42.003734166 
+0200
+++ gcc/testsuite/gcc.dg/c11-typeof-3.c 2023-06-10 19:46:38.041174992 +0200
@@ -0,0 +1,58 @@
+/* Test GNU extensions __typeof__ and __typeof_unqual__.  Invalid code.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+struct s { int i : 2; } x;
+union u { unsigned int j : 1; } y;
+
+__typeof__ (x.i) j; /* { dg-error "applied to a bit-field" } */
+__typeof_unqual__ (x.i) j2; /* { dg-error "applied to a bit-field" } */
+__typeof (y.j) j3; /* { dg-error "applied to a bit-field" } */
+__typeof_unqual (y.j) j4; /* { dg-error "applied to a bit-field" } */
+
+static int ok (void);
+static int also_ok (void);
+static int not_defined (void); /* { dg-error "used but never defined" } */
+static int also_not_defined (void); /* { dg-error "used but never defined" } */
+
+_Noreturn void nf1 (void);
+__attribute__((noreturn)) void nf2 (void);
+void fg (void) {}
+__typeof__ (&nf1) pnf1 = fg; /* { dg-error "qualified function pointer from 
unqualified" } */
+__typeof (&nf2) pnf2 = fg; /* { dg-error "qualified function pointer from 
unqualified" } */
+extern void (*pnf1) (void); /* { dg-error "conflicting types for" } */
+extern void (*pnf2) (void); /* { dg-error "conflicting types for" } */
+extern __typeof (nf1) *pnf1; /* { dg-error "conflicting types for" } */
+extern __typeof (nf1) *pnf2; /* { dg-error "conflicting types for" } */
+extern __typeof__ (nf2) *pnf1; /* { dg-error "conflicting types for" } */
+extern __typeof__ (nf2) *pnf2; /* { dg-error "conflicting types for" } */
+__typeof (*&nf1) fg2, fg2a, fg2b; /* { dg-error "ISO C forbids qualified 
function types" } */
+__typeof__ (*&nf2) fg3, fg3a, fg3b; /* { dg-error "ISO C forbids qualified 
function types" } */
+__typeof (nf1) fg4, fg4a, fg4b;
+__typeof__ (nf2) fg5, fg5a, fg5b;
+
+extern void abort (void);
+
+void fg2 (void) {} /* { dg-error "conflicting type qualifiers for" } */
+_Noreturn void fg2a (void) { abort (); } /* { dg-error "conflicting type 
qualifiers for" } */
+__attribute__((noreturn)) void fg2b (void) { abort (); } /* { dg-error 
"conflicting type qualifiers for" } */
+void fg3 (void) {} /* { dg-error "conflicting type qualifiers for" } */
+_Noreturn void fg3a (void) { abort (); } /* { dg-error "conflicting type 
qualifiers for" } */
+__attribute__((noreturn)) void fg3b (void) { abort (); } /* { dg-error 
"conflicting type qualifiers for" } */
+void fg4 (void) {}
+_Noreturn void fg4a (void) { abort (); }
+__attribute__((noreturn)) void fg4b (void) { abort (); }
+void fg5 (void) {}
+_Noreturn void fg5a (void) { abort (); }
+__attribute__((noreturn)) void fg5b (void) { abort (); }
+
+void
+f (void)
+{
+  __typeof__ (ok ()) x = 2;
+  __typeof_unqual (also_ok ()) y = 2;
+  int a[2];
+  int (*p)[x] = &a;
+  __typeof (p + not_defined ()) q;
+  __typeof_unqual__ (p + also_not_defined ()) q2;
+}
--- gcc/testsuite/gcc.dg/gnu11-typeof-3.c.jj    2023-06-10 19:39:59.936626612 
+0200
+++ gcc/testsuite/gcc.dg/gnu11-typeof-3.c       2023-06-10 19:47:32.861424347 
+0200
@@ -0,0 +1,177 @@
+/* Test GNU extensions __typeof__ and __typeof_unqual__.  Valid code.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu11" } */
+
+int i;
+extern __typeof__ (i) i;
+extern __typeof (int) i;
+extern __typeof_unqual__ (i) i;
+extern __typeof_unqual (int) i;
+
+volatile int vi;
+extern __typeof__ (volatile int) vi;
+extern __typeof (vi) vi;
+
+extern __typeof_unqual__ (volatile int) i;
+extern __typeof_unqual__ (vi) i;
+extern __typeof__ ((const int) vi) i;
+extern __typeof ((volatile int) vi) i;
+
+const int ci;
+extern __typeof (const int) ci;
+extern __typeof (ci) ci;
+
+extern __typeof_unqual (const int) i;
+extern __typeof_unqual (ci) i;
+extern __typeof__ ((const int) ci) i;
+extern __typeof__ (+ci) i;
+extern __typeof (0, ci) i;
+extern __typeof__ (1 ? ci : ci) i;
+extern __typeof (0) i;
+
+const int fci (void);
+extern __typeof__ (fci ()) i;
+
+_Atomic int ai;
+extern __typeof (_Atomic int) ai;
+extern __typeof__ (_Atomic (int)) ai;
+extern __typeof (ai) ai;
+
+extern __typeof_unqual__ (_Atomic int) i;
+extern __typeof_unqual (_Atomic (int)) i;
+extern __typeof_unqual__ (ai) i;
+extern __typeof (+ai) i;
+extern __typeof__ ((_Atomic int) ai) i;
+extern __typeof__ (0, ai) i;
+extern __typeof (1 ? ai : ai) i;
+
+_Atomic int fai (void);
+extern __typeof__ (fai ()) i;
+
+_Atomic const volatile int acvi;
+extern __typeof (int volatile const _Atomic) acvi;
+extern __typeof (acvi) acvi;
+extern const _Atomic volatile __typeof (acvi) acvi;
+extern _Atomic volatile __typeof__ (ci) acvi;
+extern _Atomic const __typeof (vi) acvi;
+extern const __typeof__ (ai) volatile acvi;
+
+extern __typeof_unqual (acvi) i;
+extern __typeof_unqual__ (__typeof (acvi)) i;
+extern __typeof_unqual (_Atomic __typeof_unqual__ (acvi)) i;
+
+extern _Atomic __typeof_unqual (acvi) ai;
+
+char c;
+volatile char vc;
+volatile char *pvc;
+volatile char *const cpvc;
+const char *pcc;
+const char *volatile vpcc;
+__typeof (*vpcc) cc;
+
+extern __typeof__ (*cpvc) vc;
+extern __typeof_unqual (*cpvc) c;
+extern __typeof_unqual__ (cpvc) pvc;
+extern __typeof_unqual__ (vpcc) pcc;
+extern const char cc;
+
+extern __typeof (++vi) i;
+extern __typeof (++ai) i;
+extern __typeof__ (--vi) i;
+extern __typeof (--ai) i;
+extern __typeof__ (vi++) i;
+extern __typeof__ (ai++) i;
+extern __typeof (vi--) i;
+extern __typeof__ (ai--) i;
+
+_Bool b;
+volatile _Bool vb;
+_Atomic _Bool ab;
+extern __typeof__ (++vb) b;
+extern __typeof__ (++ab) b;
+extern __typeof (--vb) b;
+extern __typeof__ (--ab) b;
+extern __typeof (vb++) b;
+extern __typeof (ab++) b;
+extern __typeof__ (vb--) b;
+extern __typeof (ab--) b;
+
+extern __typeof__ (vc = 1) c;
+extern __typeof__ (vpcc = 0) pcc;
+extern __typeof (ai *= 2) i;
+
+int s = sizeof (__typeof__ (int (*)[++i]));
+
+void *vp;
+
+extern void abort (void);
+extern void exit (int);
+
+extern int only_used_in_typeof;
+
+static int not_defined (void);
+
+__typeof (i)
+main (__typeof (*vp))
+{
+  volatile __typeof__ (only_used_in_typeof) ii = 2;
+  if (ii != 2)
+    abort ();
+  const __typeof__ (not_defined ()) jj = 3;
+  if (jj != 3)
+    abort ();
+  unsigned int u = 1;
+  __typeof__ (u) u2 = 0;
+  __typeof (int (*)[++u2]) p = 0;
+  if (u2 != 1)
+    abort ();
+  if (sizeof (*p) != sizeof (int))
+    abort ();
+  __typeof_unqual (int (*)[++u2]) q = 0;
+  if (u2 != 2)
+    abort ();
+  if (sizeof (*q) != 2 * sizeof (int))
+    abort ();
+  if (sizeof (*p) != sizeof (int))
+    abort ();
+  __typeof (++u2) u3 = 1;
+  if (u2 != u + u3)
+    abort ();
+  __typeof_unqual__ (++u2) u4 = 2;
+  if (u2 != u4)
+    abort ();
+  u = sizeof (__typeof__ (int (*)[++u2]));
+  if (u2 != 2)
+    abort ();
+  u = sizeof (__typeof_unqual (int (*)[++u2]));
+  if (u2 != 2)
+    abort ();
+  __typeof ((int (*)[++u2]) 0) q2;
+  if (u2 != 3)
+    abort ();
+  __typeof ((void) 0, (int (*)[++u2]) 0) q3;
+  if (u2 != 4)
+    abort ();
+  __typeof__ ((int (*)[++u2]) 0, 0) q4;
+  if (u2 != 4)
+    abort ();
+  __typeof_unqual ((int (*)[++u2]) 0) q5;
+  if (u2 != 5)
+    abort ();
+  __typeof_unqual__ ((void) 0, (int (*)[++u2]) 0) q6;
+  if (u2 != 6)
+    abort ();
+  __typeof_unqual__ ((int (*)[++u2]) 0, 0) q7;
+  if (u2 != 6)
+    abort ();
+  int a1[6], a2[6];
+  int (*pa)[u2] = &a1;
+  __typeof (pa = &a2) pp;
+  if (pa != &a2)
+    abort ();
+  __typeof_unqual (pa = &a1) pp2;
+  if (pa != &a1)
+    abort ();
+  exit (0);
+}
--- gcc/testsuite/gcc.dg/gnu11-typeof-4.c.jj    2023-06-10 19:40:04.211568056 
+0200
+++ gcc/testsuite/gcc.dg/gnu11-typeof-4.c       2023-06-10 19:50:07.819302529 
+0200
@@ -0,0 +1,58 @@
+/* Test GNU extensions __typeof__ and __typeof_unqual__.  Invalid code.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu11" } */
+
+struct s { int i : 2; } x;
+union u { unsigned int j : 1; } y;
+
+__typeof__ (x.i) j; /* { dg-error "applied to a bit-field" } */
+__typeof_unqual__ (x.i) j2; /* { dg-error "applied to a bit-field" } */
+__typeof (y.j) j3; /* { dg-error "applied to a bit-field" } */
+__typeof_unqual (y.j) j4; /* { dg-error "applied to a bit-field" } */
+
+static int ok (void);
+static int also_ok (void);
+static int not_defined (void); /* { dg-warning "used but never defined" } */
+static int also_not_defined (void); /* { dg-warning "used but never defined" } 
*/
+
+_Noreturn void nf1 (void);
+__attribute__((noreturn)) void nf2 (void);
+void fg (void) {}
+__typeof__ (&nf1) pnf1 = fg; /* { dg-warning "qualified function pointer from 
unqualified" } */
+__typeof (&nf2) pnf2 = fg; /* { dg-warning "qualified function pointer from 
unqualified" } */
+extern void (*pnf1) (void); /* { dg-error "conflicting types for" } */
+extern void (*pnf2) (void); /* { dg-error "conflicting types for" } */
+extern __typeof (nf1) *pnf1; /* { dg-error "conflicting types for" } */
+extern __typeof (nf1) *pnf2; /* { dg-error "conflicting types for" } */
+extern __typeof__ (nf2) *pnf1; /* { dg-error "conflicting types for" } */
+extern __typeof__ (nf2) *pnf2; /* { dg-error "conflicting types for" } */
+__typeof (*&nf1) fg2, fg2a, fg2b;
+__typeof__ (*&nf2) fg3, fg3a, fg3b;
+__typeof (nf1) fg4, fg4a, fg4b;
+__typeof__ (nf2) fg5, fg5a, fg5b;
+
+extern void abort (void);
+
+void fg2 (void) {} /* { dg-error "conflicting type qualifiers for" } */
+_Noreturn void fg2a (void) { abort (); } /* { dg-error "conflicting type 
qualifiers for" } */
+__attribute__((noreturn)) void fg2b (void) { abort (); } /* { dg-error 
"conflicting type qualifiers for" } */
+void fg3 (void) {} /* { dg-error "conflicting type qualifiers for" } */
+_Noreturn void fg3a (void) { abort (); } /* { dg-error "conflicting type 
qualifiers for" } */
+__attribute__((noreturn)) void fg3b (void) { abort (); } /* { dg-error 
"conflicting type qualifiers for" } */
+void fg4 (void) {}
+_Noreturn void fg4a (void) { abort (); }
+__attribute__((noreturn)) void fg4b (void) { abort (); }
+void fg5 (void) {}
+_Noreturn void fg5a (void) { abort (); }
+__attribute__((noreturn)) void fg5b (void) { abort (); }
+
+void
+f (void)
+{
+  __typeof__ (ok ()) x = 2;
+  __typeof_unqual (also_ok ()) y = 2;
+  int a[2];
+  int (*p)[x] = &a;
+  __typeof (p + not_defined ()) q;
+  __typeof_unqual__ (p + also_not_defined ()) q2;
+}

        Jakub

Reply via email to