diff mbox

Add __auto_type C extension, use it in <stdatomic.h>

Message ID Pine.LNX.4.64.1311130038460.6467@digraph.polyomino.org.uk
State New
Headers show

Commit Message

Joseph Myers Nov. 13, 2013, 12:39 a.m. UTC
<stdatomic.h> contains what C11 describes as "generic functions".
Although DR#419 makes clear that users cannot #undef these macros (or
otherwise suppress use of a macro definition) and expect to find an
underlying function, they still need to behave like functions as
regards evaluating their arguments exactly once (see C11 7.1.4).

I noted when adding <stdatomic.h> to mainline that some of the macro
definitions there failed that requirement in the case where the
pointer argument had variably modified type, because then typeof
evaluates its argument and so that argument would be evaluated twice.
Avoiding such double evaluation requires defining the type of a
temporary variable, and initializing it with the pointer argument,
with a single evaluation.  To achieve this, this patch adds a new GNU
C extension __auto_type, essentially a restricted version of C++11
auto, and uses it in <stdatomic.h>.  As this is a generic issue for
type-generic macros, if arguments of variably modified type are
allowed, and as such a facility is useful anyway to ensure the
argument text appears only once in the macro expansion and so avoid
exponential blowup when calls to such macros appear in arguments to
such macros, the extension is documented for general use.

I'm not aware of any further architecture-independent issues with the
C11 atomics support, though I'm sure some will be found as people
start using it in practice (and architecture maintainers still need to
add their TARGET_ATOMIC_ASSIGN_EXPAND_FENV definitions).

Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  Applied
to mainline.

2013-11-13  Joseph Myers  <joseph@codesourcery.com>

	* doc/extend.texi (Statement Exprs, Typeof): Discuss __auto_type.
	* ginclude/stdatomic.h (kill_dependency, atomic_store_explicit)
	(atomic_load_explicit, atomic_exchange_explicit)
	(atomic_compare_exchange_strong_explicit)
	(atomic_compare_exchange_weak_explicit): Use __auto_type to
	declare variable initialized with PTR argument.

c-family:
2013-11-13  Joseph Myers  <joseph@codesourcery.com>

	* c-common.h (enum rid): Add RID_AUTO_TYPE.
	* c-common.c (c_common_reswords): Add __auto_type.
	(keyword_begins_type_specifier): Handle RID_AUTO_TYPE.

c:
2013-11-13  Joseph Myers  <joseph@codesourcery.com>

	* c-tree.h (c_typespec_keyword): Add cts_auto_type.
	* c-decl.c (declspecs_add_type, finish_declspecs): Handle
	__auto_type.
	* c-parser.c (c_token_starts_typename, c_token_starts_declspecs)
	(c_parser_attribute_any_word, c_parser_objc_selector): Handle
	RID_AUTO_TYPE.
	(c_parser_declspecs): Take argument AUTO_TYPE_OK.
	(c_parser_declaration_or_fndef, c_parser_struct_declaration)
	(c_parser_declarator, c_parser_direct_declarator_inner)
	(c_parser_parameter_declaration, c_parser_type_name): All callers
	changed.
	(c_parser_declaration_or_fndef): Handle declarations with type
	determined from the initializer.

testsuite:
2013-11-13  Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/atomic/stdatomic-vm.c, gcc.dg/auto-type-1.c,
	gcc.dg/auto-type-2.c: New tests.

Comments

Richard Biener Nov. 13, 2013, 10:39 a.m. UTC | #1
On Wed, Nov 13, 2013 at 1:39 AM, Joseph S. Myers
<joseph@codesourcery.com> wrote:
> <stdatomic.h> contains what C11 describes as "generic functions".
> Although DR#419 makes clear that users cannot #undef these macros (or
> otherwise suppress use of a macro definition) and expect to find an
> underlying function, they still need to behave like functions as
> regards evaluating their arguments exactly once (see C11 7.1.4).
>
> I noted when adding <stdatomic.h> to mainline that some of the macro
> definitions there failed that requirement in the case where the
> pointer argument had variably modified type, because then typeof
> evaluates its argument and so that argument would be evaluated twice.
> Avoiding such double evaluation requires defining the type of a
> temporary variable, and initializing it with the pointer argument,
> with a single evaluation.  To achieve this, this patch adds a new GNU
> C extension __auto_type, essentially a restricted version of C++11
> auto, and uses it in <stdatomic.h>.

I suppose you didn't use '__auto' because that's much more likely
used elsewhere than '__auto_type'?

Richard.

>  As this is a generic issue for
> type-generic macros, if arguments of variably modified type are
> allowed, and as such a facility is useful anyway to ensure the
> argument text appears only once in the macro expansion and so avoid
> exponential blowup when calls to such macros appear in arguments to
> such macros, the extension is documented for general use.
>
> I'm not aware of any further architecture-independent issues with the
> C11 atomics support, though I'm sure some will be found as people
> start using it in practice (and architecture maintainers still need to
> add their TARGET_ATOMIC_ASSIGN_EXPAND_FENV definitions).
>
> Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  Applied
> to mainline.
>
> 2013-11-13  Joseph Myers  <joseph@codesourcery.com>
>
>         * doc/extend.texi (Statement Exprs, Typeof): Discuss __auto_type.
>         * ginclude/stdatomic.h (kill_dependency, atomic_store_explicit)
>         (atomic_load_explicit, atomic_exchange_explicit)
>         (atomic_compare_exchange_strong_explicit)
>         (atomic_compare_exchange_weak_explicit): Use __auto_type to
>         declare variable initialized with PTR argument.
>
> c-family:
> 2013-11-13  Joseph Myers  <joseph@codesourcery.com>
>
>         * c-common.h (enum rid): Add RID_AUTO_TYPE.
>         * c-common.c (c_common_reswords): Add __auto_type.
>         (keyword_begins_type_specifier): Handle RID_AUTO_TYPE.
>
> c:
> 2013-11-13  Joseph Myers  <joseph@codesourcery.com>
>
>         * c-tree.h (c_typespec_keyword): Add cts_auto_type.
>         * c-decl.c (declspecs_add_type, finish_declspecs): Handle
>         __auto_type.
>         * c-parser.c (c_token_starts_typename, c_token_starts_declspecs)
>         (c_parser_attribute_any_word, c_parser_objc_selector): Handle
>         RID_AUTO_TYPE.
>         (c_parser_declspecs): Take argument AUTO_TYPE_OK.
>         (c_parser_declaration_or_fndef, c_parser_struct_declaration)
>         (c_parser_declarator, c_parser_direct_declarator_inner)
>         (c_parser_parameter_declaration, c_parser_type_name): All callers
>         changed.
>         (c_parser_declaration_or_fndef): Handle declarations with type
>         determined from the initializer.
>
> testsuite:
> 2013-11-13  Joseph Myers  <joseph@codesourcery.com>
>
>         * gcc.dg/atomic/stdatomic-vm.c, gcc.dg/auto-type-1.c,
>         gcc.dg/auto-type-2.c: New tests.
>
> Index: gcc/ginclude/stdatomic.h
> ===================================================================
> --- gcc/ginclude/stdatomic.h    (revision 204711)
> +++ gcc/ginclude/stdatomic.h    (working copy)
> @@ -87,7 +87,7 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>  #define kill_dependency(Y)                     \
>    __extension__                                        \
>    ({                                           \
> -    __typeof__ (Y) __kill_dependency_tmp = (Y);        \
> +    __auto_type __kill_dependency_tmp = (Y);   \
>      __kill_dependency_tmp;                     \
>    })
>
> @@ -121,9 +121,9 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>    __atomic_type_lock_free (void * _Atomic)
>
>
> -/* Note that these macros require __typeof__ to remove _Atomic
> -   qualifiers (and const qualifiers, if those are valid on macro
> -   operands).
> +/* Note that these macros require __typeof__ and __auto_type to remove
> +   _Atomic qualifiers (and const qualifiers, if those are valid on
> +   macro operands).
>
>     Also note that the header file uses the generic form of __atomic
>     builtins, which requires the address to be taken of the value
> @@ -132,11 +132,12 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>     these to lock-free _N variants if possible, and throw away the
>     temps.  */
>
> -#define atomic_store_explicit(PTR, VAL, MO)            \
> -  __extension__                                                \
> -  ({                                                   \
> -    __typeof__ (*(PTR)) __atomic_store_tmp = (VAL);    \
> -    __atomic_store ((PTR), &__atomic_store_tmp, (MO)); \
> +#define atomic_store_explicit(PTR, VAL, MO)                            \
> +  __extension__                                                                \
> +  ({                                                                   \
> +    __auto_type __atomic_store_ptr = (PTR);                            \
> +    __typeof__ (*__atomic_store_ptr) __atomic_store_tmp = (VAL);       \
> +    __atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO));    \
>    })
>
>  #define atomic_store(PTR, VAL)                         \
> @@ -146,8 +147,9 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>  #define atomic_load_explicit(PTR, MO)                                  \
>    __extension__                                                                \
>    ({                                                                   \
> -    __typeof__ (*(PTR)) __atomic_load_tmp;                             \
> -    __atomic_load ((PTR), &__atomic_load_tmp, (MO));                   \
> +    __auto_type __atomic_load_ptr = (PTR);                             \
> +    __typeof__ (*__atomic_load_ptr) __atomic_load_tmp;                 \
> +    __atomic_load (__atomic_load_ptr, &__atomic_load_tmp, (MO));       \
>      __atomic_load_tmp;                                                 \
>    })
>
> @@ -157,8 +159,10 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>  #define atomic_exchange_explicit(PTR, VAL, MO)                         \
>    __extension__                                                                \
>    ({                                                                   \
> -    __typeof__ (*(PTR)) __atomic_exchange_val = (VAL), __atomic_exchange_tmp; \
> -    __atomic_exchange ((PTR), &__atomic_exchange_val,                  \
> +    __auto_type __atomic_exchange_ptr = (PTR);                         \
> +    __typeof__ (*__atomic_exchange_ptr) __atomic_exchange_val = (VAL); \
> +    __typeof__ (*__atomic_exchange_ptr) __atomic_exchange_tmp;         \
> +    __atomic_exchange (__atomic_exchange_ptr, &__atomic_exchange_val,  \
>                        &__atomic_exchange_tmp, (MO));                   \
>      __atomic_exchange_tmp;                                             \
>    })
> @@ -170,8 +174,10 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>  #define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \
>    __extension__                                                                \
>    ({                                                                   \
> -    __typeof__ (*(PTR)) __atomic_compare_exchange_tmp = (DES);         \
> -    __atomic_compare_exchange ((PTR), (VAL),                           \
> +    __auto_type __atomic_compare_exchange_ptr = (PTR);                 \
> +    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
> +      = (DES);                                                         \
> +    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),   \
>                                &__atomic_compare_exchange_tmp, 0,       \
>                                (SUC), (FAIL));                          \
>    })
> @@ -183,8 +189,10 @@ typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
>  #define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \
>    __extension__                                                                \
>    ({                                                                   \
> -    __typeof__ (*(PTR)) __atomic_compare_exchange_tmp = (DES);         \
> -    __atomic_compare_exchange ((PTR), (VAL),                           \
> +    __auto_type __atomic_compare_exchange_ptr = (PTR);                 \
> +    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
> +      = (DES);                                                         \
> +    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),   \
>                                &__atomic_compare_exchange_tmp, 1,       \
>                                (SUC), (FAIL));                          \
>    })
> Index: gcc/testsuite/gcc.dg/auto-type-1.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/auto-type-1.c  (revision 0)
> +++ gcc/testsuite/gcc.dg/auto-type-1.c  (revision 0)
> @@ -0,0 +1,37 @@
> +/* Test __auto_type.  Test correct uses.  */
> +/* { dg-do run } */
> +/* { dg-options "" } */
> +
> +extern void abort (void);
> +extern void exit (int);
> +
> +__auto_type i = 1;
> +extern int i;
> +__auto_type c = (char) 1;
> +extern char c;
> +static __auto_type u = 10U;
> +extern unsigned int u;
> +const __auto_type ll = 1LL;
> +extern const long long ll;
> +
> +int
> +main (void)
> +{
> +  if (i != 1 || c != 1 || u != 10U)
> +    abort ();
> +  __auto_type ai = i;
> +  int *aip = &ai;
> +  if (ai != 1)
> +    abort ();
> +  __auto_type p = (int (*) [++i]) 0;
> +  if (i != 2)
> +    abort ();
> +  if (sizeof (*p) != 2 * sizeof (int))
> +    abort ();
> +  int vla[u][u];
> +  int (*vp)[u] = &vla[0];
> +  __auto_type vpp = ++vp;
> +  if (vp != &vla[1])
> +    abort ();
> +  exit (0);
> +}
> Index: gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c  (revision 0)
> +++ gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c  (revision 0)
> @@ -0,0 +1,68 @@
> +/* Test atomic operations on expressions of variably modified type
> +   with side effects.  */
> +/* { dg-do run } */
> +/* { dg-options "-std=c11 -pedantic-errors" } */
> +
> +#include <stdatomic.h>
> +
> +extern void abort (void);
> +
> +int s = 5;
> +
> +int count = 0;
> +
> +int
> +func (void)
> +{
> +  count++;
> +  return 0;
> +}
> +
> +int
> +main (void)
> +{
> +  int vla[s][s];
> +  int (*_Atomic p)[s] = &vla[0];
> +  int (*b)[s] = kill_dependency (++p);
> +  if (b != &vla[1] || p != &vla[1])
> +    abort ();
> +  int (*_Atomic *q)[s] = &p;
> +  atomic_store_explicit (q + func (), &vla[0], memory_order_seq_cst);
> +  if (count != 1)
> +    abort ();
> +  atomic_store (q + func (), &vla[0]);
> +  if (count != 2)
> +    abort ();
> +  (void) atomic_load_explicit (q + func (), memory_order_seq_cst);
> +  if (count != 3)
> +    abort ();
> +  (void) atomic_load (q + func ());
> +  if (count != 4)
> +    abort ();
> +  (void) atomic_exchange_explicit (q + func (), &vla[0], memory_order_seq_cst);
> +  if (count != 5)
> +    abort ();
> +  (void) atomic_exchange (q + func (), &vla[0]);
> +  if (count != 6)
> +    abort ();
> +  int vla2[s][s];
> +  int (*p2)[s] = &vla2[0];
> +  int (**qna)[s] = &p2;
> +  (void) atomic_compare_exchange_strong_explicit (q + func (), qna, &vla[0],
> +                                                 memory_order_seq_cst,
> +                                                 memory_order_seq_cst);
> +  if (count != 7)
> +    abort ();
> +  (void) atomic_compare_exchange_strong (q + func (), qna, &vla[0]);
> +  if (count != 8)
> +    abort ();
> +  (void) atomic_compare_exchange_weak_explicit (q + func (), qna, &vla[0],
> +                                               memory_order_seq_cst,
> +                                               memory_order_seq_cst);
> +  if (count != 9)
> +    abort ();
> +  (void) atomic_compare_exchange_weak (q + func (), qna, &vla[0]);
> +  if (count != 10)
> +    abort ();
> +  return 0;
> +}
> Index: gcc/testsuite/gcc.dg/auto-type-2.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/auto-type-2.c  (revision 0)
> +++ gcc/testsuite/gcc.dg/auto-type-2.c  (revision 0)
> @@ -0,0 +1,23 @@
> +/* Test __auto_type.  Test invalid uses.  */
> +/* { dg-do compile } */
> +/* { dg-options "" } */
> +
> +__auto_type; /* { dg-error "empty declaration" } */
> +__auto_type *p = (int *) 0; /* { dg-error "plain identifier" } */
> +struct s0 { int i : 1; } x;
> +void f (void) { __auto_type v = x.i; } /* { dg-error "bit-field initializer" } */
> +__auto_type i; /* { dg-error "initialized data declaration" } */
> +__auto_type g { } /* { dg-error "initialized data declaration" } */
> +__auto_type a = 1, b = 2; /* { dg-error "single declarator" } */
> +__auto_type long e0 = 0; /* { dg-error "__auto_type" } */
> +__auto_type short e1 = 0; /* { dg-error "__auto_type" } */
> +__auto_type signed e2 = 0; /* { dg-error "__auto_type" } */
> +__auto_type unsigned e3 = 0; /* { dg-error "__auto_type" } */
> +__auto_type _Complex e4 = 0; /* { dg-error "__auto_type" } */
> +long __auto_type e5 = 0; /* { dg-error "__auto_type" } */
> +short __auto_type e6 = 0; /* { dg-error "__auto_type" } */
> +signed __auto_type e7 = 0; /* { dg-error "__auto_type" } */
> +unsigned __auto_type e8 = 0; /* { dg-error "__auto_type" } */
> +_Complex __auto_type e9 = 0; /* { dg-error "__auto_type" } */
> +int __auto_type e10 = 0; /* { dg-error "two or more data types" } */
> +__auto_type _Bool e11 = 0; /* { dg-error "two or more data types" } */
> Index: gcc/doc/extend.texi
> ===================================================================
> --- gcc/doc/extend.texi (revision 204711)
> +++ gcc/doc/extend.texi (working copy)
> @@ -153,7 +153,7 @@ the value of an enumeration constant, the width of
>  the initial value of a static variable.
>
>  If you don't know the type of the operand, you can still do this, but you
> -must use @code{typeof} (@pxref{Typeof}).
> +must use @code{typeof} or @code{__auto_type} (@pxref{Typeof}).
>
>  In G++, the result value of a statement expression undergoes array and
>  function pointer decay, and is returned by value to the enclosing
> @@ -755,6 +755,35 @@ Thus, @code{array (pointer (char), 4)} is the type
>  pointers to @code{char}.
>  @end itemize
>
> +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
> +declaration must be initialized, and the type of the variable is
> +determined by the initializer; the name of the variable is not in
> +scope until after the initializer.  (In C++, you should use C++11
> +@code{auto} for this purpose.)  Using @code{__auto_type}, the
> +``maximum'' macro above could be written as:
> +
> +@smallexample
> +#define max(a,b) \
> +  (@{ __auto_type _a = (a); \
> +      __auto_type _b = (b); \
> +    _a > _b ? _a : _b; @})
> +@end smallexample
> +
> +Using @code{__auto_type} instead of @code{typeof} has two advantages:
> +
> +@itemize @bullet
> +@item Each argument to the macro appears only once in the expansion of
> +the macro.  This prevents the size of the macro expansion growing
> +exponentially when calls to such macros are nested inside arguments of
> +such macros.
> +
> +@item If the argument to the macro has variably modified type, it is
> +evaluated only once when using @code{__auto_type}, but twice if
> +@code{typeof} is used.
> +@end itemize
> +
>  @emph{Compatibility Note:} In addition to @code{typeof}, GCC 2 supported
>  a more limited extension that permitted one to write
>
> Index: gcc/c/c-tree.h
> ===================================================================
> --- gcc/c/c-tree.h      (revision 204711)
> +++ gcc/c/c-tree.h      (working copy)
> @@ -214,7 +214,8 @@ enum c_typespec_keyword {
>    cts_dfloat64,
>    cts_dfloat128,
>    cts_fract,
> -  cts_accum
> +  cts_accum,
> +  cts_auto_type
>  };
>
>  /* This enum lists all the possible declarator specifiers, storage
> Index: gcc/c/c-decl.c
> ===================================================================
> --- gcc/c/c-decl.c      (revision 204711)
> +++ gcc/c/c-decl.c      (working copy)
> @@ -9115,6 +9115,10 @@ declspecs_add_type (location_t loc, struct c_decls
>                 error_at (loc,
>                           ("both %<long%> and %<short%> in "
>                            "declaration specifiers"));
> +             else if (specs->typespec_word == cts_auto_type)
> +               error_at (loc,
> +                         ("both %<long%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
>               else if (specs->typespec_word == cts_void)
>                 error_at (loc,
>                           ("both %<long%> and %<void%> in "
> @@ -9159,6 +9163,10 @@ declspecs_add_type (location_t loc, struct c_decls
>                 error_at (loc,
>                           ("both %<long%> and %<short%> in "
>                            "declaration specifiers"));
> +             else if (specs->typespec_word == cts_auto_type)
> +               error_at (loc,
> +                         ("both %<short%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
>               else if (specs->typespec_word == cts_void)
>                 error_at (loc,
>                           ("both %<short%> and %<void%> in "
> @@ -9207,6 +9215,10 @@ declspecs_add_type (location_t loc, struct c_decls
>                 error_at (loc,
>                           ("both %<signed%> and %<unsigned%> in "
>                            "declaration specifiers"));
> +             else if (specs->typespec_word == cts_auto_type)
> +               error_at (loc,
> +                         ("both %<signed%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
>               else if (specs->typespec_word == cts_void)
>                 error_at (loc,
>                           ("both %<signed%> and %<void%> in "
> @@ -9247,6 +9259,10 @@ declspecs_add_type (location_t loc, struct c_decls
>                 error_at (loc,
>                           ("both %<signed%> and %<unsigned%> in "
>                            "declaration specifiers"));
> +             else if (specs->typespec_word == cts_auto_type)
> +               error_at (loc,
> +                         ("both %<unsigned%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
>               else if (specs->typespec_word == cts_void)
>                 error_at (loc,
>                           ("both %<unsigned%> and %<void%> in "
> @@ -9286,8 +9302,12 @@ declspecs_add_type (location_t loc, struct c_decls
>               if (!flag_isoc99 && !in_system_header_at (loc))
>                 pedwarn (loc, OPT_Wpedantic,
>                          "ISO C90 does not support complex types");
> -             if (specs->typespec_word == cts_void)
> +             if (specs->typespec_word == cts_auto_type)
>                 error_at (loc,
> +                         ("both %<complex%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->typespec_word == cts_void)
> +               error_at (loc,
>                           ("both %<complex%> and %<void%> in "
>                            "declaration specifiers"));
>               else if (specs->typespec_word == cts_bool)
> @@ -9334,6 +9354,10 @@ declspecs_add_type (location_t loc, struct c_decls
>                             ("both %<_Sat%> and %<__int128%> in "
>                              "declaration specifiers"));
>                 }
> +             else if (specs->typespec_word == cts_auto_type)
> +               error_at (loc,
> +                         ("both %<_Sat%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
>               else if (specs->typespec_word == cts_void)
>                 error_at (loc,
>                           ("both %<_Sat%> and %<void%> in "
> @@ -9392,7 +9416,8 @@ declspecs_add_type (location_t loc, struct c_decls
>        else
>         {
>           /* "void", "_Bool", "char", "int", "float", "double", "_Decimal32",
> -            "__int128", "_Decimal64", "_Decimal128", "_Fract" or "_Accum".  */
> +            "__int128", "_Decimal64", "_Decimal128", "_Fract", "_Accum" or
> +            "__auto_type".  */
>           if (specs->typespec_word != cts_none)
>             {
>               error_at (loc,
> @@ -9401,6 +9426,37 @@ declspecs_add_type (location_t loc, struct c_decls
>             }
>           switch (i)
>             {
> +           case RID_AUTO_TYPE:
> +             if (specs->long_p)
> +               error_at (loc,
> +                         ("both %<long%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->short_p)
> +               error_at (loc,
> +                         ("both %<short%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->signed_p)
> +               error_at (loc,
> +                         ("both %<signed%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->unsigned_p)
> +               error_at (loc,
> +                         ("both %<unsigned%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->complex_p)
> +               error_at (loc,
> +                         ("both %<complex%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else if (specs->saturating_p)
> +               error_at (loc,
> +                         ("both %<_Sat%> and %<__auto_type%> in "
> +                          "declaration specifiers"));
> +             else
> +               {
> +                 specs->typespec_word = cts_auto_type;
> +                 specs->locations[cdw_typespec] = loc;
> +               }
> +             return specs;
>             case RID_INT128:
>               if (int128_integer_type_node == NULL_TREE)
>                 {
> @@ -9956,6 +10012,12 @@ finish_declspecs (struct c_declspecs *specs)
>    /* Now compute the actual type.  */
>    switch (specs->typespec_word)
>      {
> +    case cts_auto_type:
> +      gcc_assert (!specs->long_p && !specs->short_p
> +                 && !specs->signed_p && !specs->unsigned_p
> +                 && !specs->complex_p);
> +      /* Type to be filled in later.  */
> +      break;
>      case cts_void:
>        gcc_assert (!specs->long_p && !specs->short_p
>                   && !specs->signed_p && !specs->unsigned_p
> Index: gcc/c/c-parser.c
> ===================================================================
> --- gcc/c/c-parser.c    (revision 204711)
> +++ gcc/c/c-parser.c    (working copy)
> @@ -501,6 +501,7 @@ c_token_starts_typename (c_token *token)
>         case RID_FRACT:
>         case RID_ACCUM:
>         case RID_SAT:
> +       case RID_AUTO_TYPE:
>           return true;
>         default:
>           return false;
> @@ -659,6 +660,7 @@ c_token_starts_declspecs (c_token *token)
>         case RID_SAT:
>         case RID_ALIGNAS:
>         case RID_ATOMIC:
> +       case RID_AUTO_TYPE:
>           return true;
>         default:
>           return false;
> @@ -1128,7 +1130,7 @@ static void c_parser_declaration_or_fndef (c_parse
>  static void c_parser_static_assert_declaration_no_semi (c_parser *);
>  static void c_parser_static_assert_declaration (c_parser *);
>  static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
> -                               bool, bool, enum c_lookahead_kind);
> +                               bool, bool, bool, enum c_lookahead_kind);
>  static struct c_typespec c_parser_enum_specifier (c_parser *);
>  static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
>  static tree c_parser_struct_declaration (c_parser *);
> @@ -1499,7 +1501,7 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>      }
>
>    c_parser_declspecs (parser, specs, true, true, start_attr_ok,
> -                     true, cla_nonabstract_decl);
> +                     true, true, cla_nonabstract_decl);
>    if (parser->error)
>      {
>        c_parser_skip_to_end_of_block_or_statement (parser);
> @@ -1512,9 +1514,12 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>        return;
>      }
>    finish_declspecs (specs);
> +  bool auto_type_p = specs->typespec_word == cts_auto_type;
>    if (c_parser_next_token_is (parser, CPP_SEMICOLON))
>      {
> -      if (empty_ok)
> +      if (auto_type_p)
> +       error_at (here, "%<__auto_type%> in empty declaration");
> +      else if (empty_ok)
>         shadow_tag (specs);
>        else
>         {
> @@ -1537,7 +1542,7 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>        shadow_tag_warned (specs, 1);
>        return;
>      }
> -  else if (c_dialect_objc ())
> +  else if (c_dialect_objc () && !auto_type_p)
>      {
>        /* Prefix attributes are an error on method decls.  */
>        switch (c_parser_peek_token (parser)->type)
> @@ -1640,6 +1645,14 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>           c_parser_skip_to_end_of_block_or_statement (parser);
>           return;
>         }
> +      if (auto_type_p && declarator->kind != cdk_id)
> +       {
> +         error_at (here,
> +                   "%<__auto_type%> requires a plain identifier"
> +                   " as declarator");
> +         c_parser_skip_to_end_of_block_or_statement (parser);
> +         return;
> +       }
>        if (c_parser_next_token_is (parser, CPP_EQ)
>           || c_parser_next_token_is (parser, CPP_COMMA)
>           || c_parser_next_token_is (parser, CPP_SEMICOLON)
> @@ -1667,19 +1680,72 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>               struct c_expr init;
>               location_t init_loc;
>               c_parser_consume_token (parser);
> -             /* The declaration of the variable is in effect while
> -                its initializer is parsed.  */
> -             d = start_decl (declarator, specs, true,
> -                             chainon (postfix_attrs, all_prefix_attrs));
> -             if (!d)
> -               d = error_mark_node;
> -             if (omp_declare_simd_clauses.exists ())
> -               c_finish_omp_declare_simd (parser, d, NULL_TREE,
> -                                          omp_declare_simd_clauses);
> -             start_init (d, asm_name, global_bindings_p ());
> -             init_loc = c_parser_peek_token (parser)->location;
> -             init = c_parser_initializer (parser);
> -             finish_init ();
> +             if (auto_type_p)
> +               {
> +                 start_init (NULL_TREE, asm_name, global_bindings_p ());
> +                 init_loc = c_parser_peek_token (parser)->location;
> +                 init = c_parser_expr_no_commas (parser, NULL);
> +                 if (TREE_CODE (init.value) == COMPONENT_REF
> +                     && DECL_C_BIT_FIELD (TREE_OPERAND (init.value, 1)))
> +                   error_at (here,
> +                             "%<__auto_type%> used with a bit-field"
> +                             " initializer");
> +                 init = convert_lvalue_to_rvalue (init_loc, init, true, true);
> +                 tree init_type = TREE_TYPE (init.value);
> +                 /* As with typeof, remove _Atomic and const
> +                    qualifiers from atomic types.  */
> +                 if (init_type != error_mark_node && TYPE_ATOMIC (init_type))
> +                   init_type
> +                     = c_build_qualified_type (init_type,
> +                                               (TYPE_QUALS (init_type)
> +                                                & ~(TYPE_QUAL_ATOMIC
> +                                                    | TYPE_QUAL_CONST)));
> +                 bool vm_type = variably_modified_type_p (init_type,
> +                                                          NULL_TREE);
> +                 if (vm_type)
> +                   init.value = c_save_expr (init.value);
> +                 finish_init ();
> +                 specs->typespec_kind = ctsk_typeof;
> +                 specs->locations[cdw_typedef] = init_loc;
> +                 specs->typedef_p = true;
> +                 specs->type = init_type;
> +                 if (vm_type)
> +                   {
> +                     bool maybe_const = true;
> +                     tree type_expr = c_fully_fold (init.value, false,
> +                                                    &maybe_const);
> +                     specs->expr_const_operands &= maybe_const;
> +                     if (specs->expr)
> +                       specs->expr = build2 (COMPOUND_EXPR,
> +                                             TREE_TYPE (type_expr),
> +                                             specs->expr, type_expr);
> +                     else
> +                       specs->expr = type_expr;
> +                   }
> +                 d = start_decl (declarator, specs, true,
> +                                 chainon (postfix_attrs, all_prefix_attrs));
> +                 if (!d)
> +                   d = error_mark_node;
> +                 if (omp_declare_simd_clauses.exists ())
> +                   c_finish_omp_declare_simd (parser, d, NULL_TREE,
> +                                              omp_declare_simd_clauses);
> +               }
> +             else
> +               {
> +                 /* The declaration of the variable is in effect while
> +                    its initializer is parsed.  */
> +                 d = start_decl (declarator, specs, true,
> +                                 chainon (postfix_attrs, all_prefix_attrs));
> +                 if (!d)
> +                   d = error_mark_node;
> +                 if (omp_declare_simd_clauses.exists ())
> +                   c_finish_omp_declare_simd (parser, d, NULL_TREE,
> +                                              omp_declare_simd_clauses);
> +                 start_init (d, asm_name, global_bindings_p ());
> +                 init_loc = c_parser_peek_token (parser)->location;
> +                 init = c_parser_initializer (parser);
> +                 finish_init ();
> +               }
>               if (d != error_mark_node)
>                 {
>                   maybe_warn_string_init (TREE_TYPE (d), init);
> @@ -1689,6 +1755,14 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>             }
>           else
>             {
> +             if (auto_type_p)
> +               {
> +                 error_at (here,
> +                           "%<__auto_type%> requires an initialized "
> +                           "data declaration");
> +                 c_parser_skip_to_end_of_block_or_statement (parser);
> +                 return;
> +               }
>               tree d = start_decl (declarator, specs, false,
>                                    chainon (postfix_attrs,
>                                             all_prefix_attrs));
> @@ -1728,6 +1802,14 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>             }
>           if (c_parser_next_token_is (parser, CPP_COMMA))
>             {
> +             if (auto_type_p)
> +               {
> +                 error_at (here,
> +                           "%<__auto_type%> may only be used with"
> +                           " a single declarator");
> +                 c_parser_skip_to_end_of_block_or_statement (parser);
> +                 return;
> +               }
>               c_parser_consume_token (parser);
>               if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
>                 all_prefix_attrs = chainon (c_parser_attributes (parser),
> @@ -1757,6 +1839,13 @@ c_parser_declaration_or_fndef (c_parser *parser, b
>               return;
>             }
>         }
> +      else if (auto_type_p)
> +       {
> +         error_at (here,
> +                   "%<__auto_type%> requires an initialized data declaration");
> +         c_parser_skip_to_end_of_block_or_statement (parser);
> +         return;
> +       }
>        else if (!fndef_ok)
>         {
>           c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, "
> @@ -1949,7 +2038,7 @@ c_parser_static_assert_declaration_no_semi (c_pars
>     Storage class specifiers are accepted iff SCSPEC_OK; type
>     specifiers are accepted iff TYPESPEC_OK; alignment specifiers are
>     accepted iff ALIGNSPEC_OK; attributes are accepted at the start
> -   iff START_ATTR_OK.
> +   iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK.
>
>     declaration-specifiers:
>       storage-class-specifier declaration-specifiers[opt]
> @@ -2030,6 +2119,7 @@ c_parser_static_assert_declaration_no_semi (c_pars
>
>     type-specifier:
>       typeof-specifier
> +     __auto_type
>       __int128
>       _Decimal32
>       _Decimal64
> @@ -2055,7 +2145,8 @@ c_parser_static_assert_declaration_no_semi (c_pars
>  static void
>  c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
>                     bool scspec_ok, bool typespec_ok, bool start_attr_ok,
> -                   bool alignspec_ok, enum c_lookahead_kind la)
> +                   bool alignspec_ok, bool auto_type_ok,
> +                   enum c_lookahead_kind la)
>  {
>    bool attrs_ok = start_attr_ok;
>    bool seen_type = specs->typespec_kind != ctsk_none;
> @@ -2177,6 +2268,10 @@ c_parser_declspecs (c_parser *parser, struct c_dec
>                                 c_parser_peek_token (parser)->value);
>           c_parser_consume_token (parser);
>           break;
> +       case RID_AUTO_TYPE:
> +         if (!auto_type_ok)
> +           goto out;
> +         /* Fall through.  */
>         case RID_UNSIGNED:
>         case RID_LONG:
>         case RID_INT128:
> @@ -2722,7 +2817,7 @@ c_parser_struct_declaration (c_parser *parser)
>       of N1731.
>       <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1731.pdf>  */
>    c_parser_declspecs (parser, specs, false, true, true,
> -                     true, cla_nonabstract_decl);
> +                     true, false, cla_nonabstract_decl);
>    if (parser->error)
>      return NULL_TREE;
>    if (!specs->declspecs_seen_p)
> @@ -3045,7 +3140,7 @@ c_parser_declarator (c_parser *parser, bool type_s
>        struct c_declarator *inner;
>        c_parser_consume_token (parser);
>        c_parser_declspecs (parser, quals_attrs, false, false, true,
> -                         false, cla_prefer_id);
> +                         false, false, cla_prefer_id);
>        inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
>        if (inner == NULL)
>         return NULL;
> @@ -3201,13 +3296,13 @@ c_parser_direct_declarator_inner (c_parser *parser
>        dimen.original_type = NULL_TREE;
>        c_parser_consume_token (parser);
>        c_parser_declspecs (parser, quals_attrs, false, false, true,
> -                         false, cla_prefer_id);
> +                         false, false, cla_prefer_id);
>        static_seen = c_parser_next_token_is_keyword (parser, RID_STATIC);
>        if (static_seen)
>         c_parser_consume_token (parser);
>        if (static_seen && !quals_attrs->declspecs_seen_p)
>         c_parser_declspecs (parser, quals_attrs, false, false, true,
> -                           false, cla_prefer_id);
> +                           false, false, cla_prefer_id);
>        if (!quals_attrs->declspecs_seen_p)
>         quals_attrs = NULL;
>        /* If "static" is present, there must be an array dimension.
> @@ -3510,7 +3605,7 @@ c_parser_parameter_declaration (c_parser *parser,
>        declspecs_add_attrs (input_location, specs, attrs);
>        attrs = NULL_TREE;
>      }
> -  c_parser_declspecs (parser, specs, true, true, true, true,
> +  c_parser_declspecs (parser, specs, true, true, true, true, false,
>                       cla_nonabstract_decl);
>    finish_declspecs (specs);
>    pending_xref_error ();
> @@ -3643,6 +3738,7 @@ c_parser_attribute_any_word (c_parser *parser)
>         case RID_TRANSACTION_ATOMIC:
>         case RID_TRANSACTION_CANCEL:
>         case RID_ATOMIC:
> +       case RID_AUTO_TYPE:
>           ok = true;
>           break;
>         default:
> @@ -3821,7 +3917,7 @@ c_parser_type_name (c_parser *parser)
>    struct c_declarator *declarator;
>    struct c_type_name *ret;
>    bool dummy = false;
> -  c_parser_declspecs (parser, specs, false, true, true, false,
> +  c_parser_declspecs (parser, specs, false, true, true, false, false,
>                       cla_prefer_type);
>    if (!specs->declspecs_seen_p)
>      {
> @@ -8702,6 +8798,7 @@ c_parser_objc_selector (c_parser *parser)
>      case RID_VOID:
>      case RID_BOOL:
>      case RID_ATOMIC:
> +    case RID_AUTO_TYPE:
>        c_parser_consume_token (parser);
>        return value;
>      default:
> Index: gcc/c-family/c-common.c
> ===================================================================
> --- gcc/c-family/c-common.c     (revision 204711)
> +++ gcc/c-family/c-common.c     (working copy)
> @@ -433,6 +433,7 @@ const struct c_common_resword c_common_reswords[]
>    { "__asm__",         RID_ASM,        0 },
>    { "__attribute",     RID_ATTRIBUTE,  0 },
>    { "__attribute__",   RID_ATTRIBUTE,  0 },
> +  { "__auto_type",     RID_AUTO_TYPE,  D_CONLY },
>    { "__bases",          RID_BASES, D_CXXONLY },
>    { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
>    { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
> @@ -11550,6 +11551,7 @@ keyword_begins_type_specifier (enum rid keyword)
>  {
>    switch (keyword)
>      {
> +    case RID_AUTO_TYPE:
>      case RID_INT:
>      case RID_CHAR:
>      case RID_FLOAT:
> Index: gcc/c-family/c-common.h
> ===================================================================
> --- gcc/c-family/c-common.h     (revision 204711)
> +++ gcc/c-family/c-common.h     (working copy)
> @@ -102,7 +102,7 @@ enum rid
>    RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
>    RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,         RID_BUILTIN_SHUFFLE,
>    RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
> -  RID_FRACT, RID_ACCUM,
> +  RID_FRACT, RID_ACCUM, RID_AUTO_TYPE,
>
>    /* C11 */
>    RID_ALIGNAS, RID_GENERIC,
>
> --
> Joseph S. Myers
> joseph@codesourcery.com
Joseph Myers Nov. 13, 2013, 1:29 p.m. UTC | #2
On Wed, 13 Nov 2013, Richard Biener wrote:

> On Wed, Nov 13, 2013 at 1:39 AM, Joseph S. Myers
> <joseph@codesourcery.com> wrote:
> > <stdatomic.h> contains what C11 describes as "generic functions".
> > Although DR#419 makes clear that users cannot #undef these macros (or
> > otherwise suppress use of a macro definition) and expect to find an
> > underlying function, they still need to behave like functions as
> > regards evaluating their arguments exactly once (see C11 7.1.4).
> >
> > I noted when adding <stdatomic.h> to mainline that some of the macro
> > definitions there failed that requirement in the case where the
> > pointer argument had variably modified type, because then typeof
> > evaluates its argument and so that argument would be evaluated twice.
> > Avoiding such double evaluation requires defining the type of a
> > temporary variable, and initializing it with the pointer argument,
> > with a single evaluation.  To achieve this, this patch adds a new GNU
> > C extension __auto_type, essentially a restricted version of C++11
> > auto, and uses it in <stdatomic.h>.
> 
> I suppose you didn't use '__auto' because that's much more likely
> used elsewhere than '__auto_type'?

My reasoning was that it's normal in other cases for "__keyword" to mean 
the same as plain "keyword", so it would be confusing for "__auto" to mean 
something different from standard C "auto".
Michael Matz Nov. 13, 2013, 3:46 p.m. UTC | #3
Hi,

On Wed, 13 Nov 2013, Joseph S. Myers wrote:

> +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,

What's the reason for this restriction?  I can't see what would become 
ambiguous with allowing multiple declarations (even when mixing types):

  int i;
  short s;
  __auto_type i2 = i, s2 = s;

(i2 would be int, s2 be short).


Ciao,
Michael.
Joseph Myers Nov. 13, 2013, 4:38 p.m. UTC | #4
On Wed, 13 Nov 2013, Michael Matz wrote:

> Hi,
> 
> On Wed, 13 Nov 2013, Joseph S. Myers wrote:
> 
> > +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,
> 
> What's the reason for this restriction?  I can't see what would become 
> ambiguous with allowing multiple declarations (even when mixing types):
> 
>   int i;
>   short s;
>   __auto_type i2 = i, s2 = s;
> 
> (i2 would be int, s2 be short).

__auto_type is thought of as being equivalent to typeof (initializer), 
except for avoiding multiple evaluation; there aren't any existing cases 
in GNU C where the type specifier is interpreted separately for each 
identifier being declared.  Obviously you can define semantics (following 
C++) for more cases, but the minimal version is sufficient for 
<stdatomic.h> and other similar uses in macros, and keeping it minimal 
reduces the risk of incompatibility with any future addition of such a 
feature to ISO C.  (It's also simplest to implement.)
Mike Stump Nov. 13, 2013, 6:26 p.m. UTC | #5
On Nov 12, 2013, at 4:39 PM, Joseph S. Myers <joseph@codesourcery.com> wrote:
> with a single evaluation.  To achieve this, this patch adds a new GNU
> C extension __auto_type,

Nice, I like it.
Basile Starynkevitch Nov. 13, 2013, 8:43 p.m. UTC | #6
On Wed, 2013-11-13 at 11:39 +0100, Richard Biener wrote:
> On Wed, Nov 13, 2013 at 1:39 AM, Joseph S. Myers
> <joseph@codesourcery.com> wrote:
> > <stdatomic.h> contains what C11 describes as "generic functions".
> > Although DR#419 makes clear that users cannot #undef these macros (or
> > otherwise suppress use of a macro definition) and expect to find an
> > underlying function, they still need to behave like functions as
> > regards evaluating their arguments exactly once (see C11 7.1.4).
> >
> > I noted when adding <stdatomic.h> to mainline that some of the macro
> > definitions there failed that requirement in the case where the
> > pointer argument had variably modified type, because then typeof
> > evaluates its argument and so that argument would be evaluated twice.
> > Avoiding such double evaluation requires defining the type of a
> > temporary variable, and initializing it with the pointer argument,
> > with a single evaluation.  To achieve this, this patch adds a new GNU
> > C extension __auto_type, essentially a restricted version of C++11
> > auto, and uses it in <stdatomic.h>.
> 
> I suppose you didn't use '__auto' because that's much more likely
> used elsewhere than '__auto_type'?

I have no idea, but does anyone knows if other free compilers (notably
Clang/LLVM) are adding a similar feature?

If they do, perhaps (if it is not too painful) we should use the same
keyword (i.e. __auto_type) and a similar semantics.

And I also like that feature, but it should be documented outside of the
support of <stdatomic.h> since it is genuinely useful by itself (e.g. as
an alternative to typeof).

Regards.
Joseph Myers Nov. 13, 2013, 8:56 p.m. UTC | #7
On Wed, 13 Nov 2013, Basile Starynkevitch wrote:

> I have no idea, but does anyone knows if other free compilers (notably
> Clang/LLVM) are adding a similar feature?

I looked at the list of Clang language extensions before adding this one 
and didn't see mention of anything similar as a C language extension.  
(Clang uses a different set of built-in functions for C11 atomics, so it's 
possible the motivation from <stdatomic.h> doesn't apply there.)

> And I also like that feature, but it should be documented outside of the
> support of <stdatomic.h> since it is genuinely useful by itself (e.g. as
> an alternative to typeof).

The patch includes documentation in extend.texi.
diff mbox

Patch

Index: gcc/ginclude/stdatomic.h
===================================================================
--- gcc/ginclude/stdatomic.h	(revision 204711)
+++ gcc/ginclude/stdatomic.h	(working copy)
@@ -87,7 +87,7 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
 #define kill_dependency(Y)			\
   __extension__					\
   ({						\
-    __typeof__ (Y) __kill_dependency_tmp = (Y);	\
+    __auto_type __kill_dependency_tmp = (Y);	\
     __kill_dependency_tmp;			\
   })
 
@@ -121,9 +121,9 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
   __atomic_type_lock_free (void * _Atomic)
 
 
-/* Note that these macros require __typeof__ to remove _Atomic
-   qualifiers (and const qualifiers, if those are valid on macro
-   operands).
+/* Note that these macros require __typeof__ and __auto_type to remove
+   _Atomic qualifiers (and const qualifiers, if those are valid on
+   macro operands).
    
    Also note that the header file uses the generic form of __atomic
    builtins, which requires the address to be taken of the value
@@ -132,11 +132,12 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
    these to lock-free _N variants if possible, and throw away the
    temps.  */
 
-#define atomic_store_explicit(PTR, VAL, MO)		\
-  __extension__						\
-  ({							\
-    __typeof__ (*(PTR)) __atomic_store_tmp = (VAL);	\
-    __atomic_store ((PTR), &__atomic_store_tmp, (MO));	\
+#define atomic_store_explicit(PTR, VAL, MO)				\
+  __extension__								\
+  ({									\
+    __auto_type __atomic_store_ptr = (PTR);				\
+    __typeof__ (*__atomic_store_ptr) __atomic_store_tmp = (VAL);	\
+    __atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO));	\
   })
 
 #define atomic_store(PTR, VAL)				\
@@ -146,8 +147,9 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
 #define atomic_load_explicit(PTR, MO)					\
   __extension__								\
   ({									\
-    __typeof__ (*(PTR)) __atomic_load_tmp; 				\
-    __atomic_load ((PTR), &__atomic_load_tmp, (MO));			\
+    __auto_type __atomic_load_ptr = (PTR);				\
+    __typeof__ (*__atomic_load_ptr) __atomic_load_tmp;			\
+    __atomic_load (__atomic_load_ptr, &__atomic_load_tmp, (MO));	\
     __atomic_load_tmp;							\
   })
 
@@ -157,8 +159,10 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
 #define atomic_exchange_explicit(PTR, VAL, MO)				\
   __extension__								\
   ({									\
-    __typeof__ (*(PTR)) __atomic_exchange_val = (VAL), __atomic_exchange_tmp; \
-    __atomic_exchange ((PTR), &__atomic_exchange_val,			\
+    __auto_type __atomic_exchange_ptr = (PTR);				\
+    __typeof__ (*__atomic_exchange_ptr) __atomic_exchange_val = (VAL);	\
+    __typeof__ (*__atomic_exchange_ptr) __atomic_exchange_tmp;		\
+    __atomic_exchange (__atomic_exchange_ptr, &__atomic_exchange_val,	\
 		       &__atomic_exchange_tmp, (MO));			\
     __atomic_exchange_tmp;						\
   })
@@ -170,8 +174,10 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
 #define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \
   __extension__								\
   ({									\
-    __typeof__ (*(PTR)) __atomic_compare_exchange_tmp = (DES);		\
-    __atomic_compare_exchange ((PTR), (VAL),				\
+    __auto_type __atomic_compare_exchange_ptr = (PTR);			\
+    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
+      = (DES);								\
+    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),	\
 			       &__atomic_compare_exchange_tmp, 0,	\
 			       (SUC), (FAIL));				\
   })
@@ -183,8 +189,10 @@  typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
 #define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \
   __extension__								\
   ({									\
-    __typeof__ (*(PTR)) __atomic_compare_exchange_tmp = (DES);		\
-    __atomic_compare_exchange ((PTR), (VAL),				\
+    __auto_type __atomic_compare_exchange_ptr = (PTR);			\
+    __typeof__ (*__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
+      = (DES);								\
+    __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL),	\
 			       &__atomic_compare_exchange_tmp, 1,	\
 			       (SUC), (FAIL));				\
   })
Index: gcc/testsuite/gcc.dg/auto-type-1.c
===================================================================
--- gcc/testsuite/gcc.dg/auto-type-1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/auto-type-1.c	(revision 0)
@@ -0,0 +1,37 @@ 
+/* Test __auto_type.  Test correct uses.  */
+/* { dg-do run } */
+/* { dg-options "" } */
+
+extern void abort (void);
+extern void exit (int);
+
+__auto_type i = 1;
+extern int i;
+__auto_type c = (char) 1;
+extern char c;
+static __auto_type u = 10U;
+extern unsigned int u;
+const __auto_type ll = 1LL;
+extern const long long ll;
+
+int
+main (void)
+{
+  if (i != 1 || c != 1 || u != 10U)
+    abort ();
+  __auto_type ai = i;
+  int *aip = &ai;
+  if (ai != 1)
+    abort ();
+  __auto_type p = (int (*) [++i]) 0;
+  if (i != 2)
+    abort ();
+  if (sizeof (*p) != 2 * sizeof (int))
+    abort ();
+  int vla[u][u];
+  int (*vp)[u] = &vla[0];
+  __auto_type vpp = ++vp;
+  if (vp != &vla[1])
+    abort ();
+  exit (0);
+}
Index: gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c
===================================================================
--- gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c	(revision 0)
+++ gcc/testsuite/gcc.dg/atomic/stdatomic-vm.c	(revision 0)
@@ -0,0 +1,68 @@ 
+/* Test atomic operations on expressions of variably modified type
+   with side effects.  */
+/* { dg-do run } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#include <stdatomic.h>
+
+extern void abort (void);
+
+int s = 5;
+
+int count = 0;
+
+int
+func (void)
+{
+  count++;
+  return 0;
+}
+
+int
+main (void)
+{
+  int vla[s][s];
+  int (*_Atomic p)[s] = &vla[0];
+  int (*b)[s] = kill_dependency (++p);
+  if (b != &vla[1] || p != &vla[1])
+    abort ();
+  int (*_Atomic *q)[s] = &p;
+  atomic_store_explicit (q + func (), &vla[0], memory_order_seq_cst);
+  if (count != 1)
+    abort ();
+  atomic_store (q + func (), &vla[0]);
+  if (count != 2)
+    abort ();
+  (void) atomic_load_explicit (q + func (), memory_order_seq_cst);
+  if (count != 3)
+    abort ();
+  (void) atomic_load (q + func ());
+  if (count != 4)
+    abort ();
+  (void) atomic_exchange_explicit (q + func (), &vla[0], memory_order_seq_cst);
+  if (count != 5)
+    abort ();
+  (void) atomic_exchange (q + func (), &vla[0]);
+  if (count != 6)
+    abort ();
+  int vla2[s][s];
+  int (*p2)[s] = &vla2[0];
+  int (**qna)[s] = &p2;
+  (void) atomic_compare_exchange_strong_explicit (q + func (), qna, &vla[0],
+						  memory_order_seq_cst,
+						  memory_order_seq_cst);
+  if (count != 7)
+    abort ();
+  (void) atomic_compare_exchange_strong (q + func (), qna, &vla[0]);
+  if (count != 8)
+    abort ();
+  (void) atomic_compare_exchange_weak_explicit (q + func (), qna, &vla[0],
+						memory_order_seq_cst,
+						memory_order_seq_cst);
+  if (count != 9)
+    abort ();
+  (void) atomic_compare_exchange_weak (q + func (), qna, &vla[0]);
+  if (count != 10)
+    abort ();
+  return 0;
+}
Index: gcc/testsuite/gcc.dg/auto-type-2.c
===================================================================
--- gcc/testsuite/gcc.dg/auto-type-2.c	(revision 0)
+++ gcc/testsuite/gcc.dg/auto-type-2.c	(revision 0)
@@ -0,0 +1,23 @@ 
+/* Test __auto_type.  Test invalid uses.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+__auto_type; /* { dg-error "empty declaration" } */
+__auto_type *p = (int *) 0; /* { dg-error "plain identifier" } */
+struct s0 { int i : 1; } x;
+void f (void) { __auto_type v = x.i; } /* { dg-error "bit-field initializer" } */
+__auto_type i; /* { dg-error "initialized data declaration" } */
+__auto_type g { } /* { dg-error "initialized data declaration" } */
+__auto_type a = 1, b = 2; /* { dg-error "single declarator" } */
+__auto_type long e0 = 0; /* { dg-error "__auto_type" } */
+__auto_type short e1 = 0; /* { dg-error "__auto_type" } */
+__auto_type signed e2 = 0; /* { dg-error "__auto_type" } */
+__auto_type unsigned e3 = 0; /* { dg-error "__auto_type" } */
+__auto_type _Complex e4 = 0; /* { dg-error "__auto_type" } */
+long __auto_type e5 = 0; /* { dg-error "__auto_type" } */
+short __auto_type e6 = 0; /* { dg-error "__auto_type" } */
+signed __auto_type e7 = 0; /* { dg-error "__auto_type" } */
+unsigned __auto_type e8 = 0; /* { dg-error "__auto_type" } */
+_Complex __auto_type e9 = 0; /* { dg-error "__auto_type" } */
+int __auto_type e10 = 0; /* { dg-error "two or more data types" } */
+__auto_type _Bool e11 = 0; /* { dg-error "two or more data types" } */
Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 204711)
+++ gcc/doc/extend.texi	(working copy)
@@ -153,7 +153,7 @@  the value of an enumeration constant, the width of
 the initial value of a static variable.
 
 If you don't know the type of the operand, you can still do this, but you
-must use @code{typeof} (@pxref{Typeof}).
+must use @code{typeof} or @code{__auto_type} (@pxref{Typeof}).
 
 In G++, the result value of a statement expression undergoes array and
 function pointer decay, and is returned by value to the enclosing
@@ -755,6 +755,35 @@  Thus, @code{array (pointer (char), 4)} is the type
 pointers to @code{char}.
 @end itemize
 
+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
+declaration must be initialized, and the type of the variable is
+determined by the initializer; the name of the variable is not in
+scope until after the initializer.  (In C++, you should use C++11
+@code{auto} for this purpose.)  Using @code{__auto_type}, the
+``maximum'' macro above could be written as:
+
+@smallexample
+#define max(a,b) \
+  (@{ __auto_type _a = (a); \
+      __auto_type _b = (b); \
+    _a > _b ? _a : _b; @})
+@end smallexample
+
+Using @code{__auto_type} instead of @code{typeof} has two advantages:
+
+@itemize @bullet
+@item Each argument to the macro appears only once in the expansion of
+the macro.  This prevents the size of the macro expansion growing
+exponentially when calls to such macros are nested inside arguments of
+such macros.
+
+@item If the argument to the macro has variably modified type, it is
+evaluated only once when using @code{__auto_type}, but twice if
+@code{typeof} is used.
+@end itemize
+
 @emph{Compatibility Note:} In addition to @code{typeof}, GCC 2 supported
 a more limited extension that permitted one to write
 
Index: gcc/c/c-tree.h
===================================================================
--- gcc/c/c-tree.h	(revision 204711)
+++ gcc/c/c-tree.h	(working copy)
@@ -214,7 +214,8 @@  enum c_typespec_keyword {
   cts_dfloat64,
   cts_dfloat128,
   cts_fract,
-  cts_accum
+  cts_accum,
+  cts_auto_type
 };
 
 /* This enum lists all the possible declarator specifiers, storage
Index: gcc/c/c-decl.c
===================================================================
--- gcc/c/c-decl.c	(revision 204711)
+++ gcc/c/c-decl.c	(working copy)
@@ -9115,6 +9115,10 @@  declspecs_add_type (location_t loc, struct c_decls
 		error_at (loc,
 			  ("both %<long%> and %<short%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_auto_type)
+		error_at (loc,
+			  ("both %<long%> and %<__auto_type%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_void)
 		error_at (loc,
 			  ("both %<long%> and %<void%> in "
@@ -9159,6 +9163,10 @@  declspecs_add_type (location_t loc, struct c_decls
 		error_at (loc,
 			  ("both %<long%> and %<short%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_auto_type)
+		error_at (loc,
+			  ("both %<short%> and %<__auto_type%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_void)
 		error_at (loc,
 			  ("both %<short%> and %<void%> in "
@@ -9207,6 +9215,10 @@  declspecs_add_type (location_t loc, struct c_decls
 		error_at (loc,
 			  ("both %<signed%> and %<unsigned%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_auto_type)
+		error_at (loc,
+			  ("both %<signed%> and %<__auto_type%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_void)
 		error_at (loc,
 			  ("both %<signed%> and %<void%> in "
@@ -9247,6 +9259,10 @@  declspecs_add_type (location_t loc, struct c_decls
 		error_at (loc,
 			  ("both %<signed%> and %<unsigned%> in "
 			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_auto_type)
+		error_at (loc,
+			  ("both %<unsigned%> and %<__auto_type%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_void)
 		error_at (loc,
 			  ("both %<unsigned%> and %<void%> in "
@@ -9286,8 +9302,12 @@  declspecs_add_type (location_t loc, struct c_decls
 	      if (!flag_isoc99 && !in_system_header_at (loc))
 		pedwarn (loc, OPT_Wpedantic,
 			 "ISO C90 does not support complex types");
-	      if (specs->typespec_word == cts_void)
+	      if (specs->typespec_word == cts_auto_type)
 		error_at (loc,
+			  ("both %<complex%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->typespec_word == cts_void)
+		error_at (loc,
 			  ("both %<complex%> and %<void%> in "
 			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_bool)
@@ -9334,6 +9354,10 @@  declspecs_add_type (location_t loc, struct c_decls
 			    ("both %<_Sat%> and %<__int128%> in "
 			     "declaration specifiers"));
 	        }
+	      else if (specs->typespec_word == cts_auto_type)
+		error_at (loc,
+			  ("both %<_Sat%> and %<__auto_type%> in "
+			   "declaration specifiers"));
 	      else if (specs->typespec_word == cts_void)
 		error_at (loc,
 			  ("both %<_Sat%> and %<void%> in "
@@ -9392,7 +9416,8 @@  declspecs_add_type (location_t loc, struct c_decls
       else
 	{
 	  /* "void", "_Bool", "char", "int", "float", "double", "_Decimal32",
-	     "__int128", "_Decimal64", "_Decimal128", "_Fract" or "_Accum".  */
+	     "__int128", "_Decimal64", "_Decimal128", "_Fract", "_Accum" or
+	     "__auto_type".  */
 	  if (specs->typespec_word != cts_none)
 	    {
 	      error_at (loc,
@@ -9401,6 +9426,37 @@  declspecs_add_type (location_t loc, struct c_decls
 	    }
 	  switch (i)
 	    {
+	    case RID_AUTO_TYPE:
+	      if (specs->long_p)
+		error_at (loc,
+			  ("both %<long%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->short_p)
+		error_at (loc,
+			  ("both %<short%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->signed_p)
+		error_at (loc,
+			  ("both %<signed%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->unsigned_p)
+		error_at (loc,
+			  ("both %<unsigned%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->complex_p)
+		error_at (loc,
+			  ("both %<complex%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else if (specs->saturating_p)
+		error_at (loc,
+			  ("both %<_Sat%> and %<__auto_type%> in "
+			   "declaration specifiers"));
+	      else
+		{
+		  specs->typespec_word = cts_auto_type;
+		  specs->locations[cdw_typespec] = loc;
+		}
+	      return specs;
 	    case RID_INT128:
 	      if (int128_integer_type_node == NULL_TREE)
 		{
@@ -9956,6 +10012,12 @@  finish_declspecs (struct c_declspecs *specs)
   /* Now compute the actual type.  */
   switch (specs->typespec_word)
     {
+    case cts_auto_type:
+      gcc_assert (!specs->long_p && !specs->short_p
+		  && !specs->signed_p && !specs->unsigned_p
+		  && !specs->complex_p);
+      /* Type to be filled in later.  */
+      break;
     case cts_void:
       gcc_assert (!specs->long_p && !specs->short_p
 		  && !specs->signed_p && !specs->unsigned_p
Index: gcc/c/c-parser.c
===================================================================
--- gcc/c/c-parser.c	(revision 204711)
+++ gcc/c/c-parser.c	(working copy)
@@ -501,6 +501,7 @@  c_token_starts_typename (c_token *token)
 	case RID_FRACT:
 	case RID_ACCUM:
 	case RID_SAT:
+	case RID_AUTO_TYPE:
 	  return true;
 	default:
 	  return false;
@@ -659,6 +660,7 @@  c_token_starts_declspecs (c_token *token)
 	case RID_SAT:
 	case RID_ALIGNAS:
 	case RID_ATOMIC:
+	case RID_AUTO_TYPE:
 	  return true;
 	default:
 	  return false;
@@ -1128,7 +1130,7 @@  static void c_parser_declaration_or_fndef (c_parse
 static void c_parser_static_assert_declaration_no_semi (c_parser *);
 static void c_parser_static_assert_declaration (c_parser *);
 static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
-				bool, bool, enum c_lookahead_kind);
+				bool, bool, bool, enum c_lookahead_kind);
 static struct c_typespec c_parser_enum_specifier (c_parser *);
 static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
 static tree c_parser_struct_declaration (c_parser *);
@@ -1499,7 +1501,7 @@  c_parser_declaration_or_fndef (c_parser *parser, b
     }
 
   c_parser_declspecs (parser, specs, true, true, start_attr_ok,
-		      true, cla_nonabstract_decl);
+		      true, true, cla_nonabstract_decl);
   if (parser->error)
     {
       c_parser_skip_to_end_of_block_or_statement (parser);
@@ -1512,9 +1514,12 @@  c_parser_declaration_or_fndef (c_parser *parser, b
       return;
     }
   finish_declspecs (specs);
+  bool auto_type_p = specs->typespec_word == cts_auto_type;
   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
     {
-      if (empty_ok)
+      if (auto_type_p)
+	error_at (here, "%<__auto_type%> in empty declaration");
+      else if (empty_ok)
 	shadow_tag (specs);
       else
 	{
@@ -1537,7 +1542,7 @@  c_parser_declaration_or_fndef (c_parser *parser, b
       shadow_tag_warned (specs, 1);
       return;
     }
-  else if (c_dialect_objc ())
+  else if (c_dialect_objc () && !auto_type_p)
     {
       /* Prefix attributes are an error on method decls.  */
       switch (c_parser_peek_token (parser)->type)
@@ -1640,6 +1645,14 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 	  c_parser_skip_to_end_of_block_or_statement (parser);
 	  return;
 	}
+      if (auto_type_p && declarator->kind != cdk_id)
+	{
+	  error_at (here,
+		    "%<__auto_type%> requires a plain identifier"
+		    " as declarator");
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
       if (c_parser_next_token_is (parser, CPP_EQ)
 	  || c_parser_next_token_is (parser, CPP_COMMA)
 	  || c_parser_next_token_is (parser, CPP_SEMICOLON)
@@ -1667,19 +1680,72 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 	      struct c_expr init;
 	      location_t init_loc;
 	      c_parser_consume_token (parser);
-	      /* The declaration of the variable is in effect while
-		 its initializer is parsed.  */
-	      d = start_decl (declarator, specs, true,
-			      chainon (postfix_attrs, all_prefix_attrs));
-	      if (!d)
-		d = error_mark_node;
-	      if (omp_declare_simd_clauses.exists ())
-		c_finish_omp_declare_simd (parser, d, NULL_TREE,
-					   omp_declare_simd_clauses);
-	      start_init (d, asm_name, global_bindings_p ());
-	      init_loc = c_parser_peek_token (parser)->location;
-	      init = c_parser_initializer (parser);
-	      finish_init ();
+	      if (auto_type_p)
+		{
+		  start_init (NULL_TREE, asm_name, global_bindings_p ());
+		  init_loc = c_parser_peek_token (parser)->location;
+		  init = c_parser_expr_no_commas (parser, NULL);
+		  if (TREE_CODE (init.value) == COMPONENT_REF
+		      && DECL_C_BIT_FIELD (TREE_OPERAND (init.value, 1)))
+		    error_at (here,
+			      "%<__auto_type%> used with a bit-field"
+			      " initializer");
+		  init = convert_lvalue_to_rvalue (init_loc, init, true, true);
+		  tree init_type = TREE_TYPE (init.value);
+		  /* As with typeof, remove _Atomic and const
+		     qualifiers from atomic types.  */
+		  if (init_type != error_mark_node && TYPE_ATOMIC (init_type))
+		    init_type
+		      = c_build_qualified_type (init_type,
+						(TYPE_QUALS (init_type)
+						 & ~(TYPE_QUAL_ATOMIC
+						     | TYPE_QUAL_CONST)));
+		  bool vm_type = variably_modified_type_p (init_type,
+							   NULL_TREE);
+		  if (vm_type)
+		    init.value = c_save_expr (init.value);
+		  finish_init ();
+		  specs->typespec_kind = ctsk_typeof;
+		  specs->locations[cdw_typedef] = init_loc;
+		  specs->typedef_p = true;
+		  specs->type = init_type;
+		  if (vm_type)
+		    {
+		      bool maybe_const = true;
+		      tree type_expr = c_fully_fold (init.value, false,
+						     &maybe_const);
+		      specs->expr_const_operands &= maybe_const;
+		      if (specs->expr)
+			specs->expr = build2 (COMPOUND_EXPR,
+					      TREE_TYPE (type_expr),
+					      specs->expr, type_expr);
+		      else
+			specs->expr = type_expr;
+		    }
+		  d = start_decl (declarator, specs, true,
+				  chainon (postfix_attrs, all_prefix_attrs));
+		  if (!d)
+		    d = error_mark_node;
+		  if (omp_declare_simd_clauses.exists ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       omp_declare_simd_clauses);
+		}
+	      else
+		{
+		  /* The declaration of the variable is in effect while
+		     its initializer is parsed.  */
+		  d = start_decl (declarator, specs, true,
+				  chainon (postfix_attrs, all_prefix_attrs));
+		  if (!d)
+		    d = error_mark_node;
+		  if (omp_declare_simd_clauses.exists ())
+		    c_finish_omp_declare_simd (parser, d, NULL_TREE,
+					       omp_declare_simd_clauses);
+		  start_init (d, asm_name, global_bindings_p ());
+		  init_loc = c_parser_peek_token (parser)->location;
+		  init = c_parser_initializer (parser);
+		  finish_init ();
+		}
 	      if (d != error_mark_node)
 		{
 		  maybe_warn_string_init (TREE_TYPE (d), init);
@@ -1689,6 +1755,14 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 	    }
 	  else
 	    {
+	      if (auto_type_p)
+		{
+		  error_at (here,
+			    "%<__auto_type%> requires an initialized "
+			    "data declaration");
+		  c_parser_skip_to_end_of_block_or_statement (parser);
+		  return;
+		}
 	      tree d = start_decl (declarator, specs, false,
 				   chainon (postfix_attrs,
 					    all_prefix_attrs));
@@ -1728,6 +1802,14 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 	    }
 	  if (c_parser_next_token_is (parser, CPP_COMMA))
 	    {
+	      if (auto_type_p)
+		{
+		  error_at (here,
+			    "%<__auto_type%> may only be used with"
+			    " a single declarator");
+		  c_parser_skip_to_end_of_block_or_statement (parser);
+		  return;
+		}
 	      c_parser_consume_token (parser);
 	      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
 		all_prefix_attrs = chainon (c_parser_attributes (parser),
@@ -1757,6 +1839,13 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 	      return;
 	    }
 	}
+      else if (auto_type_p)
+	{
+	  error_at (here,
+		    "%<__auto_type%> requires an initialized data declaration");
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
       else if (!fndef_ok)
 	{
 	  c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, "
@@ -1949,7 +2038,7 @@  c_parser_static_assert_declaration_no_semi (c_pars
    Storage class specifiers are accepted iff SCSPEC_OK; type
    specifiers are accepted iff TYPESPEC_OK; alignment specifiers are
    accepted iff ALIGNSPEC_OK; attributes are accepted at the start
-   iff START_ATTR_OK.
+   iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK.
 
    declaration-specifiers:
      storage-class-specifier declaration-specifiers[opt]
@@ -2030,6 +2119,7 @@  c_parser_static_assert_declaration_no_semi (c_pars
 
    type-specifier:
      typeof-specifier
+     __auto_type
      __int128
      _Decimal32
      _Decimal64
@@ -2055,7 +2145,8 @@  c_parser_static_assert_declaration_no_semi (c_pars
 static void
 c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 		    bool scspec_ok, bool typespec_ok, bool start_attr_ok,
-		    bool alignspec_ok, enum c_lookahead_kind la)
+		    bool alignspec_ok, bool auto_type_ok,
+		    enum c_lookahead_kind la)
 {
   bool attrs_ok = start_attr_ok;
   bool seen_type = specs->typespec_kind != ctsk_none;
@@ -2177,6 +2268,10 @@  c_parser_declspecs (c_parser *parser, struct c_dec
 				c_parser_peek_token (parser)->value);
 	  c_parser_consume_token (parser);
 	  break;
+	case RID_AUTO_TYPE:
+	  if (!auto_type_ok)
+	    goto out;
+	  /* Fall through.  */
 	case RID_UNSIGNED:
 	case RID_LONG:
 	case RID_INT128:
@@ -2722,7 +2817,7 @@  c_parser_struct_declaration (c_parser *parser)
      of N1731.
      <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1731.pdf>  */
   c_parser_declspecs (parser, specs, false, true, true,
-		      true, cla_nonabstract_decl);
+		      true, false, cla_nonabstract_decl);
   if (parser->error)
     return NULL_TREE;
   if (!specs->declspecs_seen_p)
@@ -3045,7 +3140,7 @@  c_parser_declarator (c_parser *parser, bool type_s
       struct c_declarator *inner;
       c_parser_consume_token (parser);
       c_parser_declspecs (parser, quals_attrs, false, false, true,
-			  false, cla_prefer_id);
+			  false, false, cla_prefer_id);
       inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
       if (inner == NULL)
 	return NULL;
@@ -3201,13 +3296,13 @@  c_parser_direct_declarator_inner (c_parser *parser
       dimen.original_type = NULL_TREE;
       c_parser_consume_token (parser);
       c_parser_declspecs (parser, quals_attrs, false, false, true,
-			  false, cla_prefer_id);
+			  false, false, cla_prefer_id);
       static_seen = c_parser_next_token_is_keyword (parser, RID_STATIC);
       if (static_seen)
 	c_parser_consume_token (parser);
       if (static_seen && !quals_attrs->declspecs_seen_p)
 	c_parser_declspecs (parser, quals_attrs, false, false, true,
-			    false, cla_prefer_id);
+			    false, false, cla_prefer_id);
       if (!quals_attrs->declspecs_seen_p)
 	quals_attrs = NULL;
       /* If "static" is present, there must be an array dimension.
@@ -3510,7 +3605,7 @@  c_parser_parameter_declaration (c_parser *parser,
       declspecs_add_attrs (input_location, specs, attrs);
       attrs = NULL_TREE;
     }
-  c_parser_declspecs (parser, specs, true, true, true, true,
+  c_parser_declspecs (parser, specs, true, true, true, true, false,
 		      cla_nonabstract_decl);
   finish_declspecs (specs);
   pending_xref_error ();
@@ -3643,6 +3738,7 @@  c_parser_attribute_any_word (c_parser *parser)
 	case RID_TRANSACTION_ATOMIC:
 	case RID_TRANSACTION_CANCEL:
 	case RID_ATOMIC:
+	case RID_AUTO_TYPE:
 	  ok = true;
 	  break;
 	default:
@@ -3821,7 +3917,7 @@  c_parser_type_name (c_parser *parser)
   struct c_declarator *declarator;
   struct c_type_name *ret;
   bool dummy = false;
-  c_parser_declspecs (parser, specs, false, true, true, false,
+  c_parser_declspecs (parser, specs, false, true, true, false, false,
 		      cla_prefer_type);
   if (!specs->declspecs_seen_p)
     {
@@ -8702,6 +8798,7 @@  c_parser_objc_selector (c_parser *parser)
     case RID_VOID:
     case RID_BOOL:
     case RID_ATOMIC:
+    case RID_AUTO_TYPE:
       c_parser_consume_token (parser);
       return value;
     default:
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 204711)
+++ gcc/c-family/c-common.c	(working copy)
@@ -433,6 +433,7 @@  const struct c_common_resword c_common_reswords[]
   { "__asm__",		RID_ASM,	0 },
   { "__attribute",	RID_ATTRIBUTE,	0 },
   { "__attribute__",	RID_ATTRIBUTE,	0 },
+  { "__auto_type",	RID_AUTO_TYPE,	D_CONLY },
   { "__bases",          RID_BASES, D_CXXONLY },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
@@ -11550,6 +11551,7 @@  keyword_begins_type_specifier (enum rid keyword)
 {
   switch (keyword)
     {
+    case RID_AUTO_TYPE:
     case RID_INT:
     case RID_CHAR:
     case RID_FLOAT:
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 204711)
+++ gcc/c-family/c-common.h	(working copy)
@@ -102,7 +102,7 @@  enum rid
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
-  RID_FRACT, RID_ACCUM,
+  RID_FRACT, RID_ACCUM, RID_AUTO_TYPE,
 
   /* C11 */
   RID_ALIGNAS, RID_GENERIC,