c: Add __typeof_unqual__ and __typeof_unqual support

Message ID ZId3OsN8187JLiKV@tucnak
State New
Headers
Series c: Add __typeof_unqual__ and __typeof_unqual support |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_check--master-arm fail Patch failed to apply

Commit Message

Jakub Jelinek June 12, 2023, 7:51 p.m. UTC
  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  <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.


	Jakub
  

Patch

--- 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;
+}