diff mbox series

c: C2x typeof

Message ID alpine.DEB.2.22.394.2210060120550.917581@digraph.polyomino.org.uk
State New
Headers show
Series c: C2x typeof | expand

Commit Message

Joseph Myers Oct. 6, 2022, 1:21 a.m. UTC
[C++ maintainers / global reviewers, note that there is a C++
front-end change here needing review.]

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++ front-end changes)?

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.

Comments

Richard Biener Oct. 6, 2022, 8:32 a.m. UTC | #1
On Thu, Oct 6, 2022 at 3:22 AM Joseph Myers <joseph@codesourcery.com> wrote:
>
> [C++ maintainers / global reviewers, note that there is a C++
> front-end change here needing review.]
>
> 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++ front-end changes)?

I think those are obvious, but OK.

Thanks,
Richard.

>
> 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.
>
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index 4f9878d2695..ffe17eaa9d9 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -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 },
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 5f470d94f4a..62ab4ba437b 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -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
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index f6a94ba31d8..35fe6b4da81 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -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:
> @@ -3028,6 +3032,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?  */
> @@ -3715,22 +3720,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++;
> @@ -3772,6 +3793,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;
>  }
>
> @@ -11866,7 +11905,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
> @@ -11906,6 +11945,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:
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index ac242b5ed13..f9190680a3c 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -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;
>        }
> diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc
> index 0b121a91e1c..22d1ab92add 100644
> --- a/gcc/cp/lex.cc
> +++ b/gcc/cp/lex.cc
> @@ -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;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index e0c2c57c9b2..a2b0b9636f0 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -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
> index 00000000000..a2abe8e465c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c11-typeof-1.c
> @@ -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
> index 00000000000..0b721fedd4c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-typeof-1.c
> @@ -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
> index 00000000000..f1c30a00a7f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-typeof-2.c
> @@ -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
> index 00000000000..c7a057700d3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-typeof-3.c
> @@ -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
> index 00000000000..6477c78bd37
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/gnu11-typeof-1.c
> @@ -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
> index 00000000000..e60ad466c37
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/gnu11-typeof-2.c
> @@ -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
> index 00000000000..f14b54f1f7f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/gnu2x-typeof-1.c
> @@ -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;
> +}
>
> --
> Joseph S. Myers
> joseph@codesourcery.com
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 4f9878d2695..ffe17eaa9d9 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -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 },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 5f470d94f4a..62ab4ba437b 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index f6a94ba31d8..35fe6b4da81 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -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:
@@ -3028,6 +3032,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?  */
@@ -3715,22 +3720,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++;
@@ -3772,6 +3793,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;
 }
 
@@ -11866,7 +11905,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
@@ -11906,6 +11945,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:
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ac242b5ed13..f9190680a3c 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -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;
       }
diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc
index 0b121a91e1c..22d1ab92add 100644
--- a/gcc/cp/lex.cc
+++ b/gcc/cp/lex.cc
@@ -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;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e0c2c57c9b2..a2b0b9636f0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
index 00000000000..a2abe8e465c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-typeof-1.c
@@ -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
index 00000000000..0b721fedd4c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-typeof-1.c
@@ -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
index 00000000000..f1c30a00a7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-typeof-2.c
@@ -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
index 00000000000..c7a057700d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-typeof-3.c
@@ -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
index 00000000000..6477c78bd37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu11-typeof-1.c
@@ -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
index 00000000000..e60ad466c37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu11-typeof-2.c
@@ -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
index 00000000000..f14b54f1f7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu2x-typeof-1.c
@@ -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;
+}