c: C2x typeof
authorJoseph Myers <joseph@codesourcery.com>
Thu, 6 Oct 2022 14:26:21 +0000 (14:26 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Thu, 6 Oct 2022 14:26:21 +0000 (14:26 +0000)
C2x adds typeof as a standard feature.  In general this follows
existing GNU C semantics very closely, but there are various ways in
which the implementation involves more than simply enabling the
keyword for C2x:

* As well as typeof, there is a typeof_unqual variant, which removes
  all qualifiers and _Atomic from the resulting type (whereas typeof
  preserves qualifiers and _Atomic on qualified or atomic (lvalue or
  type name) operands).

* The typeof keyword is disabled by -fno-asm, so enabling it for C2x
  needs to be implemented in a way that preserves the disabling by
  -fno-asm for older standard versions (which having -fno-asm having
  no effect on it in C2x mode).  This is done via adding a new D_EXT11
  mask (which is also where the C++ front-end change comes from, to
  handle D_EXT11 appropriately there for -fno-asm and
  -fno-gnu-keywords).

* GNU typeof treats the noreturn property of a function (as specified
  in standard C with _Noreturn or [[noreturn]]) as being part of the
  type of a pointer to function, but it is not part of the type in
  standard terms.  Thus a special case is needed in the typeof
  implementation, just like in the _Generic implementation, to avoid
  treating it as a type for standard typeof.  It seems plausible this
  is being used when copying the type of one object to another using
  typeof, so the existing semantics are preserved for __typeof__, and
  for typeof in pre-C2x modes, while typeof for C2x or later has the
  standard semantics.

* It turns out that, even after Martin Uecker's changes in this area,
  there were still cases where rvalues could wrongly have a qualified
  or atomic type in GCC.  This applied to ++ and -- increment and
  decrement expressions, and also to calls to functions returning an
  atomic type.  (For the latter, the working draft doesn't actually
  explicitly exclude the function call expression having an atomic
  type, but given all the changes that have gone into C17 and C2x to
  avoid rvalues ever having qualified types, and given that
  lvalue-to-rvalue conversion removes both qualifiers and _Atomic, it
  seems unlikely that this (or casts, where GCC already removes
  _Atomic) is actually intended as a route to allow an
  _Atomic-qualified rvalue; I've noted this to raise as an NB comment
  on the CD ballot.)

Bootstrapped with no regressions for x86_64-pc-linux-gnu.  OK to
commit (C+

gcc/
* doc/invoke.texi (-fno-asm): Update description of effects on
typeof keyword.

gcc/c-family/
* c-common.cc (c_common_reswords): Mark typeof as D_EXT11.  Add
typeof_unqual.
* c-common.h (enum rid): Add RID_TYPEOF_UNQUAL.
(D_EXT11): New macro.  Values of subsequent macros updated.

gcc/c/
* c-parser.cc (c_parse_init): Add D_EXT11 to mask if flag_no_asm
and not C2x.
(c_keyword_starts_typename, c_token_starts_declspecs)
(c_parser_declspecs, c_parser_objc_selector): Handle
RID_TYPEOF_UNQUAL.
(c_parser_typeof_specifier): Handle RID_TYPEOF_UNQUAL.
Distinguish typeof for C2x from __typeof__ for all standard
versions and typeof before C2x.
* c-typeck.cc (build_function_call_vec): Use unqualified version
of non-void return type.
(build_unary_op): Use unqualified type for increment and
decrement.

gcc/cp/
* lex.cc (init_reswords): Handle D_EXT11.

gcc/testsuite/
* gcc.dg/c11-typeof-1.c, gcc.dg/c2x-typeof-1.c,
gcc.dg/c2x-typeof-2.c, gcc.dg/c2x-typeof-3.c,
gcc.dg/gnu11-typeof-1.c, gcc.dg/gnu11-typeof-2.c,
gcc.dg/gnu2x-typeof-1.c: New tests.

13 files changed:
gcc/c-family/c-common.cc
gcc/c-family/c-common.h
gcc/c/c-parser.cc
gcc/c/c-typeck.cc
gcc/cp/lex.cc
gcc/doc/invoke.texi
gcc/testsuite/gcc.dg/c11-typeof-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-typeof-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-typeof-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c2x-typeof-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-typeof-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-typeof-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu2x-typeof-1.c [new file with mode: 0644]

index 4f9878d..ffe17ea 100644 (file)
@@ -494,7 +494,8 @@ const struct c_common_resword c_common_reswords[] =
   { "typedef",         RID_TYPEDEF,    0 },
   { "typename",                RID_TYPENAME,   D_CXXONLY | D_CXXWARN },
   { "typeid",          RID_TYPEID,     D_CXXONLY | D_CXXWARN },
-  { "typeof",          RID_TYPEOF,     D_ASM | D_EXT },
+  { "typeof",          RID_TYPEOF,     D_EXT11 },
+  { "typeof_unqual",   RID_TYPEOF_UNQUAL,      D_CONLY | D_C2X },
   { "union",           RID_UNION,      0 },
   { "unsigned",                RID_UNSIGNED,   0 },
   { "using",           RID_USING,      D_CXXONLY | D_CXXWARN },
index 5f470d9..62ab4ba 100644 (file)
@@ -104,7 +104,8 @@ enum rid
   RID_SIZEOF,
 
   /* C extensions */
-  RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
+  RID_ASM,       RID_TYPEOF,   RID_TYPEOF_UNQUAL, RID_ALIGNOF,  RID_ATTRIBUTE,
+  RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,         RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_SHUFFLEVECTOR,   RID_BUILTIN_CONVERTVECTOR,   RID_BUILTIN_TGMATH,
@@ -438,16 +439,17 @@ extern machine_mode c_default_pointer_mode;
 #define D_CXX11         0x0010 /* In C++, C++11 only.  */
 #define D_EXT          0x0020  /* GCC extension.  */
 #define D_EXT89                0x0040  /* GCC extension incorporated in C99.  */
-#define D_ASM          0x0080  /* Disabled by -fno-asm.  */
-#define D_OBJC         0x0100  /* In Objective C and neither C nor C++.  */
-#define D_CXX_OBJC     0x0200  /* In Objective C, and C++, but not C.  */
-#define D_CXXWARN      0x0400  /* In C warn with -Wcxx-compat.  */
-#define D_CXX_CONCEPTS  0x0800 /* In C++, only with concepts.  */
-#define D_TRANSMEM     0x1000  /* C++ transactional memory TS.  */
-#define D_CXX_CHAR8_T  0x2000  /* In C++, only with -fchar8_t.  */
-#define D_CXX20                0x4000  /* In C++, C++20 only.  */
-#define D_CXX_COROUTINES 0x8000  /* In C++, only with coroutines.  */
-#define D_CXX_MODULES  0x10000  /* In C++, only with modules.  */
+#define D_EXT11                0x0080  /* GCC extension incorporated in C2X.  */
+#define D_ASM          0x0100  /* Disabled by -fno-asm.  */
+#define D_OBJC         0x0200  /* In Objective C and neither C nor C++.  */
+#define D_CXX_OBJC     0x0400  /* In Objective C, and C++, but not C.  */
+#define D_CXXWARN      0x0800  /* In C warn with -Wcxx-compat.  */
+#define D_CXX_CONCEPTS  0x1000 /* In C++, only with concepts.  */
+#define D_TRANSMEM     0x2000  /* C++ transactional memory TS.  */
+#define D_CXX_CHAR8_T  0x4000  /* In C++, only with -fchar8_t.  */
+#define D_CXX20                0x8000  /* In C++, C++20 only.  */
+#define D_CXX_COROUTINES 0x10000  /* In C++, only with coroutines.  */
+#define D_CXX_MODULES  0x20000  /* In C++, only with modules.  */
 
 #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
index 67b919c..89e0587 100644 (file)
@@ -127,6 +127,8 @@ c_parse_init (void)
       mask |= D_ASM | D_EXT;
       if (!flag_isoc99)
        mask |= D_EXT89;
+      if (!flag_isoc2x)
+       mask |= D_EXT11;
     }
   if (!c_dialect_objc ())
     mask |= D_OBJC | D_CXX_OBJC;
@@ -580,6 +582,7 @@ c_keyword_starts_typename (enum rid keyword)
     case RID_STRUCT:
     case RID_UNION:
     case RID_TYPEOF:
+    case RID_TYPEOF_UNQUAL:
     case RID_CONST:
     case RID_ATOMIC:
     case RID_VOLATILE:
@@ -757,6 +760,7 @@ c_token_starts_declspecs (c_token *token)
        case RID_STRUCT:
        case RID_UNION:
        case RID_TYPEOF:
+       case RID_TYPEOF_UNQUAL:
        case RID_CONST:
        case RID_VOLATILE:
        case RID_RESTRICT:
@@ -3081,6 +3085,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
          declspecs_add_type (loc, specs, t);
          break;
        case RID_TYPEOF:
+       case RID_TYPEOF_UNQUAL:
          /* ??? The old parser rejected typeof after other type
             specifiers, but is a syntax error the best way of
             handling this?  */
@@ -3768,22 +3773,38 @@ c_parser_struct_declaration (c_parser *parser)
   return decls;
 }
 
-/* Parse a typeof specifier (a GNU extension).
+/* Parse a typeof specifier (a GNU extension adopted in C2X).
 
    typeof-specifier:
      typeof ( expression )
      typeof ( type-name )
+     typeof_unqual ( expression )
+     typeof_unqual ( type-name )
 */
 
 static struct c_typespec
 c_parser_typeof_specifier (c_parser *parser)
 {
+  bool is_unqual;
+  bool is_std;
   struct c_typespec ret;
   ret.kind = ctsk_typeof;
   ret.spec = error_mark_node;
   ret.expr = NULL_TREE;
   ret.expr_const_operands = true;
-  gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF));
+  if (c_parser_next_token_is_keyword (parser, RID_TYPEOF))
+    {
+      is_unqual = false;
+      tree spelling = c_parser_peek_token (parser)->value;
+      is_std = (flag_isoc2x
+               && strcmp (IDENTIFIER_POINTER (spelling), "typeof") == 0);
+    }
+  else
+    {
+      gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF_UNQUAL));
+      is_unqual = true;
+      is_std = true;
+    }
   c_parser_consume_token (parser);
   c_inhibit_evaluation_warnings++;
   in_typeof++;
@@ -3825,6 +3846,24 @@ c_parser_typeof_specifier (c_parser *parser)
       pop_maybe_used (was_vm);
     }
   parens.skip_until_found_close (parser);
+  if (ret.spec != error_mark_node)
+    {
+      if (is_unqual && TYPE_QUALS (ret.spec) != TYPE_UNQUALIFIED)
+       ret.spec = TYPE_MAIN_VARIANT (ret.spec);
+      if (is_std)
+       {
+         /* In ISO C terms, _Noreturn is not part of the type of
+            expressions such as &abort, but in GCC it is represented
+            internally as a type qualifier.  */
+         if (TREE_CODE (ret.spec) == FUNCTION_TYPE
+             && TYPE_QUALS (ret.spec) != TYPE_UNQUALIFIED)
+           ret.spec = TYPE_MAIN_VARIANT (ret.spec);
+         else if (FUNCTION_POINTER_TYPE_P (ret.spec)
+                  && TYPE_QUALS (TREE_TYPE (ret.spec)) != TYPE_UNQUALIFIED)
+           ret.spec
+             = build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (ret.spec)));
+       }
+    }
   return ret;
 }
 
@@ -11961,7 +12000,7 @@ c_parser_objc_synchronized_statement (c_parser *parser)
      identifier
      one of
        enum struct union if else while do for switch case default
-       break continue return goto asm sizeof typeof __alignof
+       break continue return goto asm sizeof typeof typeof_unqual __alignof
        unsigned long const short volatile signed restrict _Complex
        in out inout bycopy byref oneway int char float double void _Bool
        _Atomic
@@ -12001,6 +12040,7 @@ c_parser_objc_selector (c_parser *parser)
     case RID_ASM:
     case RID_SIZEOF:
     case RID_TYPEOF:
+    case RID_TYPEOF_UNQUAL:
     case RID_ALIGNOF:
     case RID_UNSIGNED:
     case RID_LONG:
index ac242b5..f919068 100644 (file)
@@ -3187,6 +3187,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 
   /* fntype now gets the type of function pointed to.  */
   fntype = TREE_TYPE (fntype);
+  tree return_type = TREE_TYPE (fntype);
 
   /* Convert the parameters to the types declared in the
      function prototype, or apply default promotions.  */
@@ -3203,8 +3204,6 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
       && TREE_CODE (tem = TREE_OPERAND (tem, 0)) == FUNCTION_DECL
       && !comptypes (fntype, TREE_TYPE (tem)))
     {
-      tree return_type = TREE_TYPE (fntype);
-
       /* This situation leads to run-time undefined behavior.  We can't,
         therefore, simply error unless we can prove that all possible
         executions of the program must execute the code.  */
@@ -3229,22 +3228,25 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   bool warned_p = check_function_arguments (loc, fundecl, fntype,
                                            nargs, argarray, &arg_loc);
 
+  if (TYPE_QUALS (return_type) != TYPE_UNQUALIFIED
+      && !VOID_TYPE_P (return_type))
+    return_type = c_build_qualified_type (return_type, TYPE_UNQUALIFIED);
   if (name != NULL_TREE
       && startswith (IDENTIFIER_POINTER (name), "__builtin_"))
     {
       if (require_constant_value)
        result
-         = fold_build_call_array_initializer_loc (loc, TREE_TYPE (fntype),
+         = fold_build_call_array_initializer_loc (loc, return_type,
                                                   function, nargs, argarray);
       else
-       result = fold_build_call_array_loc (loc, TREE_TYPE (fntype),
+       result = fold_build_call_array_loc (loc, return_type,
                                            function, nargs, argarray);
       if (TREE_CODE (result) == NOP_EXPR
          && TREE_CODE (TREE_OPERAND (result, 0)) == INTEGER_CST)
        STRIP_TYPE_NOPS (result);
     }
   else
-    result = build_call_array_loc (loc, TREE_TYPE (fntype),
+    result = build_call_array_loc (loc, return_type,
                                   function, nargs, argarray);
   /* If -Wnonnull warning has been diagnosed, avoid diagnosing it again
      later.  */
@@ -4831,6 +4833,9 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
        else
          val = build2 (code, TREE_TYPE (arg), arg, inc);
        TREE_SIDE_EFFECTS (val) = 1;
+       if (TYPE_QUALS (TREE_TYPE (val)) != TYPE_UNQUALIFIED)
+         TREE_TYPE (val) = c_build_qualified_type (TREE_TYPE (val),
+                                                   TYPE_UNQUALIFIED);
        ret = val;
        goto return_build_unary_op;
       }
index 0b121a9..22d1ab9 100644 (file)
@@ -241,9 +241,9 @@ init_reswords (void)
   if (!flag_char8_t)
     mask |= D_CXX_CHAR8_T;
   if (flag_no_asm)
-    mask |= D_ASM | D_EXT;
+    mask |= D_ASM | D_EXT | D_EXT11;
   if (flag_no_gnu_keywords)
-    mask |= D_EXT;
+    mask |= D_EXT | D_EXT11;
 
   /* The Objective-C keywords are all context-dependent.  */
   mask |= D_OBJC;
index e0c2c57..a2b0b96 100644 (file)
@@ -2534,7 +2534,10 @@ this switch.  You may want to use the @option{-fno-gnu-keywords} flag
 instead, which disables @code{typeof} but not @code{asm} and
 @code{inline}.  In C99 mode (@option{-std=c99} or @option{-std=gnu99}),
 this switch only affects the @code{asm} and @code{typeof} keywords,
-since @code{inline} is a standard keyword in ISO C99.
+since @code{inline} is a standard keyword in ISO C99.  In C2X mode
+(@option{-std=c2x} or @option{-std=gnu2x}), this switch only affects
+the @code{asm} keyword, since @code{typeof} is a standard keyword in
+ISO C2X.
 
 @item -fno-builtin
 @itemx -fno-builtin-@var{function}
diff --git a/gcc/testsuite/gcc.dg/c11-typeof-1.c b/gcc/testsuite/gcc.dg/c11-typeof-1.c
new file mode 100644 (file)
index 0000000..a2abe8e
--- /dev/null
@@ -0,0 +1,6 @@
+/* Test typeof and typeof_unqual not keywords in C11.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+int typeof = 1;
+long typeof_unqual = 2;
diff --git a/gcc/testsuite/gcc.dg/c2x-typeof-1.c b/gcc/testsuite/gcc.dg/c2x-typeof-1.c
new file mode 100644 (file)
index 0000000..0b721fe
--- /dev/null
@@ -0,0 +1,208 @@
+/* Test C2x typeof and typeof_unqual.  Valid code.  */
+/* { dg-do run } */
+/* { dg-options "-std=c2x -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;
+
+/* The non-returning property of a function is not part of the type given by
+   ISO C typeof.  */
+_Noreturn void nf1 (void);
+[[noreturn]] void nf2 (void);
+void fg (void) {}
+typeof (&nf1) pnf1 = fg;
+typeof (&nf2) pnf2 = fg;
+extern void (*pnf1) (void);
+extern void (*pnf2) (void);
+extern typeof (nf1) *pnf1;
+extern typeof (nf1) *pnf2;
+extern typeof (nf2) *pnf1;
+extern typeof (nf2) *pnf2;
+typeof (*&nf1) fg2, fg2a, fg2b;
+typeof (*&nf2) fg3, fg3a, fg3b;
+typeof (nf1) fg4, fg4a, fg4b;
+typeof (nf2) fg5, fg5a, fg5b;
+
+extern void abort (void);
+extern void exit (int);
+
+void fg2 (void) {}
+_Noreturn void fg2a (void) { abort (); }
+[[noreturn]] void fg2b (void) { abort (); }
+void fg3 (void) {}
+_Noreturn void fg3a (void) { abort (); }
+[[noreturn]] void fg3b (void) { abort (); }
+void fg4 (void) {}
+_Noreturn void fg4a (void) { abort (); }
+[[noreturn]] void fg4b (void) { abort (); }
+void fg5 (void) {}
+_Noreturn void fg5a (void) { abort (); }
+[[noreturn]] void fg5b (void) { abort (); }
+
+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/c2x-typeof-2.c b/gcc/testsuite/gcc.dg/c2x-typeof-2.c
new file mode 100644 (file)
index 0000000..f1c30a0
--- /dev/null
@@ -0,0 +1,27 @@
+/* Test C2x typeof and typeof_unqual.  Invalid code.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -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" } */
+
+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/c2x-typeof-3.c b/gcc/testsuite/gcc.dg/c2x-typeof-3.c
new file mode 100644 (file)
index 0000000..c7a0577
--- /dev/null
@@ -0,0 +1,7 @@
+/* Test C2x typeof and typeof_unqual.  -fno-asm has no effect on keywords in
+   C2x mode.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -fno-asm" } */
+
+int i;
+extern typeof (i) i;
diff --git a/gcc/testsuite/gcc.dg/gnu11-typeof-1.c b/gcc/testsuite/gcc.dg/gnu11-typeof-1.c
new file mode 100644 (file)
index 0000000..6477c78
--- /dev/null
@@ -0,0 +1,6 @@
+/* Test typeof and typeof_unqual not keywords with -std=gnu11 -fno-asm.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu11 -fno-asm" } */
+
+int typeof = 1;
+long typeof_unqual = 2;
diff --git a/gcc/testsuite/gcc.dg/gnu11-typeof-2.c b/gcc/testsuite/gcc.dg/gnu11-typeof-2.c
new file mode 100644 (file)
index 0000000..e60ad46
--- /dev/null
@@ -0,0 +1,39 @@
+/* Test typeof propagation of noreturn function attributes with -std=gnu11:
+   these are part of the type of a function pointer with GNU typeof, but not
+   with C2x typeof.  */
+/* { dg-do link } */
+/* { dg-options "-std=gnu11 -O2" } */
+
+_Noreturn void f (void);
+
+typeof (&f) volatile p;
+typeof (&p) volatile pp;
+
+void link_failure (void);
+
+void
+g (void)
+{
+  (*p) ();
+  link_failure ();
+}
+
+void
+h (void)
+{
+  (**pp) ();
+  link_failure ();
+}
+
+volatile int flag;
+volatile int x;
+
+int
+main (void)
+{
+  if (flag)
+    g ();
+  if (flag)
+    h ();
+  return x;
+}
diff --git a/gcc/testsuite/gcc.dg/gnu2x-typeof-1.c b/gcc/testsuite/gcc.dg/gnu2x-typeof-1.c
new file mode 100644 (file)
index 0000000..f14b54f
--- /dev/null
@@ -0,0 +1,39 @@
+/* Test __typeof__ propagation of noreturn function attributes with -std=gnu2x:
+   these are part of the type of a function pointer with GNU __typeof__, but
+   not with C2x typeof.  */
+/* { dg-do link } */
+/* { dg-options "-std=gnu2x -O2" } */
+
+_Noreturn void f (void);
+
+__typeof__ (&f) volatile p;
+__typeof__ (&p) volatile pp;
+
+void link_failure (void);
+
+void
+g (void)
+{
+  (*p) ();
+  link_failure ();
+}
+
+void
+h (void)
+{
+  (**pp) ();
+  link_failure ();
+}
+
+volatile int flag;
+volatile int x;
+
+int
+main (void)
+{
+  if (flag)
+    g ();
+  if (flag)
+    h ();
+  return x;
+}