]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: Add __typeof_unqual__ and __typeof_unqual support
authorJakub Jelinek <jakub@redhat.com>
Fri, 11 Aug 2023 07:34:15 +0000 (09:34 +0200)
committerJakub Jelinek <jakub@redhat.com>
Fri, 11 Aug 2023 07:34:15 +0000 (09:34 +0200)
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.

2023-08-11  Jakub Jelinek  <jakub@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
gcc/c/c-parser.cc
gcc/doc/extend.texi
gcc/testsuite/gcc.dg/c11-typeof-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c11-typeof-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-typeof-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-typeof-4.c [new file with mode: 0644]

index 9fbaeb437a129fe9688f3149115f416bc9d4fcf2..268462f900e8c3030e365b2f2f67ab4d26c9015f 100644 (file)
@@ -420,6 +420,8 @@ const struct c_common_resword c_common_reswords[] =
   { "__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 },
index 57a01dc2fa384fe741dd6b8e6c507aa87d58c3ee..cabb18d04f7f2950f64ac0295b88f2810775e05a 100644 (file)
@@ -4164,7 +4164,8 @@ c_parser_typeof_specifier (c_parser *parser)
     {
       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++;
index 89c5b4ea2b20dbdaa566a65467f158d96ced9265..73a997276cbe36cb054958dea36b846cd596ecd7 100644 (file)
@@ -843,6 +843,13 @@ Thus, @code{array (pointer (char), 4)} is the type of arrays of 4
 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}.
+
 @cindex @code{__auto_type} in GNU C
 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
diff --git a/gcc/testsuite/gcc.dg/c11-typeof-2.c b/gcc/testsuite/gcc.dg/c11-typeof-2.c
new file mode 100644 (file)
index 0000000..3d49ada
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/c11-typeof-3.c b/gcc/testsuite/gcc.dg/c11-typeof-3.c
new file mode 100644 (file)
index 0000000..026a57f
--- /dev/null
@@ -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;
+}
diff --git a/gcc/testsuite/gcc.dg/gnu11-typeof-3.c b/gcc/testsuite/gcc.dg/gnu11-typeof-3.c
new file mode 100644 (file)
index 0000000..0ae5e5b
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/gnu11-typeof-4.c b/gcc/testsuite/gcc.dg/gnu11-typeof-4.c
new file mode 100644 (file)
index 0000000..313ee97
--- /dev/null
@@ -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;
+}