diff mbox series

Support C2x [[]] attributes for C

Message ID alpine.DEB.2.21.1911140349500.24245@digraph.polyomino.org.uk
State New
Headers show
Series Support C2x [[]] attributes for C | expand

Commit Message

Joseph Myers Nov. 14, 2019, 3:52 a.m. UTC
This patch adds support for the C2x [[]] attribute syntax to the C
front end.  Support is only added for C at this point, not for
Objective-C; I intend to add the unbounded lookahead required to
support it for Objective-C in a followup patch, but maybe not in
development stage 1.

The syntax is supported in all relevant places where the standard says
it is supported, but support is not added for the individual
attributes specified in C2x (all of which are optional to support).  I
expect to add support for some of them in followup patches; all except
nodiscard can be mapped directly to the semantics of an existing GNU
attribute (subject to extra checks for invalid usages such as the same
attribute being used more than once inside a single [[]]), and the
fallthrough attribute already works after this patch because of
existing special-case code handling it (but without some of the checks
for invalid usage being present).

Note that the four functions c_token_starts_declspecs,
c_token_starts_declaration, c_parser_next_token_starts_declspecs and
c_parser_next_tokens_start_declaration do *not* accept "[[".  This is
analogous with the handling of __extension__: both cases have the
property that they can start either a declaration or some other
statements and so need an unbounded number of tokens to be parsed in
the caller before it can find out what kind of syntactic construct
follows.  Note also that, while I updated all places calling those
functions for standard C syntax to handle attributes if applicable, I
did not do anything regarding calls to such functions for OpenMP or
OpenACC constructs.  Thus, if there are such constructs using such
functions where "[[" *should* be accepted as a possible start to a
declaration, the code for parsing those constructs should be updated
accordingly.

Although all cases of the syntax are handled, and attributes applied
to the constructs the standard says they should be (with less laxity
than there is for GNU attributes to allow an attribute applied to one
construct to be moved automatically to another one), there is a major
limitation in the existing language-independent code in attribs.c
preventing most cases of type attributes from working.  The following
code has been present with minor changes since the first support for
[[]] attributes for C++ was added:

      if (TYPE_P (*node)
          && cxx11_attr_p
          && !(flags & ATTR_FLAG_TYPE_IN_PLACE))
        {
          /* This is a c++11 attribute that appertains to a
             type-specifier, outside of the definition of, a class
             type.  Ignore it.  */
          auto_diagnostic_group d;
          if (warning (OPT_Wattributes, "attribute ignored"))
            inform (input_location,
                    "an attribute that appertains to a type-specifier "
                    "is ignored");
          continue;
        }

I see no justification for this in general for either C or C++ and so
propose to remove or restrict it in a followup bug-fix patch.  Both C
and C++ are clear about attributes in certain places (at the end of
declaration specifiers, or after function or array declarators)
appertaining to a specific type (and explicitly say, in the case of
attributes at the end of declaration specifiers, that they only apply
for that particular use of that type, not for subsequent uses of the
same type without the attributes).  Thus it seems clear to me that,
for example,

int [[gnu::mode(DI)]] x;

ought to be accepted as an analogue in [[]] syntax for

int __attribute__((mode(DI))) x;

(or strictly as an analogue for a version of that with extra
parentheses to make the GNU attribute bind properly to the type rather
than being automatically moved from the declaration to the type).
There are certain cases where an attribute *does* only make sense for
the definition of a type (e.g. "packed" on structure types), but those
should already be handled in the individual attribute handlers (such
as handle_packed_attribute, which already has code to deal with that
issue).  So my inclination is that the above-quoted check in attribs.c
should simply be removed, but failing that it should be restricted to
structure and union types (and such a change would be a bug-fix).
That would then allow various cases of [[]] attributes on types to
work properly.

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

gcc/c:
2019-11-14  Joseph Myers  <joseph@codesourcery.com>

	* c-tree.h (enum c_typespec_kind): Add ctsk_tagref_attrs and
	ctsk_tagfirstref_attrs.
	(struct c_declspecs): Update description of attrs.  Add
	postfix_attrs and non_std_attrs_seen_p.  Increase size of
	typespec_kind bit-field.
	(c_warn_unused_attributes): New declaration.
	(parser_xref_tag): Update prototype.
	* c-decl.c (c_warn_unused_attributes): New function.
	(shadow_tag_warned): Handle ctsk_tagfirstref_attrs and
	ctsk_tagref_attrs.  Handle attribute declarations.
	(check_compound_literal_type): Handle ctsk_tagfirstref_attrs.
	(grokdeclarator): Handle standard attributes.
	(parser_xref_tag): Add arguments have_std_attrs and attrs.  Apply
	attributes to incomplete type reference.
	(xref_tag): Update call to parser_xref_tag.
	(declspecs_add_addrspace, declspecs_add_type)
	(declspecs_add_scspec, declspecs_add_attrs): Set
	non_std_attrs_seen_p.
	(finish_declspecs): Apply postfix standard attributes to type.
	* c-parser.c (c_token_starts_declspecs)
	(c_token_starts_declaration, c_parser_next_token_starts_declspecs)
	(c_parser_next_tokens_start_declaration): Update comments.
	(c_parser_consume_token, c_parser_consume_pragma): Handle moving
	parser->tokens[2] to parser->tokens[1].
	(c_parser_nth_token_starts_std_attributes)
	(c_parser_std_attribute_specifier_sequence): New functions.
	(c_parser_declaration_or_fndef): Add arguments have_attrs and
	attrs.  All callers changed.  Handle standard attributes.
	(c_parser_parms_declarator, c_parser_parms_list_declarator)
	(c_parser_parameter_declaration): Add argument have_gnu_attrs.
	All callers changed.
	(c_parser_declspecs): Add arguments start_std_attr_ok and
	end_std_attr_ok.  All callers changed.  Handle standard
	attributes.
	(c_parser_enum_specifier, c_parser_struct_or_union_specifier)
	(c_parser_direct_declarator, c_parser_direct_declarator_inner)
	(c_parser_compound_statement_nostart, c_parser_all_labels)
	(c_parser_label, c_parser_statement, c_parser_for_statement):
	Handle standard attributes.
	* c-parser.h (c_parser_declspecs): Update prototype.
	* gimple-parser.c (c_parser_gimple_declaration): Update call to
	c_parser_declspecs.

gcc/testsuite:
2019-11-14  Joseph Myers  <joseph@codesourcery.com>

	* gcc.dg/c2x-attr-fallthrough-1.c, gcc.dg/c2x-attr-syntax-1.c,
	gcc.dg/c2x-attr-syntax-2.c, gcc.dg/c2x-attr-syntax-3.c,
	gcc.dg/gnu2x-attr-syntax-1.c, gcc.dg/gnu2x-attr-syntax-2.c,
	gcc.dg/gnu2x-attrs-1.c: New tests.

Comments

Iain Sandoe Nov. 16, 2019, 10:54 a.m. UTC | #1
Joseph Myers <joseph@codesourcery.com> wrote:

> This patch adds support for the C2x [[]] attribute syntax to the C
> front end.  

> 	gcc.dg/gnu2x-attrs-1.c: New tests.

This test fails on targets without symbol alias support, but it would
be most unfortunate to skip it entirely with the usual dg-requires.

In this instance, I propose to expect the error for Darwin as per the patch
below, but open to other suggestions, if you prefer something different.

OK / something else?

thanks
Iain

gcc/testsuite/ChangeLog:

2019-11-16  Iain Sandoe  <iain@sandoe.co.uk>

	* gcc.dg/gnu2x-attrs-1.c: Expect the alias case to produce
	an error on Darwin platforms.


diff --git a/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c b/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c
index df22fb3..fe7e95b 100644
--- a/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c
+++ b/gcc/testsuite/gcc.dg/gnu2x-attrs-1.c
@@ -5,7 +5,7 @@
 
 void f (void) {};
 
-[[gnu::alias("f")]] void g (void);
+[[gnu::alias("f")]] void g (void); /* { dg-error "only weak" *-*-darwin* } */
 
 void [[gnu::alias("f")]] h (void); /* { dg-warning "ignored" } */
 /* { dg-message "that appertains to a type-specifier" "appertains" { target *-*-* } .-1 } */
Joseph Myers Nov. 18, 2019, 2:17 p.m. UTC | #2
On Sat, 16 Nov 2019, Iain Sandoe wrote:

> Joseph Myers <joseph@codesourcery.com> wrote:
> 
> > This patch adds support for the C2x [[]] attribute syntax to the C
> > front end.  
> 
> > 	gcc.dg/gnu2x-attrs-1.c: New tests.
> 
> This test fails on targets without symbol alias support, but it would
> be most unfortunate to skip it entirely with the usual dg-requires.
> 
> In this instance, I propose to expect the error for Darwin as per the patch
> below, but open to other suggestions, if you prefer something different.
> 
> OK / something else?

This patch is OK.

The point of this test is to verify GNU attributes work in C2x syntax, as 
applied to various constructs, so "alias" is being used as an example of 
an attribute that applies to declarations but clearly does not make sense 
on types.  It's probably hard to be 100% portable for all such tests.
Joseph Myers Nov. 21, 2019, 6:30 p.m. UTC | #3
On Mon, 18 Nov 2019, Joseph Myers wrote:

> On Sat, 16 Nov 2019, Iain Sandoe wrote:
> 
> > Joseph Myers <joseph@codesourcery.com> wrote:
> > 
> > > This patch adds support for the C2x [[]] attribute syntax to the C
> > > front end.  
> > 
> > > 	gcc.dg/gnu2x-attrs-1.c: New tests.
> > 
> > This test fails on targets without symbol alias support, but it would
> > be most unfortunate to skip it entirely with the usual dg-requires.
> > 
> > In this instance, I propose to expect the error for Darwin as per the patch
> > below, but open to other suggestions, if you prefer something different.
> > 
> > OK / something else?
> 
> This patch is OK.

Actually, the committed change has incorrect syntax so causes

FAIL: gcc.dg/gnu2x-attrs-1.c *-*-darwin* (test for errors, line 8)

everywhere that's not Darwin.  You're putting "*-*-darwin*" in the "string 
that forms part of the test name" slot, when you need to put something 
else in that slot and then have { target *-*-darwin* } in the following 
slot.
Iain Sandoe Nov. 21, 2019, 6:31 p.m. UTC | #4
Joseph Myers <joseph@codesourcery.com> wrote:

> On Mon, 18 Nov 2019, Joseph Myers wrote:
>
>> On Sat, 16 Nov 2019, Iain Sandoe wrote:
>>
>>> Joseph Myers <joseph@codesourcery.com> wrote:
>>>
>>>> This patch adds support for the C2x [[]] attribute syntax to the C
>>>> front end.
>>>
>>>> gcc.dg/gnu2x-attrs-1.c: New tests.
>>>
>>> This test fails on targets without symbol alias support, but it would
>>> be most unfortunate to skip it entirely with the usual dg-requires.
>>>
>>> In this instance, I propose to expect the error for Darwin as per the  
>>> patch
>>> below, but open to other suggestions, if you prefer something different.
>>>
>>> OK / something else?
>>
>> This patch is OK.
>
> Actually, the committed change has incorrect syntax so causes
>
> FAIL: gcc.dg/gnu2x-attrs-1.c *-*-darwin* (test for errors, line 8)
>
> everywhere that's not Darwin.  You're putting "*-*-darwin*" in the "string
> that forms part of the test name" slot, when you need to put something
> else in that slot and then have { target *-*-darwin* } in the following
> slot.

yes, the goof was spotted, and will be fixed shortly.

sorry,
Iain
diff mbox series

Patch

Index: gcc/c/c-decl.c
===================================================================
--- gcc/c/c-decl.c	(revision 278145)
+++ gcc/c/c-decl.c	(working copy)
@@ -4491,6 +4491,18 @@  c_simulate_builtin_function_decl (tree decl)
   C_DECL_BUILTIN_PROTOTYPE (decl) = prototype_p (type);
   return pushdecl (decl);
 }
+
+/* Warn about attributes in a context where they are unused
+   (attribute-declarations, except for the "fallthrough" case, and
+   attributes on statements).  */
+
+void
+c_warn_unused_attributes (tree attrs)
+{
+  for (tree t = attrs; t != NULL_TREE; t = TREE_CHAIN (t))
+    warning (OPT_Wattributes, "%qE attribute ignored",
+	     get_attribute_name (t));
+}
 
 /* Called when a declaration is seen that contains no names to declare.
    If its type is a reference to a structure, union or enum inherited
@@ -4545,6 +4557,7 @@  shadow_tag_warned (const struct c_declspecs *decls
 	    }
 	  else if (declspecs->typespec_kind != ctsk_tagdef
                    && declspecs->typespec_kind != ctsk_tagfirstref
+		   && declspecs->typespec_kind != ctsk_tagfirstref_attrs
 		   && declspecs->storage_class != csc_none)
 	    {
 	      if (warned != 1)
@@ -4556,6 +4569,7 @@  shadow_tag_warned (const struct c_declspecs *decls
 	    }
 	  else if (declspecs->typespec_kind != ctsk_tagdef
                    && declspecs->typespec_kind != ctsk_tagfirstref
+                   && declspecs->typespec_kind != ctsk_tagfirstref_attrs
 		   && (declspecs->const_p
 		       || declspecs->volatile_p
 		       || declspecs->atomic_p
@@ -4571,6 +4585,7 @@  shadow_tag_warned (const struct c_declspecs *decls
 	    }
 	  else if (declspecs->typespec_kind != ctsk_tagdef
                    && declspecs->typespec_kind != ctsk_tagfirstref
+                   && declspecs->typespec_kind != ctsk_tagfirstref_attrs
 		   && declspecs->alignas_p)
 	    {
 	      if (warned != 1)
@@ -4668,9 +4683,34 @@  shadow_tag_warned (const struct c_declspecs *decls
       warned = 2;
     }
 
+  if (found_tag
+      && warned == 2
+      && (declspecs->typespec_kind == ctsk_tagref_attrs
+	  || declspecs->typespec_kind == ctsk_tagfirstref_attrs))
+    {
+      /* Standard attributes after the "struct" or "union" keyword are
+	 only permitted when the contents of the type are defined, or
+	 in the form "struct-or-union attribute-specifier-sequence
+	 identifier;".  If the ';' was not present, attributes were
+	 diagnosed in the parser.  Here, ensure that any other useless
+	 elements of the declaration result in a pedwarn, not just a
+	 warning.  Forward declarations of enum types are not part of
+	 standard C, but handle them the same.  */
+      pedwarn (input_location, 0,
+	       "invalid use of attributes in empty declaration");
+      warned = 1;
+    }
+
   if (warned != 1)
     {
-      if (!found_tag)
+      if (declspecs->declspecs_seen_p
+	  && !declspecs->non_std_attrs_seen_p)
+	/* An attribute declaration (but not a fallthrough attribute
+	   declaration, which was handled separately); warn if there
+	   are any attributes being ignored (but not if the attributes
+	   were empty).  */
+	c_warn_unused_attributes (declspecs->attrs);
+      else if (!found_tag)
 	pedwarn (input_location, 0, "empty declaration");
     }
 }
@@ -5605,7 +5645,8 @@  check_compound_literal_type (location_t loc, struc
 {
   if (warn_cxx_compat
       && (type_name->specs->typespec_kind == ctsk_tagdef
-          || type_name->specs->typespec_kind == ctsk_tagfirstref))
+          || type_name->specs->typespec_kind == ctsk_tagfirstref
+	  || type_name->specs->typespec_kind == ctsk_tagfirstref_attrs))
     warning_at (loc, OPT_Wc___compat,
 		"defining a type in a compound literal is invalid in C++");
 }
@@ -6210,18 +6251,32 @@  grokdeclarator (const struct c_declarator *declara
 	    const struct c_declarator *inner_decl;
 	    int attr_flags = 0;
 	    declarator = declarator->declarator;
+	    /* Standard attribute syntax precisely defines what entity
+	       an attribute in each position appertains to, so only
+	       apply laxity about positioning to GNU attribute syntax.
+	       Standard attributes applied to a function or array
+	       declarator apply exactly to that type; standard
+	       attributes applied to the identifier apply to the
+	       declaration rather than to the type.  */
 	    inner_decl = declarator;
 	    while (inner_decl->kind == cdk_attrs)
 	      inner_decl = inner_decl->declarator;
-	    if (inner_decl->kind == cdk_id)
-	      attr_flags |= (int) ATTR_FLAG_DECL_NEXT;
-	    else if (inner_decl->kind == cdk_function)
-	      attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT;
-	    else if (inner_decl->kind == cdk_array)
-	      attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT;
-	    returned_attrs = decl_attributes (&type,
-					      chainon (returned_attrs, attrs),
-					      attr_flags);
+	    if (!cxx11_attribute_p (attrs))
+	      {
+		if (inner_decl->kind == cdk_id)
+		  attr_flags |= (int) ATTR_FLAG_DECL_NEXT;
+		else if (inner_decl->kind == cdk_function)
+		  attr_flags |= (int) ATTR_FLAG_FUNCTION_NEXT;
+		else if (inner_decl->kind == cdk_array)
+		  attr_flags |= (int) ATTR_FLAG_ARRAY_NEXT;
+	      }
+	    if (cxx11_attribute_p (attrs) && inner_decl->kind == cdk_id)
+	      returned_attrs = chainon (returned_attrs, attrs);
+	    else
+	      returned_attrs = decl_attributes (&type,
+						chainon (returned_attrs,
+							 attrs),
+						attr_flags);
 	    break;
 	  }
 	case cdk_array:
@@ -7686,11 +7741,14 @@  get_parm_info (bool ellipsis, tree expr)
 
 /* Get the struct, enum or union (CODE says which) with tag NAME.
    Define the tag as a forward-reference with location LOC if it is
-   not defined.  Return a c_typespec structure for the type
-   specifier.  */
+   not defined.  HAVE_STD_ATTRS says whether any standard attributes
+   were present after the struct, union or enum keyword; ATTRS are the
+   standard attributes present there.  Return a c_typespec structure
+   for the type specifier.  */
 
 struct c_typespec
-parser_xref_tag (location_t loc, enum tree_code code, tree name)
+parser_xref_tag (location_t loc, enum tree_code code, tree name,
+		 bool have_std_attrs, tree attrs)
 {
   struct c_typespec ret;
   tree ref;
@@ -7714,9 +7772,12 @@  struct c_typespec
      this would not work properly if we return the reference found.
      (For example, with "struct foo" in an outer scope, "union foo;"
      must shadow that tag with a new one of union type.)  */
-  ret.kind = (ref ? ctsk_tagref : ctsk_tagfirstref);
+  ret.kind = (ref
+	      ? (have_std_attrs ? ctsk_tagref_attrs : ctsk_tagref)
+	      : (have_std_attrs ? ctsk_tagfirstref_attrs : ctsk_tagfirstref));
   if (ref && TREE_CODE (ref) == code)
     {
+      decl_attributes (&ref, attrs, (int) ATTR_FLAG_TYPE_IN_PLACE);
       if (C_TYPE_DEFINED_IN_STRUCT (ref)
 	  && loc != UNKNOWN_LOCATION
 	  && warn_cxx_compat)
@@ -7770,6 +7831,7 @@  struct c_typespec
     }
 
   pushtag (loc, name, ref);
+  decl_attributes (&ref, attrs, (int) ATTR_FLAG_TYPE_IN_PLACE);
 
   ret.spec = ref;
   return ret;
@@ -7782,7 +7844,7 @@  struct c_typespec
 tree
 xref_tag (enum tree_code code, tree name)
 {
-  return parser_xref_tag (input_location, code, name).spec;
+  return parser_xref_tag (input_location, code, name, false, NULL_TREE).spec;
 }
 
 /* Make sure that the tag NAME is defined *in the current scope*
@@ -10214,6 +10276,7 @@  declspecs_add_addrspace (location_t location,
 {
   specs->non_sc_seen_p = true;
   specs->declspecs_seen_p = true;
+  specs->non_std_attrs_seen_p = true;
 
   if (!ADDR_SPACE_GENERIC_P (specs->address_space)
       && specs->address_space != as)
@@ -10239,6 +10302,7 @@  declspecs_add_qual (location_t loc,
   bool dupe = false;
   specs->non_sc_seen_p = true;
   specs->declspecs_seen_p = true;
+  specs->non_std_attrs_seen_p = true;
   gcc_assert (TREE_CODE (qual) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (qual));
   i = C_RID_CODE (qual);
@@ -10297,6 +10361,7 @@  declspecs_add_type (location_t loc, struct c_decls
   tree type = spec.spec;
   specs->non_sc_seen_p = true;
   specs->declspecs_seen_p = true;
+  specs->non_std_attrs_seen_p = true;
   specs->typespec_kind = spec.kind;
   if (TREE_DEPRECATED (type))
     specs->deprecated_p = true;
@@ -11162,6 +11227,7 @@  declspecs_add_scspec (location_t loc,
   enum c_storage_class n = csc_none;
   bool dupe = false;
   specs->declspecs_seen_p = true;
+  specs->non_std_attrs_seen_p = true;
   gcc_assert (TREE_CODE (scspec) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (scspec));
   i = C_RID_CODE (scspec);
@@ -11278,6 +11344,9 @@  declspecs_add_attrs (location_t loc, struct c_decl
   specs->attrs = chainon (attrs, specs->attrs);
   specs->locations[cdw_attributes] = loc;
   specs->declspecs_seen_p = true;
+  /* In the case of standard attributes at the start of the
+     declaration, the caller will reset this.  */
+  specs->non_std_attrs_seen_p = true;
   return specs;
 }
 
@@ -11306,7 +11375,7 @@  declspecs_add_alignas (location_t loc,
    specifiers with any other type specifier to determine the resulting
    type.  This is where ISO C checks on complex types are made, since
    "_Complex long" is a prefix of the valid ISO C type "_Complex long
-   double".  */
+   double".  Also apply postfix standard attributes to modify the type.  */
 
 struct c_declspecs *
 finish_declspecs (struct c_declspecs *specs)
@@ -11376,6 +11445,8 @@  finish_declspecs (struct c_declspecs *specs)
 		  && !specs->signed_p && !specs->unsigned_p
 		  && !specs->complex_p);
       /* Type to be filled in later.  */
+      if (specs->postfix_attrs)
+	error ("%<__auto_type%> followed by %<[[]]%> attributes");
       break;
     case cts_void:
       gcc_assert (!specs->long_p && !specs->short_p
@@ -11581,6 +11652,11 @@  finish_declspecs (struct c_declspecs *specs)
     default:
       gcc_unreachable ();
     }
+  if (specs->type != NULL)
+    {
+      decl_attributes (&specs->type, specs->postfix_attrs, 0);
+      specs->postfix_attrs = NULL_TREE;
+    }
 
   return specs;
 }
Index: gcc/c/c-parser.c
===================================================================
--- gcc/c/c-parser.c	(revision 278145)
+++ gcc/c/c-parser.c	(working copy)
@@ -633,8 +633,8 @@  c_parser_next_token_is_qualifier (c_parser *parser
   return c_token_is_qualifier (token);
 }
 
-/* Return true if TOKEN can start declaration specifiers, false
-   otherwise.  */
+/* Return true if TOKEN can start declaration specifiers (not
+   including standard attributes), false otherwise.  */
 static bool
 c_token_starts_declspecs (c_token *token)
 {
@@ -713,8 +713,9 @@  c_token_starts_declspecs (c_token *token)
 }
 
 
-/* Return true if TOKEN can start declaration specifiers or a static
-   assertion, false otherwise.  */
+/* Return true if TOKEN can start declaration specifiers (not
+   including standard attributes) or a static assertion, false
+   otherwise.  */
 static bool
 c_token_starts_declaration (c_token *token)
 {
@@ -726,7 +727,8 @@  c_token_starts_declaration (c_token *token)
 }
 
 /* Return true if the next token from PARSER can start declaration
-   specifiers, false otherwise.  */
+   specifiers (not including standard attributes), false
+   otherwise.  */
 bool
 c_parser_next_token_starts_declspecs (c_parser *parser)
 {
@@ -748,7 +750,8 @@  c_parser_next_token_starts_declspecs (c_parser *pa
 }
 
 /* Return true if the next tokens from PARSER can start declaration
-   specifiers or a static assertion, false otherwise.  */
+   specifiers (not including standard attributes) or a static
+   assertion, false otherwise.  */
 bool
 c_parser_next_tokens_start_declaration (c_parser *parser)
 {
@@ -787,8 +790,12 @@  c_parser_consume_token (c_parser *parser)
   parser->last_token_location = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
-  else if (parser->tokens_avail == 2)
-    parser->tokens[0] = parser->tokens[1];
+  else if (parser->tokens_avail >= 2)
+    {
+      parser->tokens[0] = parser->tokens[1];
+      if (parser->tokens_avail >= 3)
+	parser->tokens[1] = parser->tokens[2];
+    }
   parser->tokens_avail--;
 }
 
@@ -803,8 +810,12 @@  c_parser_consume_pragma (c_parser *parser)
   gcc_assert (parser->tokens[0].type == CPP_PRAGMA);
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
-  else if (parser->tokens_avail == 2)
-    parser->tokens[0] = parser->tokens[1];
+  else if (parser->tokens_avail >= 2)
+    {
+      parser->tokens[0] = parser->tokens[1];
+      if (parser->tokens_avail >= 3)
+	parser->tokens[1] = parser->tokens[2];
+    }
   parser->tokens_avail--;
   parser->in_pragma = true;
 }
@@ -1384,10 +1395,15 @@  struct oacc_routine_data {
   location_t loc;
 };
 
+static bool c_parser_nth_token_starts_std_attributes (c_parser *,
+						      unsigned int);
+static tree c_parser_std_attribute_specifier_sequence (c_parser *);
 static void c_parser_external_declaration (c_parser *);
 static void c_parser_asm_definition (c_parser *);
 static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool,
 					   bool, bool, tree *, vec<c_token>,
+					   bool have_attrs = false,
+					   tree attrs = NULL,
 					   struct oacc_routine_data * = NULL,
 					   bool * = NULL);
 static void c_parser_static_assert_declaration_no_semi (c_parser *);
@@ -1402,10 +1418,11 @@  static struct c_declarator *c_parser_direct_declar
 static struct c_declarator *c_parser_direct_declarator_inner (c_parser *,
 							      bool,
 							      struct c_declarator *);
-static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
+static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree,
+						     bool);
 static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree,
-							  tree);
-static struct c_parm *c_parser_parameter_declaration (c_parser *, tree);
+							  tree, bool);
+static struct c_parm *c_parser_parameter_declaration (c_parser *, tree, bool);
 static tree c_parser_simple_asm_expr (c_parser *);
 static tree c_parser_gnu_attributes (c_parser *);
 static struct c_expr c_parser_initializer (c_parser *);
@@ -1706,11 +1723,15 @@  add_debug_begin_stmt (location_t loc)
    declarations are OK (subject to all other constraints); otherwise
    (old-style parameter declarations) they are diagnosed.  If
    START_ATTR_OK is true, the declaration specifiers may start with
-   attributes; otherwise they may not.
+   attributes (GNU or standard); otherwise they may not.
    OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed
    declaration when parsing an Objective-C foreach statement.
    FALLTHRU_ATTR_P is used to signal whether this function parsed
-   "__attribute__((fallthrough));".
+   "__attribute__((fallthrough));".  ATTRS are any standard attributes
+   parsed in the caller (in contexts where such attributes had to be
+   parsed to determine whether what follows is a declaration or a
+   statement); HAVE_ATTRS says whether there were any such attributes
+   (even empty).
 
    declaration:
      declaration-specifiers init-declarator-list[opt] ;
@@ -1784,6 +1805,7 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 			       bool nested, bool start_attr_ok,
 			       tree *objc_foreach_object_declaration,
 			       vec<c_token> omp_declare_simd_clauses,
+			       bool have_attrs, tree attrs,
 			       struct oacc_routine_data *oacc_routine_data,
 			       bool *fallthru_attr_p)
 {
@@ -1803,6 +1825,13 @@  c_parser_declaration_or_fndef (c_parser *parser, b
     }
   specs = build_null_declspecs ();
 
+  /* Handle any standard attributes parsed in the caller.  */
+  if (have_attrs)
+    {
+      declspecs_add_attrs (here, specs, attrs);
+      specs->non_std_attrs_seen_p = false;
+    }
+
   /* Try to detect an unknown type name when we have "A B" or "A *B".  */
   if (c_parser_peek_token (parser)->type == CPP_NAME
       && c_parser_peek_token (parser)->id_kind == C_ID_ID
@@ -1867,8 +1896,14 @@  c_parser_declaration_or_fndef (c_parser *parser, b
       fndef_ok = !nested;
     }
 
+  /* When there are standard attributes at the start of the
+     declaration (to apply to the entity being declared), an
+     init-declarator-list or function definition must be present.  */
+  if (c_parser_nth_token_starts_std_attributes (parser, 1))
+    have_attrs = true;
+
   c_parser_declspecs (parser, specs, true, true, start_attr_ok,
-		      true, true, cla_nonabstract_decl);
+		      true, true, start_attr_ok, true, cla_nonabstract_decl);
   if (parser->error)
     {
       c_parser_skip_to_end_of_block_or_statement (parser);
@@ -1896,7 +1931,8 @@  c_parser_declaration_or_fndef (c_parser *parser, b
 						  void_type_node, 0);
 	  add_stmt (fn);
 	}
-      else if (empty_ok)
+      else if (empty_ok && !(have_attrs
+			     && specs->non_std_attrs_seen_p))
 	shadow_tag (specs);
       else
 	{
@@ -2556,7 +2592,14 @@  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; gnu-attributes are accepted at the start
-   iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK.
+   iff START_ATTR_OK; __auto_type is accepted iff AUTO_TYPE_OK.  In
+   addition to the syntax shown, standard attributes are accepted at
+   the start iff START_STD_ATTR_OK and at the end iff END_STD_ATTR_OK;
+   unlike gnu-attributes, they are not accepted in the middle of the
+   list.  (This combines various different syntax productions in the C
+   standard, and in some cases gnu-attributes and standard attributes
+   at the start may already have been parsed before this function is
+   called.)
 
    declaration-specifiers:
      storage-class-specifier declaration-specifiers[opt]
@@ -2664,6 +2707,7 @@  void
 c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
 		    bool scspec_ok, bool typespec_ok, bool start_attr_ok,
 		    bool alignspec_ok, bool auto_type_ok,
+		    bool start_std_attr_ok, bool end_std_attr_ok,
 		    enum c_lookahead_kind la)
 {
   bool attrs_ok = start_attr_ok;
@@ -2672,6 +2716,16 @@  c_parser_declspecs (c_parser *parser, struct c_dec
   if (!typespec_ok)
     gcc_assert (la == cla_prefer_id);
 
+  if (start_std_attr_ok
+      && c_parser_nth_token_starts_std_attributes (parser, 1))
+    {
+      gcc_assert (!specs->non_std_attrs_seen_p);
+      location_t loc = c_parser_peek_token (parser)->location;
+      tree attrs = c_parser_std_attribute_specifier_sequence (parser);
+      declspecs_add_attrs (loc, specs, attrs);
+      specs->non_std_attrs_seen_p = false;
+    }
+
   while (c_parser_next_token_is (parser, CPP_NAME)
 	 || c_parser_next_token_is (parser, CPP_KEYWORD)
 	 || (c_dialect_objc () && c_parser_next_token_is (parser, CPP_LESS)))
@@ -2944,7 +2998,10 @@  c_parser_declspecs (c_parser *parser, struct c_dec
 	  goto out;
 	}
     }
- out: ;
+ out:
+  if (end_std_attr_ok
+      && c_parser_nth_token_starts_std_attributes (parser, 1))
+    specs->postfix_attrs = c_parser_std_attribute_specifier_sequence (parser);
 }
 
 /* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2, C11 6.7.2.2).
@@ -2967,14 +3024,16 @@  c_parser_declspecs (c_parser *parser, struct c_dec
      enumerator-list , enumerator
 
    enumerator:
-     enumeration-constant
-     enumeration-constant = constant-expression
+     enumeration-constant attribute-specifier-sequence[opt]
+     enumeration-constant attribute-specifier-sequence[opt]
+       = constant-expression
 
    GNU Extensions:
 
    enumerator:
-     enumeration-constant gnu-attributes[opt]
-     enumeration-constant gnu-attributes[opt] = constant-expression
+     enumeration-constant attribute-specifier-sequence[opt] gnu-attributes[opt]
+     enumeration-constant attribute-specifier-sequence[opt] gnu-attributes[opt]
+       = constant-expression
 
 */
 
@@ -2982,6 +3041,8 @@  static struct c_typespec
 c_parser_enum_specifier (c_parser *parser)
 {
   struct c_typespec ret;
+  bool have_std_attrs;
+  tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
   location_t enum_loc;
@@ -2988,6 +3049,9 @@  c_parser_enum_specifier (c_parser *parser)
   location_t ident_loc = UNKNOWN_LOCATION;  /* Quiet warning.  */
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ENUM));
   c_parser_consume_token (parser);
+  have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1);
+  if (have_std_attrs)
+    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
   attrs = c_parser_gnu_attributes (parser);
   enum_loc = c_parser_peek_token (parser)->location;
   /* Set the location in case we create a decl now.  */
@@ -3044,7 +3108,11 @@  c_parser_enum_specifier (c_parser *parser)
 	  decl_loc = value_loc = token->location;
 	  c_parser_consume_token (parser);
 	  /* Parse any specified attributes.  */
-	  tree enum_attrs = c_parser_gnu_attributes (parser);
+	  tree std_attrs = NULL_TREE;
+	  if (c_parser_nth_token_starts_std_attributes (parser, 1))
+	    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+	  tree enum_attrs = chainon (std_attrs,
+				     c_parser_gnu_attributes (parser));
 	  if (c_parser_next_token_is (parser, CPP_EQ))
 	    {
 	      c_parser_consume_token (parser);
@@ -3084,7 +3152,8 @@  c_parser_enum_specifier (c_parser *parser)
 	}
       postfix_attrs = c_parser_gnu_attributes (parser);
       ret.spec = finish_enum (type, nreverse (values),
-			      chainon (attrs, postfix_attrs));
+			      chainon (std_attrs,
+				       chainon (attrs, postfix_attrs)));
       ret.kind = ctsk_tagdef;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
@@ -3100,7 +3169,14 @@  c_parser_enum_specifier (c_parser *parser)
       ret.expr_const_operands = true;
       return ret;
     }
-  ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident);
+  /* Attributes may only appear when the members are defined or in
+     certain forward declarations (treat enum forward declarations in
+     GNU C analogously to struct and union forward declarations in
+     standard C).  */
+  if (have_std_attrs && c_parser_next_token_is_not (parser, CPP_SEMICOLON))
+    c_parser_error (parser, "expected %<;%>");
+  ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident, have_std_attrs,
+			 std_attrs);
   /* In ISO C, enumerated types can be referred to only if already
      defined.  */
   if (pedantic && !COMPLETE_TYPE_P (ret.spec))
@@ -3115,9 +3191,10 @@  c_parser_enum_specifier (c_parser *parser)
 /* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1, C11 6.7.2.1).
 
    struct-or-union-specifier:
-     struct-or-union gnu-attributes[opt] identifier[opt]
-       { struct-contents } gnu-attributes[opt]
-     struct-or-union gnu-attributes[opt] identifier
+     struct-or-union attribute-specifier-sequence[opt] gnu-attributes[opt]
+       identifier[opt] { struct-contents } gnu-attributes[opt]
+     struct-or-union attribute-specifier-sequence[opt] gnu-attributes[opt]
+       identifier
 
    struct-contents:
      struct-declaration-list
@@ -3155,6 +3232,8 @@  static struct c_typespec
 c_parser_struct_or_union_specifier (c_parser *parser)
 {
   struct c_typespec ret;
+  bool have_std_attrs;
+  tree std_attrs = NULL_TREE;
   tree attrs;
   tree ident = NULL_TREE;
   location_t struct_loc;
@@ -3173,6 +3252,9 @@  c_parser_struct_or_union_specifier (c_parser *pars
     }
   struct_loc = c_parser_peek_token (parser)->location;
   c_parser_consume_token (parser);
+  have_std_attrs = c_parser_nth_token_starts_std_attributes (parser, 1);
+  if (have_std_attrs)
+    std_attrs = c_parser_std_attribute_specifier_sequence (parser);
   attrs = c_parser_gnu_attributes (parser);
 
   /* Set the location in case we create a decl now.  */
@@ -3291,7 +3373,9 @@  c_parser_struct_or_union_specifier (c_parser *pars
 	}
       postfix_attrs = c_parser_gnu_attributes (parser);
       ret.spec = finish_struct (struct_loc, type, nreverse (contents),
-				chainon (attrs, postfix_attrs), struct_info);
+				chainon (std_attrs,
+					 chainon (attrs, postfix_attrs)),
+				struct_info);
       ret.kind = ctsk_tagdef;
       ret.expr = NULL_TREE;
       ret.expr_const_operands = true;
@@ -3307,7 +3391,13 @@  c_parser_struct_or_union_specifier (c_parser *pars
       ret.expr_const_operands = true;
       return ret;
     }
-  ret = parser_xref_tag (ident_loc, code, ident);
+  /* Attributes may only appear when the members are defined or in
+     certain forward declarations.  */
+  if (have_std_attrs && c_parser_next_token_is_not (parser, CPP_SEMICOLON))
+    c_parser_error (parser, "expected %<;%>");
+  /* ??? Existing practice is that GNU attributes are ignored after
+     the struct or union keyword when not defining the members.  */
+  ret = parser_xref_tag (ident_loc, code, ident, have_std_attrs, std_attrs);
   return ret;
 }
 
@@ -3315,7 +3405,8 @@  c_parser_struct_or_union_specifier (c_parser *pars
    *without* the trailing semicolon.
 
    struct-declaration:
-     specifier-qualifier-list struct-declarator-list
+     attribute-specifier-sequence[opt] specifier-qualifier-list
+       attribute-specifier-sequence[opt] struct-declarator-list
      static_assert-declaration-no-semi
 
    specifier-qualifier-list:
@@ -3375,7 +3466,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, false, cla_nonabstract_decl);
+		      true, false, true, true, cla_nonabstract_decl);
   if (parser->error)
     return NULL_TREE;
   if (!specs->declspecs_seen_p)
@@ -3693,7 +3784,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, false, cla_prefer_id);
+			  false, false, true, false, cla_prefer_id);
       inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
       if (inner == NULL)
 	return NULL;
@@ -3718,14 +3809,15 @@  c_parser_direct_declarator (c_parser *parser, bool
      parenthesized declarator.  In an abstract declarator or parameter
      declarator, they could start a parenthesized declarator or a
      parameter list.  To tell which, the open parenthesis and any
-     following gnu-attributes must be read.  If a declaration specifier
-     follows, then it is a parameter list; if the specifier is a
-     typedef name, there might be an ambiguity about redeclaring it,
-     which is resolved in the direction of treating it as a typedef
-     name.  If a close parenthesis follows, it is also an empty
-     parameter list, as the syntax does not permit empty abstract
-     declarators.  Otherwise, it is a parenthesized declarator (in
-     which case the analysis may be repeated inside it, recursively).
+     following gnu-attributes must be read.  If a declaration
+     specifier or standard attributes follow, then it is a parameter
+     list; if the specifier is a typedef name, there might be an
+     ambiguity about redeclaring it, which is resolved in the
+     direction of treating it as a typedef name.  If a close
+     parenthesis follows, it is also an empty parameter list, as the
+     syntax does not permit empty abstract declarators.  Otherwise, it
+     is a parenthesized declarator (in which case the analysis may be
+     repeated inside it, recursively).
 
      ??? There is an ambiguity in a parameter declaration "int
      (__attribute__((foo)) x)", where x is not a typedef name: it
@@ -3758,11 +3850,18 @@  c_parser_direct_declarator (c_parser *parser, bool
       *seen_id = true;
       inner->id_loc = c_parser_peek_token (parser)->location;
       c_parser_consume_token (parser);
+      if (c_parser_nth_token_starts_std_attributes (parser, 1))
+	{
+	  tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+	  if (std_attrs)
+	    inner = build_attrs_declarator (std_attrs, inner);
+	}
       return c_parser_direct_declarator_inner (parser, *seen_id, inner);
     }
 
   if (kind != C_DTR_NORMAL
-      && c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+      && c_parser_next_token_is (parser, CPP_OPEN_SQUARE)
+      && !c_parser_nth_token_starts_std_attributes (parser, 1))
     {
       struct c_declarator *inner = build_id_declarator (NULL_TREE);
       inner->id_loc = c_parser_peek_token (parser)->location;
@@ -3777,14 +3876,18 @@  c_parser_direct_declarator (c_parser *parser, bool
       tree attrs;
       struct c_declarator *inner;
       c_parser_consume_token (parser);
+      bool have_gnu_attrs = c_parser_next_token_is_keyword (parser,
+							    RID_ATTRIBUTE);
       attrs = c_parser_gnu_attributes (parser);
       if (kind != C_DTR_NORMAL
 	  && (c_parser_next_token_starts_declspecs (parser)
+	      || (!have_gnu_attrs
+		  && c_parser_nth_token_starts_std_attributes (parser, 1))
 	      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN)))
 	{
 	  struct c_arg_info *args
 	    = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL,
-					 attrs);
+					 attrs, have_gnu_attrs);
 	  if (args == NULL)
 	    return NULL;
 	  else
@@ -3792,6 +3895,16 @@  c_parser_direct_declarator (c_parser *parser, bool
 	      inner
 		= build_function_declarator (args,
 					     build_id_declarator (NULL_TREE));
+	      if (!(args->types
+		    && args->types != error_mark_node
+		    && TREE_CODE (TREE_VALUE (args->types)) == IDENTIFIER_NODE)
+		  && c_parser_nth_token_starts_std_attributes (parser, 1))
+		{
+		  tree std_attrs
+		    = c_parser_std_attribute_specifier_sequence (parser);
+		  if (std_attrs)
+		    inner = build_attrs_declarator (std_attrs, inner);
+		}
 	      return c_parser_direct_declarator_inner (parser, *seen_id,
 						       inner);
 	    }
@@ -3837,7 +3950,8 @@  c_parser_direct_declarator_inner (c_parser *parser
 				  struct c_declarator *inner)
 {
   /* Parse a sequence of array declarators and parameter lists.  */
-  if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+  if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)
+      && !c_parser_nth_token_starts_std_attributes (parser, 1))
     {
       location_t brace_loc = c_parser_peek_token (parser)->location;
       struct c_declarator *declarator;
@@ -3850,13 +3964,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, false, cla_prefer_id);
+			  false, false, 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, false, cla_prefer_id);
+			    false, false, false, false, cla_prefer_id);
       if (!quals_attrs->declspecs_seen_p)
 	quals_attrs = NULL;
       /* If "static" is present, there must be an array dimension.
@@ -3909,6 +4023,13 @@  c_parser_direct_declarator_inner (c_parser *parser
       if (declarator == NULL)
 	return NULL;
       inner = set_array_declarator_inner (declarator, inner);
+      if (c_parser_nth_token_starts_std_attributes (parser, 1))
+	{
+	  tree std_attrs
+	    = c_parser_std_attribute_specifier_sequence (parser);
+	  if (std_attrs)
+	    inner = build_attrs_declarator (std_attrs, inner);
+	}
       return c_parser_direct_declarator_inner (parser, id_present, inner);
     }
   else if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
@@ -3916,13 +4037,26 @@  c_parser_direct_declarator_inner (c_parser *parser
       tree attrs;
       struct c_arg_info *args;
       c_parser_consume_token (parser);
+      bool have_gnu_attrs = c_parser_next_token_is_keyword (parser,
+							    RID_ATTRIBUTE);
       attrs = c_parser_gnu_attributes (parser);
-      args = c_parser_parms_declarator (parser, id_present, attrs);
+      args = c_parser_parms_declarator (parser, id_present, attrs,
+					have_gnu_attrs);
       if (args == NULL)
 	return NULL;
       else
 	{
 	  inner = build_function_declarator (args, inner);
+	  if (!(args->types
+		&& args->types != error_mark_node
+		&& TREE_CODE (TREE_VALUE (args->types)) == IDENTIFIER_NODE)
+	      && c_parser_nth_token_starts_std_attributes (parser, 1))
+	    {
+	      tree std_attrs
+		= c_parser_std_attribute_specifier_sequence (parser);
+	      if (std_attrs)
+		inner = build_attrs_declarator (std_attrs, inner);
+	    }
 	  return c_parser_direct_declarator_inner (parser, id_present, inner);
 	}
     }
@@ -3930,12 +4064,16 @@  c_parser_direct_declarator_inner (c_parser *parser
 }
 
 /* Parse a parameter list or identifier list, including the closing
-   parenthesis but not the opening one.  ATTRS are the attributes at
-   the start of the list.  ID_LIST_OK is true if an identifier list is
-   acceptable; such a list must not have attributes at the start.  */
+   parenthesis but not the opening one.  ATTRS are the gnu-attributes
+   at the start of the list.  ID_LIST_OK is true if an identifier list
+   is acceptable; such a list must not have attributes at the start.
+   HAVE_GNU_ATTRS says whether any gnu-attributes (including empty
+   attributes) were present (in which case standard attributes cannot
+   occur).  */
 
 static struct c_arg_info *
-c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
+c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs,
+			   bool have_gnu_attrs)
 {
   push_scope ();
   declare_parm_level ();
@@ -3988,8 +4126,8 @@  static struct c_arg_info *
     }
   else
     {
-      struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs,
-							       NULL);
+      struct c_arg_info *ret
+	= c_parser_parms_list_declarator (parser, attrs, NULL, have_gnu_attrs);
       pop_scope ();
       return ret;
     }
@@ -3996,13 +4134,16 @@  static struct c_arg_info *
 }
 
 /* Parse a parameter list (possibly empty), including the closing
-   parenthesis but not the opening one.  ATTRS are the attributes at
-   the start of the list.  EXPR is NULL or an expression that needs to
-   be evaluated for the side effects of array size expressions in the
-   parameters.  */
+   parenthesis but not the opening one.  ATTRS are the gnu-attributes
+   at the start of the list; if HAVE_GNU_ATTRS, there were some such
+   attributes (possibly empty, in which case ATTRS is NULL_TREE),
+   which means standard attributes cannot start the list.  EXPR is
+   NULL or an expression that needs to be evaluated for the side
+   effects of array size expressions in the parameters.  */
 
 static struct c_arg_info *
-c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
+c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr,
+				bool have_gnu_attrs)
 {
   bool bad_parm = false;
 
@@ -4009,7 +4150,7 @@  static struct c_arg_info *
   /* ??? Following the old parser, forward parameter declarations may
      use abstract declarators, and if no real parameter declarations
      follow the forward declarations then this is not diagnosed.  Also
-     note as above that attributes are ignored as the only contents of
+     note as above that gnu-attributes are ignored as the only contents of
      the parentheses, or as the only contents after forward
      declarations.  */
   if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
@@ -4053,8 +4194,10 @@  static struct c_arg_info *
   while (true)
     {
       /* Parse a parameter.  */
-      struct c_parm *parm = c_parser_parameter_declaration (parser, attrs);
+      struct c_parm *parm = c_parser_parameter_declaration (parser, attrs,
+							    have_gnu_attrs);
       attrs = NULL_TREE;
+      have_gnu_attrs = false;
       if (parm == NULL)
 	bad_parm = true;
       else
@@ -4064,8 +4207,11 @@  static struct c_arg_info *
 	  tree new_attrs;
 	  c_parser_consume_token (parser);
 	  mark_forward_parm_decls ();
+	  bool new_have_gnu_attrs
+	    = c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE);
 	  new_attrs = c_parser_gnu_attributes (parser);
-	  return c_parser_parms_list_declarator (parser, new_attrs, expr);
+	  return c_parser_parms_list_declarator (parser, new_attrs, expr,
+						 new_have_gnu_attrs);
 	}
       if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	{
@@ -4103,11 +4249,14 @@  static struct c_arg_info *
     }
 }
 
-/* Parse a parameter declaration.  ATTRS are the attributes at the
-   start of the declaration if it is the first parameter.  */
+/* Parse a parameter declaration.  ATTRS are the gnu-attributes at the
+   start of the declaration if it is the first parameter;
+   HAVE_GNU_ATTRS is true if there were any gnu-attributes there (even
+   empty) there.  */
 
 static struct c_parm *
-c_parser_parameter_declaration (c_parser *parser, tree attrs)
+c_parser_parameter_declaration (c_parser *parser, tree attrs,
+				bool have_gnu_attrs)
 {
   struct c_declspecs *specs;
   struct c_declarator *declarator;
@@ -4119,7 +4268,8 @@  static struct c_parm *
   while (c_parser_next_token_is (parser, CPP_PRAGMA))
     c_parser_pragma (parser, pragma_param, NULL);
 
-  if (!c_parser_next_token_starts_declspecs (parser))
+  if (!c_parser_next_token_starts_declspecs (parser)
+      && !c_parser_nth_token_starts_std_attributes (parser, 1))
     {
       c_token *token = c_parser_peek_token (parser);
       if (parser->error)
@@ -4161,7 +4311,7 @@  static struct c_parm *
       attrs = NULL_TREE;
     }
   c_parser_declspecs (parser, specs, true, true, true, true, false,
-		      cla_nonabstract_decl);
+		      !have_gnu_attrs, true, cla_nonabstract_decl);
   finish_declspecs (specs);
   pending_xref_error ();
   prefix_attrs = specs->attrs;
@@ -4733,6 +4883,33 @@  c_parser_std_attribute_specifier (c_parser *parser
   return nreverse (attributes);
 }
 
+/* Return whether standard attributes start with the Nth token.  */
+
+static bool
+c_parser_nth_token_starts_std_attributes (c_parser *parser, unsigned int n)
+{
+  if (!(c_parser_peek_nth_token (parser, n)->type == CPP_OPEN_SQUARE
+	&& c_parser_peek_nth_token (parser, n + 1)->type == CPP_OPEN_SQUARE))
+    return false;
+  /* In C, '[[' must start attributes.  In Objective-C, identifying
+     whether those tokens start attributes requires unbounded
+     lookahead, which is not yet implemented.  */
+  return !c_dialect_objc ();
+}
+
+static tree
+c_parser_std_attribute_specifier_sequence (c_parser *parser)
+{
+  tree attributes = NULL_TREE;
+  do
+    {
+      tree attrs = c_parser_std_attribute_specifier (parser, false);
+      attributes = chainon (attributes, attrs);
+    }
+  while (c_parser_nth_token_starts_std_attributes (parser, 1));
+  return attributes;
+}
+
 /* Parse a type name (C90 6.5.5, C99 6.7.6, C11 6.7.7).  ALIGNAS_OK
    says whether alignment specifiers are OK (only in cases that might
    be the type name of a compound literal).
@@ -4749,7 +4926,7 @@  c_parser_type_name (c_parser *parser, bool alignas
   struct c_type_name *ret;
   bool dummy = false;
   c_parser_declspecs (parser, specs, false, true, true, alignas_ok, false,
-		      cla_prefer_type);
+		      false, true, cla_prefer_type);
   if (!specs->declspecs_seen_p)
     {
       c_parser_error (parser, "expected specifier-qualifier-list");
@@ -5281,11 +5458,18 @@  c_parser_compound_statement_nostart (c_parser *par
     {
       location_t loc = c_parser_peek_token (parser)->location;
       loc = expansion_point_location_if_in_system_header (loc);
+      /* Standard attributes may start a statement or a declaration.  */
+      bool have_std_attrs
+	= c_parser_nth_token_starts_std_attributes (parser, 1);
+      tree std_attrs = NULL_TREE;
+      if (have_std_attrs)
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
       if (c_parser_next_token_is_keyword (parser, RID_CASE)
 	  || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
 	  || (c_parser_next_token_is (parser, CPP_NAME)
 	      && c_parser_peek_2nd_token (parser)->type == CPP_COLON))
 	{
+	  c_warn_unused_attributes (std_attrs);
 	  if (c_parser_next_token_is_keyword (parser, RID_CASE))
 	    label_loc = c_parser_peek_2nd_token (parser)->location;
 	  else
@@ -5296,14 +5480,17 @@  c_parser_compound_statement_nostart (c_parser *par
 	  c_parser_label (parser);
 	}
       else if (!last_label
-	       && c_parser_next_tokens_start_declaration (parser))
+	       && (c_parser_next_tokens_start_declaration (parser)
+		   || (have_std_attrs
+		       && c_parser_next_token_is (parser, CPP_SEMICOLON))))
 	{
 	  last_label = false;
 	  mark_valid_location_for_stdc_pragma (false);
 	  bool fallthru_attr_p = false;
-	  c_parser_declaration_or_fndef (parser, true, true, true, true,
-					 true, NULL, vNULL, NULL,
-					 &fallthru_attr_p);
+	  c_parser_declaration_or_fndef (parser, true, !have_std_attrs,
+					 true, true, true, NULL,
+					 vNULL, have_std_attrs, std_attrs,
+					 NULL, &fallthru_attr_p);
 	  if (last_stmt && !fallthru_attr_p)
 	    pedwarn_c90 (loc, OPT_Wdeclaration_after_statement,
 			 "ISO C90 forbids mixed declarations and code");
@@ -5315,12 +5502,17 @@  c_parser_compound_statement_nostart (c_parser *par
 	  /* __extension__ can start a declaration, but is also an
 	     unary operator that can start an expression.  Consume all
 	     but the last of a possible series of __extension__ to
-	     determine which.  */
+	     determine which.  If standard attributes have already
+	     been seen, it must start a statement, not a declaration,
+	     but standard attributes starting a declaration may appear
+	     after __extension__.  */
 	  while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD
 		 && (c_parser_peek_2nd_token (parser)->keyword
 		     == RID_EXTENSION))
 	    c_parser_consume_token (parser);
-	  if (c_token_starts_declaration (c_parser_peek_2nd_token (parser)))
+	  if (!have_std_attrs
+	      && (c_token_starts_declaration (c_parser_peek_2nd_token (parser))
+		  || c_parser_nth_token_starts_std_attributes (parser, 2)))
 	    {
 	      int ext;
 	      ext = disable_extension_diagnostics ();
@@ -5342,6 +5534,8 @@  c_parser_compound_statement_nostart (c_parser *par
 	}
       else if (c_parser_next_token_is (parser, CPP_PRAGMA))
 	{
+	  if (have_std_attrs)
+	    c_parser_error (parser, "expected declaration or statement");
 	  /* External pragmas, and some omp pragmas, are not associated
 	     with regular c code, and so are not to be considered statements
 	     syntactically.  This ensures that the user doesn't put them
@@ -5376,6 +5570,7 @@  c_parser_compound_statement_nostart (c_parser *par
       else
 	{
 	statement:
+	  c_warn_unused_attributes (std_attrs);
 	  last_label = false;
 	  last_stmt = true;
 	  mark_valid_location_for_stdc_pragma (false);
@@ -5391,11 +5586,22 @@  c_parser_compound_statement_nostart (c_parser *par
   mark_valid_location_for_stdc_pragma (save_valid_for_pragma);
 }
 
-/* Parse all consecutive labels. */
+/* Parse all consecutive labels, possibly preceded by standard
+   attributes.  In this context, a statement is required, not a
+   declaration, so attributes must be followed by a statement that is
+   not just a semicolon.  */
 
 static void
 c_parser_all_labels (c_parser *parser)
 {
+  if (c_parser_nth_token_starts_std_attributes (parser, 1))
+    {
+      tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	c_parser_error (parser, "expected statement");
+      else
+	c_warn_unused_attributes (std_attrs);
+    }
   while (c_parser_next_token_is_keyword (parser, RID_CASE)
 	 || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
 	 || (c_parser_next_token_is (parser, CPP_NAME)
@@ -5417,7 +5623,11 @@  c_parser_all_labels (c_parser *parser)
 
    The use of gnu-attributes on labels is a GNU extension.  The syntax in
    GNU C accepts any expressions without commas, non-constant
-   expressions being rejected later.  */
+   expressions being rejected later.  Any standard
+   attribute-specifier-sequence before the first label has been parsed
+   in the caller, to distinguish statements from declarations.  Any
+   attribute-specifier-sequence after the label is parsed in this
+   function.  */
 
 static void
 c_parser_label (c_parser *parser)
@@ -5480,8 +5690,18 @@  c_parser_label (c_parser *parser)
       else
 	FALLTHROUGH_LABEL_P (CASE_LABEL (label)) = fallthrough_p;
 
+      /* Standard attributes are only allowed here if they start a
+	 statement, not a declaration (including the case of an
+	 attribute-declaration with only attributes).  */
+      bool have_std_attrs
+	= c_parser_nth_token_starts_std_attributes (parser, 1);
+      tree std_attrs = NULL_TREE;
+      if (have_std_attrs)
+	std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+
       /* Allow '__attribute__((fallthrough));'.  */
-      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+      if (!have_std_attrs
+	  && c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
 	{
 	  location_t loc = c_parser_peek_token (parser)->location;
 	  tree attrs = c_parser_gnu_attributes (parser);
@@ -5502,7 +5722,9 @@  c_parser_label (c_parser *parser)
 	    warning_at (loc, OPT_Wattributes, "only attribute %<fallthrough%>"
 			" can be applied to a null statement");
 	}
-      if (c_parser_next_tokens_start_declaration (parser))
+      if (c_parser_next_tokens_start_declaration (parser)
+	  || (have_std_attrs
+	      && c_parser_next_token_is (parser, CPP_SEMICOLON)))
 	{
 	  error_at (c_parser_peek_token (parser)->location,
 		    "a label can only be part of a statement and "
@@ -5511,8 +5733,11 @@  c_parser_label (c_parser *parser)
 					 /*static_assert_ok*/ true,
 					 /*empty_ok*/ true, /*nested*/ true,
 					 /*start_attr_ok*/ true, NULL,
-					 vNULL);
+					 vNULL, have_std_attrs, std_attrs);
 	}
+      else if (std_attrs)
+	/* Nonempty attributes on the following statement are ignored.  */
+	c_warn_unused_attributes (std_attrs);
     }
 }
 
@@ -5520,17 +5745,18 @@  c_parser_label (c_parser *parser)
 
    statement:
      labeled-statement
-     compound-statement
+     attribute-specifier-sequence[opt] compound-statement
      expression-statement
-     selection-statement
-     iteration-statement
-     jump-statement
+     attribute-specifier-sequence[opt] selection-statement
+     attribute-specifier-sequence[opt] iteration-statement
+     attribute-specifier-sequence[opt] jump-statement
 
    labeled-statement:
-     label statement
+     attribute-specifier-sequence[opt] label statement
 
    expression-statement:
      expression[opt] ;
+     attribute-specifier-sequence expression ;
 
    selection-statement:
      if-statement
@@ -5550,7 +5776,7 @@  c_parser_label (c_parser *parser)
    GNU extensions:
 
    statement:
-     asm-statement
+     attribute-specifier-sequence[opt] asm-statement
 
    jump-statement:
      goto * expression ;
@@ -5561,9 +5787,9 @@  c_parser_label (c_parser *parser)
    Objective-C:
 
    statement:
-     objc-throw-statement
-     objc-try-catch-statement
-     objc-synchronized-statement
+     attribute-specifier-sequence[opt] objc-throw-statement
+     attribute-specifier-sequence[opt] objc-try-catch-statement
+     attribute-specifier-sequence[opt] objc-synchronized-statement
 
    objc-throw-statement:
      @throw expression ;
@@ -5572,7 +5798,7 @@  c_parser_label (c_parser *parser)
    OpenACC:
 
    statement:
-     openacc-construct
+     attribute-specifier-sequence[opt] openacc-construct
 
    openacc-construct:
      parallel-construct
@@ -5595,7 +5821,7 @@  c_parser_label (c_parser *parser)
    OpenMP:
 
    statement:
-     openmp-construct
+     attribute-specifier-sequence[opt] openmp-construct
 
    openmp-construct:
      parallel-construct
@@ -5654,8 +5880,8 @@  c_parser_label (c_parser *parser)
    Transactional Memory:
 
    statement:
-     transaction-statement
-     transaction-cancel-statement
+     attribute-specifier-sequence[opt] transaction-statement
+     attribute-specifier-sequence[opt] transaction-cancel-statement
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
@@ -5671,7 +5897,8 @@  c_parser_statement (c_parser *parser, bool *if_p,
 }
 
 /* Parse a statement, other than a labeled statement.  CHAIN is a vector
-   of if-else-if conditions.
+   of if-else-if conditions.  All labels and standard attributes have
+   been parsed in the caller.
 
    IF_P is used to track whether there's a (possibly labeled) if statement
    which is not enclosed in braces and has an else clause.  This is used to
@@ -6394,7 +6621,8 @@  c_parser_for_statement (c_parser *parser, bool ivd
 	  c_parser_consume_token (parser);
 	  c_finish_expr_stmt (loc, NULL_TREE);
 	}
-      else if (c_parser_next_tokens_start_declaration (parser))
+      else if (c_parser_next_tokens_start_declaration (parser)
+	       || c_parser_nth_token_starts_std_attributes (parser, 1))
 	{
 	  c_parser_declaration_or_fndef (parser, true, true, true, true, true, 
 					 &object_expression, vNULL);
@@ -6421,7 +6649,8 @@  c_parser_for_statement (c_parser *parser, bool ivd
 		 && (c_parser_peek_2nd_token (parser)->keyword
 		     == RID_EXTENSION))
 	    c_parser_consume_token (parser);
-	  if (c_token_starts_declaration (c_parser_peek_2nd_token (parser)))
+	  if (c_token_starts_declaration (c_parser_peek_2nd_token (parser))
+	      || c_parser_nth_token_starts_std_attributes (parser, 2))
 	    {
 	      int ext;
 	      ext = disable_extension_diagnostics ();
@@ -10983,7 +11212,7 @@  c_parser_objc_method_decl (c_parser *parser, bool
 						(parser, attributes) ;
 	      break;
 	    }
-	  parm = c_parser_parameter_declaration (parser, NULL_TREE);
+	  parm = c_parser_parameter_declaration (parser, NULL_TREE, false);
 	  if (parm == NULL)
 	    break;
 	  parms = chainon (parms,
@@ -11148,7 +11377,7 @@  c_parser_objc_try_catch_finally_statement (c_parse
 	{
 	  /* We have "@catch (NSException *exception)" or something
 	     like that.  Parse the parameter declaration.  */
-	  parm = c_parser_parameter_declaration (parser, NULL_TREE);
+	  parm = c_parser_parameter_declaration (parser, NULL_TREE, false);
 	  if (parm == NULL)
 	    parameter_declaration = error_mark_node;
 	  else
@@ -16490,12 +16719,12 @@  c_parser_oacc_routine (c_parser *parser, enum prag
 	  while (c_parser_next_token_is (parser, CPP_KEYWORD)
 		 && c_parser_peek_token (parser)->keyword == RID_EXTENSION);
 	  c_parser_declaration_or_fndef (parser, true, true, true, false, true,
-					 NULL, vNULL, &data);
+					 NULL, vNULL, false, NULL, &data);
 	  restore_extension_diagnostics (ext);
 	}
       else
 	c_parser_declaration_or_fndef (parser, true, true, true, false, true,
-				       NULL, vNULL, &data);
+				       NULL, vNULL, false, NULL, &data);
     }
 }
 
Index: gcc/c/c-parser.h
===================================================================
--- gcc/c/c-parser.h	(revision 278145)
+++ gcc/c/c-parser.h	(working copy)
@@ -190,7 +190,8 @@  extern struct c_declarator *
 c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 		     bool *seen_id);
 extern void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
-				bool, bool, bool, enum c_lookahead_kind);
+				bool, bool, bool, bool, bool,
+				enum c_lookahead_kind);
 extern struct c_type_name *c_parser_type_name (c_parser *, bool = false);
 
 #endif
Index: gcc/c/c-tree.h
===================================================================
--- gcc/c/c-tree.h	(revision 278145)
+++ gcc/c/c-tree.h	(working copy)
@@ -186,9 +186,13 @@  enum c_typespec_kind {
      kind of tag, in which case this is only valid if shadowing that
      tag in an inner scope.  */
   ctsk_tagref,
+  /* Likewise, with standard attributes present in the reference.  */
+  ctsk_tagref_attrs,
   /* A reference to a tag, not previously declared in a visible
      scope.  */
   ctsk_tagfirstref,
+  /* Likewise, with standard attributes present in the reference.  */
+  ctsk_tagfirstref_attrs,
   /* A definition of a tag such as "struct foo { int a; }".  */
   ctsk_tagdef,
   /* A typedef name.  */
@@ -311,10 +315,15 @@  struct c_declspecs {
   tree expr;
   /* The attributes from a typedef decl.  */
   tree decl_attr;
-  /* When parsing, the attributes.  Outside the parser, this will be
-     NULL; attributes (possibly from multiple lists) will be passed
-     separately.  */
+  /* When parsing, the GNU attributes and prefix standard attributes.
+     Outside the parser, this will be NULL; attributes (possibly from
+     multiple lists) will be passed separately.  */
   tree attrs;
+  /* When parsing, postfix standard attributes (which appertain to the
+     type specified by the preceding declaration specifiers, unlike
+     prefix standard attributes which appertain to the declaration or
+     declarations as a whole).  */
+  tree postfix_attrs;
   /* The pass to start compiling a __GIMPLE or __RTL function with.  */
   char *gimple_or_rtl_pass;
   /* ENTRY BB count.  */
@@ -335,7 +344,7 @@  struct c_declspecs {
   ENUM_BITFIELD (c_typespec_keyword) typespec_word : 8;
   /* The kind of type specifier if one has been seen, ctsk_none
      otherwise.  */
-  ENUM_BITFIELD (c_typespec_kind) typespec_kind : 3;
+  ENUM_BITFIELD (c_typespec_kind) typespec_kind : 4;
   ENUM_BITFIELD (c_declspec_il) declspec_il : 3;
   /* Whether any expressions in typeof specifiers may appear in
      constant expressions.  */
@@ -342,6 +351,10 @@  struct c_declspecs {
   BOOL_BITFIELD expr_const_operands : 1;
   /* Whether any declaration specifiers have been seen at all.  */
   BOOL_BITFIELD declspecs_seen_p : 1;
+  /* Whether any declaration specifiers other than standard attributes
+     have been seen at all.  If only standard attributes have been
+     seen, this is an attribute-declaration.  */
+  BOOL_BITFIELD non_std_attrs_seen_p : 1;
   /* Whether something other than a storage class specifier or
      attribute has been seen.  This is used to warn for the
      obsolescent usage of storage class specifiers other than at the
@@ -582,6 +595,7 @@  extern struct c_declarator *set_array_declarator_i
 extern tree c_builtin_function (tree);
 extern tree c_builtin_function_ext_scope (tree);
 extern tree c_simulate_builtin_function_decl (tree);
+extern void c_warn_unused_attributes (tree);
 extern void shadow_tag (const struct c_declspecs *);
 extern void shadow_tag_warned (const struct c_declspecs *, int);
 extern tree start_enum (location_t, struct c_enum_contents *, tree);
@@ -595,7 +609,8 @@  extern void store_parm_decls_from (struct c_arg_in
 extern void temp_store_parm_decls (tree, tree);
 extern void temp_pop_parm_decls (void);
 extern tree xref_tag (enum tree_code, tree);
-extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree);
+extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree,
+					  bool, tree);
 extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
 				    struct c_declarator *, location_t);
 extern struct c_declarator *build_attrs_declarator (tree,
Index: gcc/c/gimple-parser.c
===================================================================
--- gcc/c/gimple-parser.c	(revision 278145)
+++ gcc/c/gimple-parser.c	(working copy)
@@ -2014,7 +2014,7 @@  c_parser_gimple_declaration (gimple_parser &parser
   struct c_declarator *declarator;
   struct c_declspecs *specs = build_null_declspecs ();
   c_parser_declspecs (parser, specs, true, true, true,
-		      true, true, cla_nonabstract_decl);
+		      true, true, true, true, cla_nonabstract_decl);
   finish_declspecs (specs);
 
   /* Provide better error recovery.  Note that a type name here is usually
Index: gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c
===================================================================
--- gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c2x-attr-fallthrough-1.c	(working copy)
@@ -0,0 +1,21 @@ 
+/* Test C2x attribute syntax.  Valid use of fallthrough attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -Wextra" } */
+
+int
+f (int a)
+{
+  int b = 2;
+  switch (a)
+    {
+    case 1:
+      b = 1; /* { dg-warning "may fall through" } */
+    case 2:
+      b = 2;
+      [[fallthrough]];
+    case 3:
+      b += 7;
+      break;
+    }
+  return b;
+}
Index: gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c
===================================================================
--- gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c2x-attr-syntax-1.c	(working copy)
@@ -0,0 +1,55 @@ 
+/* Test C2x attribute syntax.  Basic tests of valid uses of empty
+   attributes.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+[ [ ] ] [[]];
+
+[[]] int [[]] a [[]] = 123;
+
+int f([[]] int x [[]], [[]] long [[]], short [[]] *[[]] [3] [[]],
+      int [[]] (int)[[]], int (*)(int)[[]]) [[]] [[]];
+
+int g [[]] [2] [[]] [3] [[]];
+
+int *[[]] const *[[]] volatile *[[]] *const p;
+
+int *[[]][[]] q = 0;
+
+struct [[]] s;
+union [[]][[]] u;
+
+struct [[]] s2 { [[]] long [[]] *a[[]] [3] [[]] [4], b[[]]; };
+
+union [[]] u2 { [[]] long [[]] *a[[]] [3] [[]] [4]; };
+
+int z = sizeof (int [[]]);
+
+enum [[]] { E1 [[]][[]], E2[[]][[]] = 3 };
+enum [[]] e { E3 = 4, E4 [[]] };
+
+void
+func (void) [[]]
+{
+  [[]] int var;
+  [[]] { }
+  [[]] switch (a) { [[]] case 1: [[]] case 2: [[]] default: [[]] var = 3; }
+  [[]] x : [[]] y: [[]] var = 1;
+  [[]];
+  int [[]] var2;
+  [[]] if (a) [[]] (void) 0; else [[]] (void) 1;
+  [[]] while (0) [[]] var = 2;
+  [[]] do [[]] var = 3; while (0);
+  for ([[]] int zz = 1; zz < 10; zz++)
+    {
+      [[]] var2 = 8;
+      [[]] continue;
+      [[]] break;
+    }
+  if (a) [[]] goto x;
+  [[]] return;
+}
+
+void func2 () [[]];
+
+void func3 () [[]] { }
Index: gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c
===================================================================
--- gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c2x-attr-syntax-2.c	(working copy)
@@ -0,0 +1,60 @@ 
+/* Test C2x attribute syntax.  Test ignored attributes diagnosed.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* A GNU attribute that is valid in some contexts, but should be
+   diagnosed in contexts where all attributes are ignored (attribute
+   declarations, except for fallthrough attributes, and
+   statements).  */
+#define CTX [[gnu::const]]
+
+/* An attribute that is unknown, so ignored with a warning.  */
+#define UNK [[gnu::no_such_attribute(![!(!)!]!,;;)]]
+
+CTX; /* { dg-warning "ignored" } */
+UNK; /* { dg-warning "ignored" } */
+
+UNK extern int a; /* { dg-warning "ignored" } */
+extern int UNK a; /* { dg-warning "ignored" } */
+extern int a UNK; /* { dg-warning "ignored" } */
+
+int f () UNK; /* { dg-warning "ignored" } */
+int f (void) UNK; /* { dg-warning "ignored" } */
+int g (UNK int a); /* { dg-warning "ignored" } */
+int g (int UNK a); /* { dg-warning "ignored" } */
+int g (int a UNK); /* { dg-warning "ignored" } */
+int g (UNK int); /* { dg-warning "ignored" } */
+int g (int UNK); /* { dg-warning "ignored" } */
+int g (int) UNK; /* { dg-warning "ignored" } */
+
+int *UNK p; /* { dg-warning "ignored" } */
+int b[3] UNK; /* { dg-warning "ignored" } */
+
+int h (int () UNK); /* { dg-warning "ignored" } */
+
+struct UNK s; /* { dg-warning "ignored" } */
+union UNK u; /* { dg-warning "ignored" } */
+
+struct UNK s2 { int a; }; /* { dg-warning "ignored" } */
+union UNK u2 { int a; }; /* { dg-warning "ignored" } */
+
+struct s3 { UNK int a; }; /* { dg-warning "ignored" } */
+struct s4 { int UNK a; }; /* { dg-warning "ignored" } */
+union u3 { UNK int a; }; /* { dg-warning "ignored" } */
+union u4 { int UNK a; }; /* { dg-warning "ignored" } */
+
+int z = sizeof (int UNK); /* { dg-warning "ignored" } */
+
+enum UNK { E1 }; /* { dg-warning "ignored" } */
+enum { E2 UNK }; /* { dg-warning "ignored" } */
+enum { E3 UNK = 4 }; /* { dg-warning "ignored" } */
+
+void
+func (void) UNK { /* { dg-warning "ignored" } */
+  UNK int var; /* { dg-warning "ignored" } */
+  CTX { } /* { dg-warning "ignored" } */
+  CTX; /* { dg-warning "ignored" } */
+  CTX var = 1; /* { dg-warning "ignored" } */
+  CTX x: var = 2; /* { dg-warning "ignored" } */
+  for (UNK int zz = 1; zz < 10; zz++) ; /* { dg-warning "ignored" } */
+}
Index: gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c
===================================================================
--- gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/c2x-attr-syntax-3.c	(working copy)
@@ -0,0 +1,56 @@ 
+/* Test C2x attribute syntax.  Invalid uses of attributes.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* Prefix attributes not allowed on declarations without declarators.  */
+
+[[]] struct s { int a; }; /* { dg-error "empty declaration" } */
+
+[[]] union u { int a; }; /* { dg-error "empty declaration" } */
+
+void
+f1 (void)
+{
+  [[]] struct t { int a; }; /* { dg-error "empty declaration" } */
+}
+
+/* Prefix attributes not allowed on _Static_assert.  */
+
+[[]] _Static_assert (1); /* { dg-error "expected" } */
+
+void
+f2 (void)
+{
+  [[]] _Static_assert (1); /* { dg-error "expected" } */
+}
+
+/* Declarations, including attribute declarations, cannot appear after
+   labels.  */
+
+void
+f3 (void)
+{
+ x: [[]];; /* { dg-error "can only be part of a statement" } */
+}
+
+/* Prefix attributes cannot appear on type names.  */
+
+int z = sizeof ([[]] int); /* { dg-error "expected" } */
+
+/* Attributes are not allowed after struct, union or enum, except when
+   the type contents are being defined or the declaration is just
+   "struct-or-union atribute-specifier-sequence identifier;".  */
+
+const struct [[]] s2; /* { dg-warning "useless type qualifier" } */
+/* { dg-error "invalid use of attributes in empty declaration" "invalid" { target *-*-* } .-1 } */
+
+const union [[]] u2; /* { dg-warning "useless type qualifier" } */
+/* { dg-error "invalid use of attributes in empty declaration" "invalid" { target *-*-* } .-1 } */
+
+struct [[]] s3 *sv; /* { dg-error "expected" } */
+
+union [[]] u3 *uv; /* { dg-error "expected" } */
+
+enum e { E1 };
+
+enum [[]] e *ev; /* { dg-error "expected" } */
Index: gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c
===================================================================
--- gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/gnu2x-attr-syntax-1.c	(working copy)
@@ -0,0 +1,16 @@ 
+/* Test C2x attribute syntax.  Basic tests of valid uses of empty
+   attributes with GNU C features.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x" } */
+
+/* Attributes can be used in declarations after __extension__, and
+   before asm statements.  */
+
+__extension__ [[]] int a;
+
+void
+f (void)
+{
+  __extension__ [[]] int b;
+  [[]] asm ("");
+}
Index: gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c
===================================================================
--- gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/gnu2x-attr-syntax-2.c	(working copy)
@@ -0,0 +1,16 @@ 
+/* Test C2x attribute syntax.  Invalid uses of attributes with GNU C
+   features.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x -w" } */
+
+/* Attributes cannot be used as prefix attributes on old-style
+   parameter declarations or on function declarators with identifier
+   lists (removed from C2x).  */
+
+void (*f(a, b) [[]])() int a, b; { } /* { dg-error "expected" } */
+
+void f(x, y) int x; [[]] int y; { } /* { dg-error "expected" } */
+
+/* Nonempty attributes cannot be used as postfix attributes with
+   __auto_type.  */
+__auto_type [[gnu::no_such_attr]] x = 1; /* { dg-error "'__auto_type' followed by" } */
Index: gcc/testsuite/gcc.dg/gnu2x-attrs-1.c
===================================================================
--- gcc/testsuite/gcc.dg/gnu2x-attrs-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/gnu2x-attrs-1.c	(working copy)
@@ -0,0 +1,72 @@ 
+/* Test C2x attribute syntax.  Test GNU attributes appertain to
+   appropriate constructs.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x" } */
+
+void f (void) {};
+
+[[gnu::alias("f")]] void g (void);
+
+void [[gnu::alias("f")]] h (void); /* { dg-warning "ignored" } */
+/* { dg-message "that appertains to a type-specifier" "appertains" { target *-*-* } .-1 } */
+
+struct [[gnu::packed]] s { int a; char b; };
+_Static_assert (sizeof (struct s) == (sizeof (int) + sizeof (char)));
+
+int
+f2 (void)
+{
+  [[gnu::deprecated]] int a = 1;
+  return a; /* { dg-warning "deprecated" } */
+}
+
+int
+f3 (void)
+{
+  int a [[gnu::deprecated]] = 1;
+  return a; /* { dg-warning "deprecated" } */
+}
+
+struct s2 { [[gnu::deprecated]] int a; int b [[gnu::deprecated]]; } x;
+
+int
+f4 (void)
+{
+  return x.a; /* { dg-warning "deprecated" } */
+}
+
+int
+f5 (void)
+{
+  return x.b; /* { dg-warning "deprecated" } */
+}
+
+enum e { E1 [[gnu::deprecated]] };
+
+enum e
+f6 (void)
+{
+  return E1; /* { dg-warning "deprecated" } */
+}
+
+int
+f7 ([[gnu::deprecated]] int y)
+{
+  return y; /* { dg-warning "deprecated" } */
+}
+
+union [[gnu::deprecated]] u { int x; };
+
+void
+f8 (void)
+{
+  union u var; /* { dg-warning "deprecated" } */
+}
+
+enum [[gnu::deprecated]] edep { E2 };
+
+void
+f9 (void)
+{
+  enum edep var; /* { dg-warning "deprecated" } */
+}