aaron.ballman created this revision.
Herald added a subscriber: javed.absar.

WG14 has demonstrated sincere interest in adding C++-style attributes to C for 
C2x, and have added the proposal to their SD-3 document tracking features which 
are possibly desired for the next version of the standard. The proposal has 
*not* been formally adopted into the working draft (C17 needs to be finalized 
first), but it is at the stage where having implementation experience is 
important to the committee.

This patch implements the feature as proposed, but it might make sense to split 
the patch up, depending on what we think is the correct way to proceed. This 
patch adds `c2x` as a new language standard as well as `-fc-attributes` and 
`-fno-c-attributes` flags. The intention is that `-std=c2x` will enable 
`-fc-attributes` once the feature is formally adopted by WG14, but 
`-fc-attributes` can be used to enable the functionality in earlier standard 
modes (but is currently required in order to enable the functionality). 
`fno-c-attributes` is provided on the chance that the syntax proposed causes 
parsing issues for Objective-C and users need a way to work around that; 
however, such cases should be considered bugs that we need to work around 
similar to Objective-C++ and C++11-style attributes. The patch also adds 
support for the `deprecated` attribute (WG14 N2050), mostly so that I would 
have an attribute handy to test the semantic functionality. Once the basic 
syntax is committed, I plan to implement the other attributes: `nodiscard` 
(WG14 N2051), `fallthrough` (WG14 N2052), and `maybe_unused` (WG14 N2053) in 
separate patches.


https://reviews.llvm.org/D37436

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/Attributes.h
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Basic/LangOptions.def
  include/clang/Driver/Options.td
  include/clang/Frontend/LangStandard.h
  include/clang/Frontend/LangStandards.def
  include/clang/Parse/Parser.h
  include/clang/Sema/AttributeList.h
  lib/Frontend/CompilerInvocation.cpp
  lib/Lex/Lexer.cpp
  lib/Parse/ParseDecl.cpp
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseExpr.cpp
  lib/Parse/ParseExprCXX.cpp
  lib/Parse/ParseObjc.cpp
  lib/Parse/ParseOpenMP.cpp
  lib/Parse/ParsePragma.cpp
  lib/Parse/ParseStmt.cpp
  lib/Parse/ParseTemplate.cpp
  lib/Parse/ParseTentative.cpp
  lib/Parse/Parser.cpp
  lib/Sema/AttributeList.cpp
  test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp
  test/Driver/unknown-std.c
  test/Misc/ast-dump-c-attr.c
  test/Parser/c2x-attributes.c
  test/Parser/cxx0x-attributes.cpp
  test/Sema/attr-deprecated-c2x.c
  test/Sema/c11-compat.c
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -58,7 +58,7 @@
 
     assert(V != "GCC" && "Given a GCC spelling, which means this hasn't been"
            "flattened!");
-    if (V == "CXX11" || V == "Pragma")
+    if (V == "CXX11" || V == "C2x" || V == "Pragma")
       NS = Spelling.getValueAsString("Namespace");
     bool Unset;
     K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset);
@@ -1326,7 +1326,7 @@
     if (Variety == "GNU") {
       Prefix = " __attribute__((";
       Suffix = "))";
-    } else if (Variety == "CXX11") {
+    } else if (Variety == "CXX11" || Variety == "C2x") {
       Prefix = " [[";
       Suffix = "]]";
       std::string Namespace = Spellings[I].nameSpace();
@@ -2716,10 +2716,14 @@
       // If this is the C++11 variety, also add in the LangOpts test.
       if (Variety == "CXX11")
         Test += " && LangOpts.CPlusPlus11";
+      else if (Variety == "C2x")
+        Test += " && LangOpts.C2x && LangOpts.CAttributes";
     } else if (Variety == "CXX11")
       // C++11 mode should be checked against LangOpts, which is presumed to be
       // present in the caller.
       Test = "LangOpts.CPlusPlus11";
+    else if (Variety == "C2x")
+      Test = "LangOpts.C2x && LangOpts.CAttributes";
 
     std::string TestStr =
         !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1";
@@ -2740,7 +2744,7 @@
   // and declspecs. Then generate a big switch statement for each of them.
   std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
   std::vector<Record *> Declspec, Microsoft, GNU, Pragma;
-  std::map<std::string, std::vector<Record *>> CXX;
+  std::map<std::string, std::vector<Record *>> CXX, C2x;
 
   // Walk over the list of all attributes, and split them out based on the
   // spelling variety.
@@ -2756,6 +2760,8 @@
         Microsoft.push_back(R);
       else if (Variety == "CXX11")
         CXX[SI.nameSpace()].push_back(R);
+      else if (Variety == "C2x")
+        C2x[SI.nameSpace()].push_back(R);
       else if (Variety == "Pragma")
         Pragma.push_back(R);
     }
@@ -2775,20 +2781,25 @@
   OS << "case AttrSyntax::Pragma:\n";
   OS << "  return llvm::StringSwitch<int>(Name)\n";
   GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma");
-  OS << "case AttrSyntax::CXX: {\n";
-  // C++11-style attributes are further split out based on the Scope.
-  for (auto I = CXX.cbegin(), E = CXX.cend(); I != E; ++I) {
-    if (I != CXX.begin())
-      OS << " else ";
-    if (I->first.empty())
-      OS << "if (!Scope || Scope->getName() == \"\") {\n";
-    else
-      OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
-    OS << "  return llvm::StringSwitch<int>(Name)\n";
-    GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first);
-    OS << "}";
-  }
-  OS << "\n}\n";
+  auto fn = [&OS](const char *Spelling, const char *Variety,
+                  const std::map<std::string, std::vector<Record *>> &List) {
+    OS << "case AttrSyntax::" << Variety << ": {\n";
+    // C++11-style attributes are further split out based on the Scope.
+    for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) {
+      if (I != List.cbegin())
+        OS << " else ";
+      if (I->first.empty())
+        OS << "if (!Scope || Scope->getName() == \"\") {\n";
+      else
+        OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
+      OS << "  return llvm::StringSwitch<int>(Name)\n";
+      GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first);
+      OS << "}";
+    }
+    OS << "\n}\n";
+  };
+  fn("CXX11", "CXX", CXX);
+  fn("C2x", "C2x", C2x);
   OS << "}\n";
 }
 
@@ -2809,10 +2820,11 @@
          << StringSwitch<unsigned>(Spellings[I].variety())
                 .Case("GNU", 0)
                 .Case("CXX11", 1)
-                .Case("Declspec", 2)
-                .Case("Microsoft", 3)
-                .Case("Keyword", 4)
-                .Case("Pragma", 5)
+                .Case("C2x", 2)
+                .Case("Declspec", 3)
+                .Case("Microsoft", 4)
+                .Case("Keyword", 5)
+                .Case("Pragma", 6)
                 .Default(0)
          << " && Scope == \"" << Spellings[I].nameSpace() << "\")\n"
          << "        return " << I << ";\n";
@@ -3505,7 +3517,7 @@
 
   std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
   std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11,
-      Keywords, Pragma;
+      Keywords, Pragma, C2x;
   std::set<std::string> Seen;
   for (const auto *A : Attrs) {
     const Record &Attr = *A;
@@ -3543,6 +3555,10 @@
           Matches = &CXX11;
           Spelling += S.nameSpace();
           Spelling += "::";
+        } else if (Variety == "C2x") {
+          Matches = &C2x;
+          Spelling += S.nameSpace();
+          Spelling += "::";
         } else if (Variety == "GNU")
           Matches = &GNU;
         else if (Variety == "Declspec")
@@ -3581,6 +3597,8 @@
   StringMatcher("Name", Microsoft, OS).Emit();
   OS << "  } else if (AttributeList::AS_CXX11 == Syntax) {\n";
   StringMatcher("Name", CXX11, OS).Emit();
+  OS << "  } else if (AttributeList::AS_C2x == Syntax) {\n";
+  StringMatcher("Name", C2x, OS).Emit();
   OS << "  } else if (AttributeList::AS_Keyword == Syntax || ";
   OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n";
   StringMatcher("Name", Keywords, OS).Emit();
@@ -3666,10 +3684,11 @@
 enum SpellingKind {
   GNU = 1 << 0,
   CXX11 = 1 << 1,
-  Declspec = 1 << 2,
-  Microsoft = 1 << 3,
-  Keyword = 1 << 4,
-  Pragma = 1 << 5
+  C2x = 1 << 2,
+  Declspec = 1 << 3,
+  Microsoft = 1 << 4,
+  Keyword = 1 << 5,
+  Pragma = 1 << 6
 };
 
 static void WriteDocumentation(RecordKeeper &Records,
@@ -3716,6 +3735,7 @@
     SpellingKind Kind = StringSwitch<SpellingKind>(I.variety())
                             .Case("GNU", GNU)
                             .Case("CXX11", CXX11)
+                            .Case("C2x", C2x)
                             .Case("Declspec", Declspec)
                             .Case("Microsoft", Microsoft)
                             .Case("Keyword", Keyword)
@@ -3725,7 +3745,7 @@
     SupportedSpellings |= Kind;
 
     std::string Name;
-    if (Kind == CXX11 && !I.nameSpace().empty())
+    if ((Kind == CXX11 || Kind == C2x) && !I.nameSpace().empty())
       Name = I.nameSpace() + "::";
     Name += I.name();
 
@@ -3754,13 +3774,15 @@
 
   // List what spelling syntaxes the attribute supports.
   OS << ".. csv-table:: Supported Syntaxes\n";
-  OS << "   :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\",";
+  OS << "   :header: \"GNU\", \"C++11\", \"C2x\", \"__declspec\", \"Keyword\",";
   OS << " \"Pragma\", \"Pragma clang attribute\"\n\n";
   OS << "   \"";
   if (SupportedSpellings & GNU) OS << "X";
   OS << "\",\"";
   if (SupportedSpellings & CXX11) OS << "X";
   OS << "\",\"";
+  if (SupportedSpellings & C2x) OS << "X";
+  OS << "\",\"";
   if (SupportedSpellings & Declspec) OS << "X";
   OS << "\",\"";
   if (SupportedSpellings & Keyword) OS << "X";
Index: test/Sema/c11-compat.c
===================================================================
--- test/Sema/c11-compat.c
+++ test/Sema/c11-compat.c
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c2x -fc-attributes -Wc11-compat -verify %s
+
+[[]] int i; // expected-warning {{attribute syntax is incompatible with standards before C2x}}
+
Index: test/Sema/attr-deprecated-c2x.c
===================================================================
--- test/Sema/attr-deprecated-c2x.c
+++ test/Sema/attr-deprecated-c2x.c
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only -std=c2x -fc-attributes
+
+int f() [[deprecated]]; // expected-note 2 {{'f' has been explicitly marked deprecated here}}
+void g() [[deprecated]];// expected-note {{'g' has been explicitly marked deprecated here}}
+void g();
+
+extern int var [[deprecated]]; // expected-note 2 {{'var' has been explicitly marked deprecated here}}
+
+int a() {
+  int (*ptr)() = f; // expected-warning {{'f' is deprecated}}
+  f(); // expected-warning {{'f' is deprecated}}
+
+  // test if attributes propagate to functions
+  g(); // expected-warning {{'g' is deprecated}}
+
+  return var; // expected-warning {{'var' is deprecated}}
+}
+
+// test if attributes propagate to variables
+extern int var;
+int w() {
+  return var; // expected-warning {{'var' is deprecated}}
+}
+
+int old_fn() [[deprecated]];// expected-note {{'old_fn' has been explicitly marked deprecated here}}
+int old_fn();
+int (*fn_ptr)() = old_fn; // expected-warning {{'old_fn' is deprecated}}
+
+int old_fn() {
+  return old_fn()+1;  // no warning, deprecated functions can use deprecated symbols.
+}
+
+struct foo {
+  int x [[deprecated]]; // expected-note 3 {{'x' has been explicitly marked deprecated here}}
+};
+
+void test1(struct foo *F) {
+  ++F->x;  // expected-warning {{'x' is deprecated}}
+  struct foo f1 = { .x = 17 }; // expected-warning {{'x' is deprecated}}
+  struct foo f2 = { 17 }; // expected-warning {{'x' is deprecated}}
+}
+
+typedef struct foo foo_dep [[deprecated]]; // expected-note {{'foo_dep' has been explicitly marked deprecated here}}
+foo_dep *test2;    // expected-warning {{'foo_dep' is deprecated}}
+
+struct [[deprecated, // expected-note {{'bar_dep' has been explicitly marked deprecated here}}
+         invalid_attribute]] bar_dep ;  // expected-warning {{unknown attribute 'invalid_attribute' ignored}}
+
+struct bar_dep *test3;   // expected-warning {{'bar_dep' is deprecated}}
+
+[[deprecated("this is the message")]] int i; // expected-note {{'i' has been explicitly marked deprecated here}}
+void test4(void) {
+  i = 12; // expected-warning {{'i' is deprecated: this is the message}}
+}
Index: test/Parser/cxx0x-attributes.cpp
===================================================================
--- test/Parser/cxx0x-attributes.cpp
+++ test/Parser/cxx0x-attributes.cpp
@@ -175,15 +175,15 @@
 template <> struct [[]] Template<void>;
 
 enum [[]] E1 {};
-enum [[]] E2; // expected-error {{forbids forward references}}
-enum [[]] E1;
-enum [[]] E3 : int;
+enum [[]] E2; // expected-error {{forbids forward references}} // expected-error {{an attribute list cannot appear here}}
+enum [[]] E1; // expected-error {{an attribute list cannot appear here}}
+enum [[]] E3 : int; // expected-error {{an attribute list cannot appear here}}
 enum [[]] {
   k_123 [[]] = 123 // expected-warning {{attributes on an enumerator declaration are incompatible with C++ standards before C++17}}
 };
 enum [[]] E1 e; // expected-error {{an attribute list cannot appear here}}
 enum [[]] class E4 { }; // expected-error {{an attribute list cannot appear here}}
-enum struct [[]] E5;
+enum struct [[]] E5; // expected-error {{an attribute list cannot appear here}}
 
 struct S {
   friend int f [[]] (); // expected-FIXME{{an attribute list cannot appear here}}
Index: test/Parser/c2x-attributes.c
===================================================================
--- test/Parser/c2x-attributes.c
+++ test/Parser/c2x-attributes.c
@@ -0,0 +1,106 @@
+// RUN: %clang_cc1 -fsyntax-only -fc-attributes -verify %s -std=c2x
+
+enum [[]] E {
+  One [[]],
+  Two,
+  Three [[]]
+};
+
+enum [[]] { Four };
+[[]] enum E2 { Five }; // expected-error {{an attribute list cannot appear here}}
+
+// FIXME: this diagnostic can be improved.
+enum { [[]] Six }; // expected-error {{expected identifier}}
+
+// FIXME: this diagnostic can be improved.
+enum E3 [[]] { Seven }; // expected-error {{expected identifier or '('}}
+
+struct [[]] S1 {
+  int i [[]];
+  int [[]] j;
+  int k[10] [[]];
+  int l[[]][10];
+  [[]] int m, n;
+  int o [[]] : 12;
+};
+
+[[]] struct S2 { int a; }; // expected-error {{an attribute list cannot appear here}}
+struct S3 [[]] { int a; }; // expected-error {{an attribute list cannot appear here}}
+
+union [[]] U {
+  double d [[]];
+  [[]] int i;
+};
+
+[[]] union U2 { double d; }; // expected-error {{an attribute list cannot appear here}}
+union U3 [[]] { double d; }; // expected-error {{an attribute list cannot appear here}}
+
+struct [[]] IncompleteStruct;
+union [[]] IncompleteUnion;
+enum [[]] IncompleteEnum; // expected-error {{an attribute list cannot appear here}}
+
+[[]] void f1(void);
+void [[]] f2(void);
+void f3 [[]] (void);
+void f4(void) [[]];
+
+void f5(int i [[]], [[]] int j, int [[]] k);
+
+void f6(a, b) [[]] int a; int b; { // expected-error {{an attribute list cannot appear here}}
+}
+
+// FIXME: technically, an attribute list cannot appear here, but we currently
+// parse it as part of the return type of the function, which is reasonable
+// behavior given that we *don't* want to parse it as part of the K&R parameter
+// declarations. It is disallowed to avoid a parsing ambiguity we already
+// handle well.
+int (*f7(a, b))(int, int) [[]] int a; int b; {
+  return 0;
+}
+
+[[]] int a, b;
+int c [[]], d [[]];
+
+void f8(void) [[]] {
+  [[]] int i, j;
+  int k, l [[]];
+}
+
+[[]] void f9(void) {
+  int i[10] [[]];
+  int (*fp1)(void)[[]];
+  int (*fp2 [[]])(void);
+
+  int * [[]] *ipp;
+}
+
+void f10(int j[static 10] [[]], int k[*] [[]]);
+
+void f11(void) {
+  [[]] {}
+  [[]] if (1) {}
+
+  [[]] switch (1) {
+  [[]] case 1: [[]] break;
+  [[]] default: break;
+  }
+
+  goto foo;
+  [[]] foo: (void)1;
+
+  [[]] for (;;);
+  [[]] while (1);
+  [[]] do [[]] { } while(1);
+
+  [[]] (void)1;
+
+  [[]];
+
+  (void)sizeof(int [4][[]]);
+  (void)sizeof(struct [[]] S3 { int a [[]]; });
+
+  [[]] return;
+}
+
+[[attr]] void f12(void); // expected-warning {{unknown attribute 'attr' ignored}}
+[[vendor::attr]] void f13(void); // expected-warning {{unknown attribute 'attr' ignored}}
Index: test/Misc/ast-dump-c-attr.c
===================================================================
--- test/Misc/ast-dump-c-attr.c
+++ test/Misc/ast-dump-c-attr.c
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c2x -fc-attributes -Wno-deprecated-declarations -ast-dump -ast-dump-filter Test %s | FileCheck --strict-whitespace %s
+
+int Test1 [[deprecated]];
+// CHECK:      VarDecl{{.*}}Test1
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:13> "" ""
+
+enum [[deprecated("Frobble")]] Test2 {
+  Test3 [[deprecated]]
+};
+// CHECK:      EnumDecl{{.*}}Test2
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:8, col:28> "Frobble" ""
+// CHECK-NEXT:   EnumConstantDecl{{.*}}Test3
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:11> "" ""
+
+struct [[deprecated]] Test4 {
+  [[deprecated("Frobble")]] int Test5, Test6;
+  int Test7 [[deprecated]] : 12;
+};
+// CHECK:      RecordDecl{{.*}}Test4
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""
+// CHECK-NEXT:   FieldDecl{{.*}}Test5
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""
+// CHECK-NEXT:   FieldDecl{{.*}}Test6
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""
+// CHECK-NEXT:   FieldDecl{{.*}}Test7
+// CHECK-NEXT:     IntegerLiteral{{.*}}'int' 12
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""
+
+struct [[deprecated]] Test8;
+// CHECK:      RecordDecl{{.*}}Test8
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""
+
+[[deprecated]] void Test9(int Test10 [[deprecated]]);
+// CHECK:      FunctionDecl{{.*}}Test9
+// CHECK-NEXT:   ParmVarDecl{{.*}}Test10
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:40> "" ""
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:3> "" ""
+
+void Test11 [[deprecated]](void);
+// CHECK:      FunctionDecl{{.*}}Test11
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""
+
+void Test12(void) [[deprecated]] {}
+// CHECK:      FunctionDecl{{.*}}Test12
+// CHECK-NEXT:   CompoundStmt
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:21> "" ""
Index: test/Driver/unknown-std.c
===================================================================
--- test/Driver/unknown-std.c
+++ test/Driver/unknown-std.c
@@ -14,6 +14,7 @@
 // CHECK-NEXT: note: use 'gnu99' for 'ISO C 1999 with GNU extensions' standard
 // CHECK-NEXT: note: use 'c11' or 'iso9899:2011' for 'ISO C 2011' standard
 // CHECK-NEXT: note: use 'gnu11' for 'ISO C 2011 with GNU extensions' standard
+// CHECK-NEXT: note: use 'c2x' for 'Working draft for ISO C 202x' standard
 
 // Make sure that no other output is present.
 // CHECK-NOT: {{^.+$}}
Index: test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp
===================================================================
--- test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp
+++ test/CXX/dcl.dcl/dcl.attr/dcl.align/p6.cpp
@@ -28,18 +28,18 @@
 alignas(4) extern int n9; // expected-note {{declared with 'alignas' attribute here}}
 
 
-enum alignas(2) E : char; // expected-note {{declared with 'alignas' attribute here}}
-enum E : char {}; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}}
+enum alignas(2) E : char; // expected-error {{an attribute list cannot appear here}}
+enum E : char {};
 
-enum alignas(4) F : char; // expected-note {{previous declaration is here}}
-enum alignas(2) F : char; // expected-error {{redeclaration has different alignment requirement (2 vs 4)}}
+enum alignas(4) F : char; // expected-error {{an attribute list cannot appear here}}
+enum alignas(2) F : char; // expected-error {{an attribute list cannot appear here}}
 
 enum G : char;
 enum alignas(8) G : char {};
 enum G : char;
 
-enum H : char {}; // expected-error {{'alignas' must be specified on definition if it is specified on any declaration}}
-enum alignas(1) H : char; // expected-note {{declared with 'alignas' attribute here}}
+enum H : char {};
+enum alignas(1) H : char; // expected-error {{an attribute list cannot appear here}}
 
 
 struct S;
@@ -74,7 +74,7 @@
 char *x1248 = X<1,2,4,8>::Buffer; // expected-note {{in instantiation of}}
 
 template<int M, int N, int O, int P> struct Y {
-  enum alignas(M) alignas(N) E : char;
+  enum alignas(M) alignas(N) E : char; // expected-error {{an attribute list cannot appear here}}
 };
 template<int M, int N, int O, int P>
 enum alignas(O) alignas(P) Y<M,N,O,P>::E : char { e };
Index: lib/Sema/AttributeList.cpp
===================================================================
--- lib/Sema/AttributeList.cpp
+++ lib/Sema/AttributeList.cpp
@@ -114,7 +114,8 @@
   // Normalize the attribute name, __foo__ becomes foo. This is only allowable
   // for GNU attributes.
   bool IsGNU = SyntaxUsed == AttributeList::AS_GNU ||
-               (SyntaxUsed == AttributeList::AS_CXX11 && ScopeName == "gnu");
+               ((SyntaxUsed == AttributeList::AS_CXX11 ||
+                SyntaxUsed == AttributeList::AS_C2x) && ScopeName == "gnu");
   if (IsGNU && AttrName.size() >= 4 && AttrName.startswith("__") &&
       AttrName.endswith("__"))
     AttrName = AttrName.slice(2, AttrName.size() - 2);
@@ -135,7 +136,7 @@
 
   // Ensure that in the case of C++11 attributes, we look for '::foo' if it is
   // unscoped.
-  if (ScopeName || SyntaxUsed == AS_CXX11)
+  if (ScopeName || SyntaxUsed == AS_CXX11 || SyntaxUsed == AS_C2x)
     FullName += "::";
   FullName += AttrName;
 
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -608,7 +608,7 @@
   }
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
 
   Result = ParseExternalDeclaration(attrs);
   return false;
@@ -2028,7 +2028,7 @@
   // FIXME: Support module import within __if_exists?
   while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
     ParsedAttributesWithRange attrs(AttrFactory);
-    MaybeParseCXX11Attributes(attrs);
+    MaybeParseAttributes(attrs);
     DeclGroupPtrTy Result = ParseExternalDeclaration(attrs);
     if (Result && !getCurScope()->getParent())
       Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
@@ -2070,8 +2070,8 @@
 
   // We don't support any module attributes yet; just parse them and diagnose.
   ParsedAttributesWithRange Attrs(AttrFactory);
-  MaybeParseCXX11Attributes(Attrs);
-  ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_module_attr);
+  MaybeParseAttributes(Attrs);
+  ProhibitAttributes(Attrs, diag::err_attribute_not_module_attr);
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
@@ -2098,9 +2098,9 @@
     return nullptr;
 
   ParsedAttributesWithRange Attrs(AttrFactory);
-  MaybeParseCXX11Attributes(Attrs);
+  MaybeParseAttributes(Attrs);
   // We don't support any module import attributes yet.
-  ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_import_attr);
+  ProhibitAttributes(Attrs, diag::err_attribute_not_import_attr);
 
   if (PP.hadModuleLoaderFatalFailure()) {
     // With a fatal failure in the module loader, we abort parsing.
Index: lib/Parse/ParseTentative.cpp
===================================================================
--- lib/Parse/ParseTentative.cpp
+++ lib/Parse/ParseTentative.cpp
@@ -587,9 +587,9 @@
 ///     attribute-argument-clause:
 ///         '(' balanced-token-seq ')'
 Parser::CXX11AttributeKind
-Parser::isCXX11AttributeSpecifier(bool Disambiguate,
+Parser::isAttributeSpecifier(bool Disambiguate,
                                   bool OuterMightBeMessageSend) {
-  if (Tok.is(tok::kw_alignas))
+  if (Tok.is(tok::kw_alignas) && getLangOpts().CPlusPlus11)
     return CAK_AttributeSpecifier;
 
   if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square))
@@ -660,13 +660,13 @@
     //   requirements of an identifier is contained in an attribute-token,
     //   it is considered an identifier.
     SourceLocation Loc;
-    if (!TryParseCXX11AttributeIdentifier(Loc)) {
+    if (!TryParseAttributeIdentifier(Loc)) {
       IsAttribute = false;
       break;
     }
     if (Tok.is(tok::coloncolon)) {
       ConsumeToken();
-      if (!TryParseCXX11AttributeIdentifier(Loc)) {
+      if (!TryParseAttributeIdentifier(Loc)) {
         IsAttribute = false;
         break;
       }
@@ -1753,8 +1753,8 @@
     }
 
     // An attribute-specifier-seq here is a sign of a function declarator.
-    if (isCXX11AttributeSpecifier(/*Disambiguate*/false,
-                                  /*OuterMightBeMessageSend*/true))
+    if (isAttributeSpecifier(/*Disambiguate*/ false,
+                             /*OuterMightBeMessageSend*/ true))
       return TPResult::True;
 
     ParsedAttributes attrs(AttrFactory);
Index: lib/Parse/ParseTemplate.cpp
===================================================================
--- lib/Parse/ParseTemplate.cpp
+++ lib/Parse/ParseTemplate.cpp
@@ -194,7 +194,7 @@
   }
 
   ParsedAttributesWithRange prefixAttrs(AttrFactory);
-  MaybeParseCXX11Attributes(prefixAttrs);
+  MaybeParseAttributes(prefixAttrs);
 
   if (Tok.is(tok::kw_using)) {
     // FIXME: We should return the DeclGroup to the caller.
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -103,7 +103,7 @@
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
 
   ParsedAttributesWithRange Attrs(AttrFactory);
-  MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true);
+  MaybeParseAttributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true);
   if (!MaybeParseOpenCLUnrollHintAttribute(Attrs))
     return StmtError();
 
@@ -1011,8 +1011,8 @@
         ConsumeToken();
 
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs, nullptr,
-                                /*MightBeObjCMessageSend*/ true);
+      MaybeParseAttributes(attrs, nullptr,
+                           /*MightBeObjCMessageSend*/ true);
 
       // If this is the start of a declaration, parse it as such.
       if (isDeclarationStatement()) {
@@ -1476,7 +1476,7 @@
   T.consumeOpen();
 
   // A do-while expression is not a condition, so can't have attributes.
-  DiagnoseAndSkipCXX11Attributes();
+  DiagnoseAndSkipAttributes();
 
   ExprResult Cond = ParseExpression();
   T.consumeClose();
@@ -1499,7 +1499,7 @@
   if (Next.isOneOf(tok::l_square, tok::kw_alignas)) {
     TentativeParsingAction PA(*this);
     ConsumeToken();
-    SkipCXX11Attributes();
+    SkipAttributes();
     bool Result = Tok.is(tok::colon);
     PA.Revert();
     return Result;
@@ -1589,7 +1589,7 @@
   }
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
 
   // Parse the first part of the for specifier.
   if (Tok.is(tok::semi)) {  // for (;
@@ -1601,7 +1601,7 @@
     ProhibitAttributes(attrs);
     IdentifierInfo *Name = Tok.getIdentifierInfo();
     SourceLocation Loc = ConsumeToken();
-    MaybeParseCXX11Attributes(attrs);
+    MaybeParseAttributes(attrs);
 
     ForRangeInit.ColonLoc = ConsumeToken();
     if (Tok.is(tok::l_brace))
@@ -1941,7 +1941,7 @@
   }
 
   // Get the next statement.
-  MaybeParseCXX11Attributes(Attrs);
+  MaybeParseAttributes(Attrs);
 
   StmtResult S = ParseStatementOrDeclarationAfterAttributes(
       Stmts, Allowed, TrailingElseLoc, Attrs);
@@ -2122,7 +2122,7 @@
 
     // C++11 attributes can't appear here, despite this context seeming
     // statement-like.
-    DiagnoseAndSkipCXX11Attributes();
+    DiagnoseAndSkipAttributes();
 
     if (Tok.isNot(tok::kw_catch))
       return StmtError(Diag(Tok, diag::err_expected_catch));
@@ -2170,7 +2170,7 @@
   Decl *ExceptionDecl = nullptr;
   if (Tok.isNot(tok::ellipsis)) {
     ParsedAttributesWithRange Attributes(AttrFactory);
-    MaybeParseCXX11Attributes(Attributes);
+    MaybeParseAttributes(Attributes);
 
     DeclSpec DS(AttrFactory);
     DS.takeAttributesFrom(Attributes);
Index: lib/Parse/ParsePragma.cpp
===================================================================
--- lib/Parse/ParsePragma.cpp
+++ lib/Parse/ParsePragma.cpp
@@ -1281,8 +1281,8 @@
   };
 
   if (Tok.is(tok::l_square) && NextToken().is(tok::l_square)) {
-    // Parse the CXX11 style attribute.
-    ParseCXX11AttributeSpecifier(Attrs);
+    // Parse the C++11 or C2x style attribute.
+    ParseAttributeSpecifier(Attrs);
   } else if (Tok.is(tok::kw___attribute)) {
     ConsumeToken();
     if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after,
Index: lib/Parse/ParseOpenMP.cpp
===================================================================
--- lib/Parse/ParseOpenMP.cpp
+++ lib/Parse/ParseOpenMP.cpp
@@ -625,7 +625,7 @@
       // Here we expect to see some function declaration.
       if (AS == AS_none) {
         assert(TagType == DeclSpec::TST_unspecified);
-        MaybeParseCXX11Attributes(Attrs);
+        MaybeParseAttributes(Attrs);
         ParsingDeclSpec PDS(*this);
         Ptr = ParseExternalDeclaration(Attrs, &PDS);
       } else {
@@ -686,7 +686,7 @@
     while (DKind != OMPD_end_declare_target && DKind != OMPD_declare_target &&
            Tok.isNot(tok::eof) && Tok.isNot(tok::r_brace)) {
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
+      MaybeParseAttributes(attrs);
       ParseExternalDeclaration(attrs);
       if (Tok.isAnnotation() && Tok.is(tok::annot_pragma_openmp)) {
         TentativeParsingAction TPA(*this);
Index: lib/Parse/ParseObjc.cpp
===================================================================
--- lib/Parse/ParseObjc.cpp
+++ lib/Parse/ParseObjc.cpp
@@ -2219,7 +2219,7 @@
     ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl);
     while (!ObjCImplParsing.isFinished() && !isEofOrEom()) {
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
+      MaybeParseAttributes(attrs);
       if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) {
         DeclGroupRef DG = DGP.get();
         DeclsInGroup.append(DG.begin(), DG.end());
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -1168,7 +1168,7 @@
       DeclEndLoc = ESpecRange.getEnd();
 
     // Parse attribute-specifier[opt].
-    MaybeParseCXX11Attributes(Attr, &DeclEndLoc);
+    MaybeParseAttributes(Attr, &DeclEndLoc);
 
     SourceLocation FunLocalRangeEnd = DeclEndLoc;
 
@@ -1241,7 +1241,7 @@
     }
 
     // Parse attribute-specifier[opt].
-    MaybeParseCXX11Attributes(Attr, &DeclEndLoc);
+    MaybeParseAttributes(Attr, &DeclEndLoc);
 
     // Parse the return type, if there is one.
     if (Tok.is(tok::arrow)) {
@@ -1732,7 +1732,7 @@
   }
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
 
   // Determine what kind of thing we have.
   switch (isCXXConditionDeclarationOrInitStatement(InitStmt)) {
@@ -2802,7 +2802,7 @@
   bool first = true;
   while (Tok.is(tok::l_square)) {
     // An array-size expression can't start with a lambda.
-    if (CheckProhibitedCXX11Attribute())
+    if (CheckProhibitedAttribute())
       continue;
 
     BalancedDelimiterTracker T(*this, tok::l_square);
@@ -2821,7 +2821,7 @@
 
     // Attributes here appertain to the array type. C++11 [expr.new]p5.
     ParsedAttributes Attrs(AttrFactory);
-    MaybeParseCXX11Attributes(Attrs);
+    MaybeParseAttributes(Attrs);
 
     D.AddTypeInfo(DeclaratorChunk::getArray(0,
                                             /*static=*/false, /*star=*/false,
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -1503,7 +1503,7 @@
 
       // Reject array indices starting with a lambda-expression. '[[' is
       // reserved for attributes.
-      if (CheckProhibitedCXX11Attribute()) {
+      if (CheckProhibitedAttribute()) {
         (void)Actions.CorrectDelayedTyposInExpr(LHS);
         return ExprError();
       }
@@ -2083,7 +2083,7 @@
         Comps.back().LocEnd = ConsumeToken();
 
       } else if (Tok.is(tok::l_square)) {
-        if (CheckProhibitedCXX11Attribute())
+        if (CheckProhibitedAttribute())
           return ExprError();
 
         // offsetof-member-designator: offsetof-member-design '[' expression ']'
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -76,12 +76,12 @@
 
   ParsedAttributesWithRange attrs(AttrFactory);
   SourceLocation attrLoc;
-  if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+  if (getLangOpts().CPlusPlus11 && isAttributeSpecifier()) {
     if (!getLangOpts().CPlusPlus1z)
       Diag(Tok.getLocation(), diag::warn_cxx14_compat_attribute)
           << 0 /*namespace*/;
     attrLoc = Tok.getLocation();
-    ParseCXX11Attributes(attrs);
+    ParseAttributes(attrs);
   }
 
   if (Tok.is(tok::identifier)) {
@@ -216,7 +216,7 @@
     while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
            Tok.isNot(tok::eof)) {
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
+      MaybeParseAttributes(attrs);
       ParseExternalDeclaration(attrs);
     }
 
@@ -319,7 +319,7 @@
                 Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation());
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
 
   if (Tok.isNot(tok::l_brace)) {
     // Reset the source range in DS, as the leading "extern"
@@ -369,7 +369,7 @@
       // Fall through.
     default:
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX11Attributes(attrs);
+      MaybeParseAttributes(attrs);
       ParseExternalDeclaration(attrs);
       continue;
     }
@@ -401,7 +401,7 @@
   if (Tok.isNot(tok::l_brace)) {
     // FIXME: Factor out a ParseExternalDeclarationWithAttrs.
     ParsedAttributesWithRange Attrs(AttrFactory);
-    MaybeParseCXX11Attributes(Attrs);
+    MaybeParseAttributes(Attrs);
     MaybeParseMicrosoftAttributes(Attrs);
     ParseExternalDeclaration(Attrs);
     return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
@@ -420,7 +420,7 @@
   while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
          Tok.isNot(tok::eof)) {
     ParsedAttributesWithRange Attrs(AttrFactory);
-    MaybeParseCXX11Attributes(Attrs);
+    MaybeParseAttributes(Attrs);
     MaybeParseMicrosoftAttributes(Attrs);
     ParseExternalDeclaration(Attrs);
   }
@@ -635,14 +635,14 @@
   // Check for misplaced attributes before the identifier in an
   // alias-declaration.
   ParsedAttributesWithRange MisplacedAttrs(AttrFactory);
-  MaybeParseCXX11Attributes(MisplacedAttrs);
+  MaybeParseAttributes(MisplacedAttrs);
 
   UsingDeclarator D;
   bool InvalidDeclarator = ParseUsingDeclarator(Context, D);
 
   ParsedAttributesWithRange Attrs(AttrFactory);
   MaybeParseGNUAttributes(Attrs);
-  MaybeParseCXX11Attributes(Attrs);
+  MaybeParseAttributes(Attrs);
 
   // Maybe this is an alias-declaration.
   if (Tok.is(tok::equal)) {
@@ -1397,7 +1397,7 @@
   // If C++0x attributes exist here, parse them.
   // FIXME: Are we consistent with the ordering of parsing of different
   // styles of attributes?
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
 
   // Source location used by FIXIT to insert misplaced
   // C++11 attributes
@@ -1614,7 +1614,7 @@
   // For these, DSC is DSC_type_specifier or DSC_alias_declaration.
 
   // If there are attributes after class name, parse them.
-  MaybeParseCXX11Attributes(Attributes);
+  MaybeParseAttributes(Attributes);
 
   const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
   Sema::TagUseKind TUK;
@@ -2028,20 +2028,20 @@
   SourceLocation StartLoc = Tok.getLocation();
 
   ParsedAttributesWithRange Attributes(AttrFactory);
-  MaybeParseCXX11Attributes(Attributes);
+  MaybeParseAttributes(Attributes);
 
   // Parse the 'virtual' keyword.
   if (TryConsumeToken(tok::kw_virtual))
     IsVirtual = true;
 
-  CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+  CheckMisplacedAttribute(Attributes, StartLoc);
 
   // Parse an (optional) access specifier.
   AccessSpecifier Access = getAccessSpecifierIfPresent();
   if (Access != AS_none)
     ConsumeToken();
 
-  CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+  CheckMisplacedAttribute(Attributes, StartLoc);
 
   // Parse the 'virtual' keyword (again!), in case it came after the
   // access specifier.
@@ -2056,7 +2056,7 @@
     IsVirtual = true;
   }
 
-  CheckMisplacedCXX11Attribute(Attributes, StartLoc);
+  CheckMisplacedAttribute(Attributes, StartLoc);
 
   // Parse the class-name.
 
@@ -2523,7 +2523,7 @@
   ParsedAttributesWithRange attrs(AttrFactory);
   ParsedAttributesWithRange FnAttrs(AttrFactory);
   // Optional C++11 attribute-specifier
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
   // We need to keep these attributes for future diagnostic
   // before they are taken over by declaration specifier.
   FnAttrs.addAll(attrs.getList());
@@ -2958,7 +2958,7 @@
     // Diagnose any C++11 attributes after 'final' keyword.
     // We deliberately discard these attributes.
     ParsedAttributesWithRange Attrs(AttrFactory);
-    CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
+    CheckMisplacedAttribute(Attrs, AttrFixitLoc);
 
     // This can only happen if we had malformed misplaced attributes;
     // we only get called if there is a colon or left-brace after the
@@ -3177,7 +3177,7 @@
     // These attributes are not allowed to appear here,
     // and the only possible place for them to appertain
     // to the class would be between class-key and class-name.
-    CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
+    CheckMisplacedAttribute(Attrs, AttrFixitLoc);
 
     // ParseClassSpecifier() does only a superficial check for attributes before
     // deciding to call this method.  For example, for
@@ -3775,7 +3775,7 @@
 ///   If a keyword or an alternative token that satisfies the syntactic
 ///   requirements of an identifier is contained in an attribute-token,
 ///   it is considered an identifier.
-IdentifierInfo *Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc) {
+IdentifierInfo *Parser::TryParseAttributeIdentifier(SourceLocation &Loc) {
   switch (Tok.getKind()) {
   default:
     // Identifiers and keywords have identifier info attached.
@@ -3813,7 +3813,7 @@
 }
 
 static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
-                                               IdentifierInfo *ScopeName) {
+                                              IdentifierInfo *ScopeName) {
   switch (AttributeList::getKind(AttrName, ScopeName,
                                  AttributeList::AS_CXX11)) {
   case AttributeList::AT_CarriesDependency:
@@ -3844,19 +3844,22 @@
 ///         '[' balanced-token-seq ']'
 ///         '{' balanced-token-seq '}'
 ///         any token but '(', ')', '[', ']', '{', or '}'
-bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
-                                     SourceLocation AttrNameLoc,
-                                     ParsedAttributes &Attrs,
-                                     SourceLocation *EndLoc,
-                                     IdentifierInfo *ScopeName,
-                                     SourceLocation ScopeLoc) {
+bool Parser::ParseAttributeArgs(IdentifierInfo *AttrName,
+                                SourceLocation AttrNameLoc,
+                                ParsedAttributes &Attrs, SourceLocation *EndLoc,
+                                IdentifierInfo *ScopeName,
+                                SourceLocation ScopeLoc) {
   assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
   SourceLocation LParenLoc = Tok.getLocation();
+  AttributeList::Syntax Syntax = getLangOpts().CPlusPlus11
+                                     ? AttributeList::AS_CXX11
+                                     : AttributeList::AS_C2x;
 
   // If the attribute isn't known, we will not attempt to parse any
   // arguments.
-  if (!hasAttribute(AttrSyntax::CXX, ScopeName, AttrName,
-                    getTargetInfo(), getLangOpts())) {
+  if (!hasAttribute(getLangOpts().CPlusPlus11 ? AttrSyntax::CXX
+                                              : AttrSyntax::C2x,
+                    ScopeName, AttrName, getTargetInfo(), getLangOpts())) {
     // Eat the left paren, then skip to the ending right paren.
     ConsumeParen();
     SkipUntil(tok::r_paren);
@@ -3867,20 +3870,20 @@
     // GNU-scoped attributes have some special cases to handle GNU-specific
     // behaviors.
     ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
-                          ScopeLoc, AttributeList::AS_CXX11, nullptr);
+                          ScopeLoc, Syntax, nullptr);
     return true;
   }
 
   unsigned NumArgs;
   // Some Clang-scoped attributes have some special parsing behavior.
   if (ScopeName && ScopeName->getName() == "clang")
     NumArgs =
         ParseClangAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
-                                ScopeLoc, AttributeList::AS_CXX11);
+                                ScopeLoc, Syntax);
   else
     NumArgs =
         ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
-                                 ScopeName, ScopeLoc, AttributeList::AS_CXX11);
+                                 ScopeName, ScopeLoc, Syntax);
 
   const AttributeList *Attr = Attrs.getList();
   if (Attr && IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
@@ -3906,7 +3909,7 @@
   return true;
 }
 
-/// ParseCXX11AttributeSpecifier - Parse a C++11 attribute-specifier.
+/// ParseAttributeSpecifier - Parse a C++11 or C2x attribute-specifier.
 ///
 /// [C++11] attribute-specifier:
 ///         '[' '[' attribute-list ']' ']'
@@ -3930,40 +3933,42 @@
 ///
 /// [C++11] attribute-namespace:
 ///         identifier
-void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
-                                          SourceLocation *endLoc) {
-  if (Tok.is(tok::kw_alignas)) {
+void Parser::ParseAttributeSpecifier(ParsedAttributes &attrs,
+                                     SourceLocation *endLoc) {
+  bool CXXAttr = getLangOpts().CPlusPlus11;
+  if (CXXAttr && Tok.is(tok::kw_alignas)) {
     Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
     ParseAlignmentSpecifier(attrs, endLoc);
     return;
   }
 
-  assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)
-      && "Not a C++11 attribute list");
+  assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square) &&
+         "Not a standards-based attribute list");
 
-  Diag(Tok.getLocation(), diag::warn_cxx98_compat_attribute);
+  Diag(Tok.getLocation(), CXXAttr ? diag::warn_cxx98_compat_attribute
+                                  : diag::warn_c11_compat_attribute);
 
   ConsumeBracket();
   ConsumeBracket();
 
   SourceLocation CommonScopeLoc;
   IdentifierInfo *CommonScopeName = nullptr;
-  if (Tok.is(tok::kw_using)) {
+  if (CXXAttr && Tok.is(tok::kw_using)) {
     Diag(Tok.getLocation(), getLangOpts().CPlusPlus1z
                                 ? diag::warn_cxx14_compat_using_attribute_ns
                                 : diag::ext_using_attribute_ns);
     ConsumeToken();
 
-    CommonScopeName = TryParseCXX11AttributeIdentifier(CommonScopeLoc);
+    CommonScopeName = TryParseAttributeIdentifier(CommonScopeLoc);
     if (!CommonScopeName) {
       Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
       SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
     }
     if (!TryConsumeToken(tok::colon) && CommonScopeName)
       Diag(Tok.getLocation(), diag::err_expected) << tok::colon;
   }
 
-  llvm::SmallDenseMap<IdentifierInfo*, SourceLocation, 4> SeenAttrs;
+  llvm::SmallDenseMap<IdentifierInfo *, SourceLocation, 4> SeenAttrs;
 
   while (Tok.isNot(tok::r_square)) {
     // attribute not present
@@ -3973,7 +3978,7 @@
     SourceLocation ScopeLoc, AttrLoc;
     IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;
 
-    AttrName = TryParseCXX11AttributeIdentifier(AttrLoc);
+    AttrName = TryParseAttributeIdentifier(AttrLoc);
     if (!AttrName)
       // Break out to the "expected ']'" diagnostic.
       break;
@@ -3983,7 +3988,7 @@
       ScopeName = AttrName;
       ScopeLoc = AttrLoc;
 
-      AttrName = TryParseCXX11AttributeIdentifier(AttrLoc);
+      AttrName = TryParseAttributeIdentifier(AttrLoc);
       if (!AttrName) {
         Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
         SkipUntil(tok::r_square, tok::comma, StopAtSemi | StopBeforeMatch);
@@ -4011,18 +4016,19 @@
 
     // Parse attribute arguments
     if (Tok.is(tok::l_paren))
-      AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, attrs, endLoc,
-                                           ScopeName, ScopeLoc);
+      AttrParsed = ParseAttributeArgs(AttrName, AttrLoc, attrs, endLoc,
+                                      ScopeName, ScopeLoc);
 
     if (!AttrParsed)
-      attrs.addNew(AttrName,
-                   SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc,
-                               AttrLoc),
-                   ScopeName, ScopeLoc, nullptr, 0, AttributeList::AS_CXX11);
+      attrs.addNew(
+          AttrName,
+          SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, AttrLoc),
+          ScopeName, ScopeLoc, nullptr, 0,
+          CXXAttr ? AttributeList::AS_CXX11 : AttributeList::AS_C2x);
 
     if (TryConsumeToken(tok::ellipsis))
       Diag(Tok, diag::err_cxx11_attribute_forbids_ellipsis)
-        << AttrName->getName();
+          << AttrName->getName();
   }
 
   if (ExpectAndConsume(tok::r_square))
@@ -4033,41 +4039,40 @@
     SkipUntil(tok::r_square);
 }
 
-/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq.
+/// ParseAttributes - Parse a C++11 or C2x attribute-specifier-seq.
 ///
 /// attribute-specifier-seq:
 ///       attribute-specifier-seq[opt] attribute-specifier
-void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
-                                  SourceLocation *endLoc) {
-  assert(getLangOpts().CPlusPlus11);
+void Parser::ParseAttributes(ParsedAttributesWithRange &attrs,
+                             SourceLocation *endLoc) {
+  assert(standardAttributesAllowed());
 
   SourceLocation StartLoc = Tok.getLocation(), Loc;
   if (!endLoc)
     endLoc = &Loc;
 
   do {
-    ParseCXX11AttributeSpecifier(attrs, endLoc);
-  } while (isCXX11AttributeSpecifier());
+    ParseAttributeSpecifier(attrs, endLoc);
+  } while (isAttributeSpecifier());
 
   attrs.Range = SourceRange(StartLoc, *endLoc);
 }
 
-void Parser::DiagnoseAndSkipCXX11Attributes() {
+void Parser::DiagnoseAndSkipAttributes() {
   // Start and end location of an attribute or an attribute list.
   SourceLocation StartLoc = Tok.getLocation();
-  SourceLocation EndLoc = SkipCXX11Attributes();
+  SourceLocation EndLoc = SkipAttributes();
 
   if (EndLoc.isValid()) {
     SourceRange Range(StartLoc, EndLoc);
-    Diag(StartLoc, diag::err_attributes_not_allowed)
-      << Range;
+    Diag(StartLoc, diag::err_attributes_not_allowed) << Range;
   }
 }
 
-SourceLocation Parser::SkipCXX11Attributes() {
+SourceLocation Parser::SkipAttributes() {
   SourceLocation EndLoc;
 
-  if (!isCXX11AttributeSpecifier())
+  if (!isAttributeSpecifier())
     return EndLoc;
 
   do {
@@ -4084,7 +4089,7 @@
         T.skipToEnd();
       EndLoc = T.getCloseLocation();
     }
-  } while (isCXX11AttributeSpecifier());
+  } while (isAttributeSpecifier());
 
   return EndLoc;
 }
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -1509,10 +1509,10 @@
 /// \return \c true if we skipped an attribute-like chunk of tokens, \c false if
 /// this doesn't appear to actually be an attribute-specifier, and the caller
 /// should try to parse it.
-bool Parser::DiagnoseProhibitedCXX11Attribute() {
+bool Parser::DiagnoseProhibitedAttribute() {
   assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square));
 
-  switch (isCXX11AttributeSpecifier(/*Disambiguate*/true)) {
+  switch (isAttributeSpecifier(/*Disambiguate*/true)) {
   case CAK_NotAttributeSpecifier:
     // No diagnostic: we're in Obj-C++11 and this is not actually an attribute.
     return false;
@@ -1539,37 +1539,35 @@
 /// attribute-specifier in a location where an attribute is not permitted, but
 /// we know where the attributes ought to be written. Parse them anyway, and
 /// provide a fixit moving them to the right place.
-void Parser::DiagnoseMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
-                                             SourceLocation CorrectLocation) {
+void Parser::DiagnoseMisplacedAttribute(ParsedAttributesWithRange &Attrs,
+                                        SourceLocation CorrectLocation) {
   assert((Tok.is(tok::l_square) && NextToken().is(tok::l_square)) ||
          Tok.is(tok::kw_alignas));
 
   // Consume the attributes.
   SourceLocation Loc = Tok.getLocation();
-  ParseCXX11Attributes(Attrs);
+  ParseAttributes(Attrs);
   CharSourceRange AttrRange(SourceRange(Loc, Attrs.Range.getEnd()), true);
 
   Diag(Loc, diag::err_attributes_not_allowed)
-    << FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange)
-    << FixItHint::CreateRemoval(AttrRange);
+      << FixItHint::CreateInsertionFromRange(CorrectLocation, AttrRange)
+      << FixItHint::CreateRemoval(AttrRange);
 }
 
 void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
-  Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed)
-    << attrs.Range;
+  Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed) << attrs.Range;
 }
 
-void Parser::ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs,
-                                     unsigned DiagID) {
+void Parser::ProhibitAttributes(ParsedAttributesWithRange &Attrs,
+                                unsigned DiagID) {
   for (AttributeList *Attr = Attrs.getList(); Attr; Attr = Attr->getNext()) {
     if (!Attr->isCXX11Attribute())
       continue;
     if (Attr->getKind() == AttributeList::UnknownAttribute)
       Diag(Attr->getLoc(), diag::warn_unknown_attribute_ignored)
           << Attr->getName();
     else {
-      Diag(Attr->getLoc(), DiagID)
-        << Attr->getName();
+      Diag(Attr->getLoc(), DiagID) << Attr->getName();
       Attr->setInvalid();
     }
   }
@@ -2893,7 +2891,7 @@
         // Reject C++11 attributes that appertain to decl specifiers as
         // we don't support any C++11 attributes that appertain to decl
         // specifiers. This also conforms to what g++ 4.8 is doing.
-        ProhibitCXX11Attributes(attrs, diag::err_attribute_not_type_attr);
+        ProhibitAttributes(attrs, diag::err_attribute_not_type_attr);
 
         DS.takeAttributesFrom(attrs);
       }
@@ -2905,7 +2903,7 @@
 
     case tok::l_square:
     case tok::kw_alignas:
-      if (!getLangOpts().CPlusPlus11 || !isCXX11AttributeSpecifier())
+      if (!standardAttributesAllowed() || !isAttributeSpecifier())
         goto DoneWithDeclSpec;
 
       ProhibitAttributes(attrs);
@@ -2915,7 +2913,7 @@
       attrs.clear();
       attrs.Range = SourceRange();
 
-      ParseCXX11Attributes(attrs);
+      ParseAttributes(attrs);
       AttrsLastTime = true;
       continue;
 
@@ -3754,7 +3752,8 @@
 /// semicolon.
 ///
 ///       struct-declaration:
-///         specifier-qualifier-list struct-declarator-list
+/// [C2x]   attributes-specifier-seq[opt]
+///           specifier-qualifier-list struct-declarator-list
 /// [GNU]   __extension__ struct-declaration
 /// [GNU]   specifier-qualifier-list
 ///       struct-declarator-list:
@@ -3778,6 +3777,11 @@
     return ParseStructDeclaration(DS, FieldsCallback);
   }
 
+  // Parse leading attributes.
+  ParsedAttributesWithRange Attrs(AttrFactory);
+  MaybeParseAttributes(Attrs);
+  DS.takeAttributesFrom(Attrs);
+
   // Parse the common specifier-qualifiers-list piece.
   ParseSpecifierQualifierList(DS);
 
@@ -4004,7 +4008,7 @@
   // If attributes exist after tag, parse them.
   ParsedAttributesWithRange attrs(AttrFactory);
   MaybeParseGNUAttributes(attrs);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseAttributes(attrs);
   MaybeParseMicrosoftDeclSpecs(attrs);
 
   SourceLocation ScopedEnumKWLoc;
@@ -4023,7 +4027,7 @@
 
     // They are allowed afterwards, though.
     MaybeParseGNUAttributes(attrs);
-    MaybeParseCXX11Attributes(attrs);
+    MaybeParseAttributes(attrs);
     MaybeParseMicrosoftDeclSpecs(attrs);
   }
 
@@ -4209,6 +4213,10 @@
       PP.EnterToken(Tok);
       Tok.setKind(tok::semi);
     }
+
+    // Attributes are prohibited in this location in C2x (and forward
+    // declarations are prohibited in C++).
+    ProhibitAttributes(attrs);
   } else {
     TUK = Sema::TUK_Reference;
   }
@@ -4388,11 +4396,11 @@
     ParsedAttributesWithRange attrs(AttrFactory);
     MaybeParseGNUAttributes(attrs);
     ProhibitAttributes(attrs); // GNU-style attributes are prohibited.
-    if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+    if (standardAttributesAllowed() && isAttributeSpecifier()) {
       if (!getLangOpts().CPlusPlus1z)
         Diag(Tok.getLocation(), diag::warn_cxx14_compat_attribute)
             << 1 /*enumerator*/;
-      ParseCXX11Attributes(attrs);
+      ParseAttributes(attrs);
     }
 
     SourceLocation EqualLoc;
@@ -4868,7 +4876,7 @@
 
   // There may be attributes here, appertaining to the constructor name or type
   // we just stepped past.
-  SkipCXX11Attributes();
+  SkipAttributes();
 
   // Current class name must be followed by a left parenthesis.
   if (Tok.isNot(tok::l_paren)) {
@@ -4888,7 +4896,7 @@
   // A C++11 attribute here signals that we have a constructor, and is an
   // attribute on the first constructor parameter.
   if (getLangOpts().CPlusPlus11 &&
-      isCXX11AttributeSpecifier(/*Disambiguate*/ false,
+      isAttributeSpecifier(/*Disambiguate*/ false,
                                 /*OuterMightBeMessageSend*/ true)) {
     TPA.Revert();
     return true;
@@ -4941,7 +4949,7 @@
       // Skip past the right-paren and any following attributes to get to
       // the function body or trailing-return-type.
       ConsumeParen();
-      SkipCXX11Attributes();
+      SkipAttributes();
 
       if (DeductionGuide) {
         // C(X) -> ... is a deduction guide.
@@ -4997,10 +5005,10 @@
     DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed,
     bool IdentifierRequired,
     Optional<llvm::function_ref<void()>> CodeCompletionHandler) {
-  if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) &&
-      isCXX11AttributeSpecifier()) {
+  if (standardAttributesAllowed() && (AttrReqs & AR_CXX11AttributesParsed) &&
+      isAttributeSpecifier()) {
     ParsedAttributesWithRange attrs(AttrFactory);
-    ParseCXX11Attributes(attrs);
+    ParseAttributes(attrs);
     DS.takeAttributesFrom(attrs);
   }
 
@@ -5674,7 +5682,7 @@
 
   // Don't parse attributes unless we have parsed an unparenthesized name.
   if (D.hasName() && !D.getNumTypeObjects())
-    MaybeParseCXX11Attributes(D);
+    MaybeParseAttributes(D);
 
   while (1) {
     if (Tok.is(tok::l_paren)) {
@@ -5835,7 +5843,7 @@
              (getLangOpts().CPlusPlus && Tok.is(tok::ellipsis) &&
               NextToken().is(tok::r_paren)) || // C++ int(...)
              isDeclarationSpecifier() ||       // 'int(int)' is a function.
-             isCXX11AttributeSpecifier()) {    // 'int([[]]int)' is a function.
+             isAttributeSpecifier()) {         // 'int([[]]int)' is a function.
     // This handles C99 6.7.5.3p11: in "typedef int X; void foo(X)", X is
     // considered to be a type, not a K&R identifier-list.
     isGrouping = false;
@@ -5934,7 +5942,7 @@
   SmallVector<SourceRange, 2> DynamicExceptionRanges;
   ExprResult NoexceptExpr;
   CachedTokens *ExceptionSpecTokens = nullptr;
-  ParsedAttributes FnAttrs(AttrFactory);
+  ParsedAttributesWithRange FnAttrs(AttrFactory);
   TypeResult TrailingReturnType;
 
   /* LocalEndLoc is the end location for the local FunctionTypeLoc.
@@ -5955,6 +5963,11 @@
     RParenLoc = Tracker.getCloseLocation();
     LocalEndLoc = RParenLoc;
     EndLoc = RParenLoc;
+
+    // If there are attributes following the identifier list, parse them and 
+    // prohibit them.
+    MaybeParseAttributes(FnAttrs);
+    ProhibitAttributes(FnAttrs);
   } else {
     if (Tok.isNot(tok::r_paren))
       ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, 
@@ -6047,7 +6060,7 @@
 
       // Parse attribute-specifier-seq[opt]. Per DR 979 and DR 1297, this goes
       // after the exception-specification.
-      MaybeParseCXX11Attributes(FnAttrs);
+      MaybeParseAttributes(FnAttrs);
 
       // Parse trailing-return-type[opt].
       LocalEndLoc = EndLoc;
@@ -6060,6 +6073,8 @@
         TrailingReturnType = ParseTrailingReturnType(Range);
         EndLoc = Range.getEnd();
       }
+    } else if (standardAttributesAllowed()) {
+      MaybeParseAttributes(FnAttrs);
     }
   }
 
@@ -6248,7 +6263,7 @@
     DeclSpec DS(AttrFactory);
 
     // Parse any C++11 attributes.
-    MaybeParseCXX11Attributes(DS.getAttributes());
+    MaybeParseAttributes(DS.getAttributes());
 
     // Skip any Microsoft attributes before a param.
     MaybeParseMicrosoftAttributes(DS.getAttributes());
@@ -6405,7 +6420,7 @@
 /// [C++11] direct-declarator '[' constant-expression[opt] ']'
 ///                           attribute-specifier-seq[opt]
 void Parser::ParseBracketDeclarator(Declarator &D) {
-  if (CheckProhibitedCXX11Attribute())
+  if (CheckProhibitedAttribute())
     return;
 
   BalancedDelimiterTracker T(*this, tok::l_square);
@@ -6416,7 +6431,7 @@
   if (Tok.getKind() == tok::r_square) {
     T.consumeClose();
     ParsedAttributes attrs(AttrFactory);
-    MaybeParseCXX11Attributes(attrs);
+    MaybeParseAttributes(attrs);
 
     // Remember that we parsed the empty array type.
     D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false, nullptr,
@@ -6432,7 +6447,7 @@
 
     T.consumeClose();
     ParsedAttributes attrs(AttrFactory);
-    MaybeParseCXX11Attributes(attrs);
+    MaybeParseAttributes(attrs);
 
     // Remember that we parsed a array type, and remember its features.
     D.AddTypeInfo(DeclaratorChunk::getArray(0, false, false,
@@ -6509,7 +6524,7 @@
 
   T.consumeClose();
 
-  MaybeParseCXX11Attributes(DS.getAttributes());
+  MaybeParseAttributes(DS.getAttributes());
 
   // Remember that we parsed a array type, and remember its features.
   D.AddTypeInfo(DeclaratorChunk::getArray(DS.getTypeQualifiers(),
Index: lib/Lex/Lexer.cpp
===================================================================
--- lib/Lex/Lexer.cpp
+++ lib/Lex/Lexer.cpp
@@ -3612,7 +3612,7 @@
     if (LangOpts.Digraphs && Char == '>') {
       Kind = tok::r_square; // ':>' -> ']'
       CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
-    } else if (LangOpts.CPlusPlus && Char == ':') {
+    } else if ((LangOpts.CPlusPlus || LangOpts.C2x) && Char == ':') {
       Kind = tok::coloncolon;
       CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
     } else {
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1726,6 +1726,7 @@
   Opts.LineComment = Std.hasLineComments();
   Opts.C99 = Std.isC99();
   Opts.C11 = Std.isC11();
+  Opts.C2x = Std.isC2x();
   Opts.CPlusPlus = Std.isCPlusPlus();
   Opts.CPlusPlus11 = Std.isCPlusPlus11();
   Opts.CPlusPlus14 = Std.isCPlusPlus14();
@@ -2134,6 +2135,7 @@
     && Opts.OpenCLVersion >= 200);
   Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional);
   Opts.CoroutinesTS = Args.hasArg(OPT_fcoroutines_ts);
+  Opts.CAttributes = Args.hasArg(OPT_fcattributes);
   Opts.ModulesTS = Args.hasArg(OPT_fmodules_ts);
   Opts.Modules = Args.hasArg(OPT_fmodules) || Opts.ModulesTS;
   Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse);
Index: include/clang/Sema/AttributeList.h
===================================================================
--- include/clang/Sema/AttributeList.h
+++ include/clang/Sema/AttributeList.h
@@ -100,6 +100,8 @@
     AS_GNU,
     /// [[...]]
     AS_CXX11,
+    /// [[...]]
+    AS_C2x,
     /// __declspec(...)
     AS_Declspec,
     /// [uuid("...")] class Foo
@@ -378,6 +380,9 @@
   bool isCXX11Attribute() const {
     return SyntaxUsed == AS_CXX11 || isAlignasAttribute();
   }
+  bool isC2xAttribute() const {
+    return SyntaxUsed == AS_C2x;
+  }
   bool isKeywordAttribute() const {
     return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword;
   }
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -2164,50 +2164,58 @@
 private:
   void ParseBlockId(SourceLocation CaretLoc);
 
-  // Check for the start of a C++11 attribute-specifier-seq in a context where
-  // an attribute is not allowed.
-  bool CheckProhibitedCXX11Attribute() {
+  /// Are C++11 or C2x attributes enabled?
+  bool standardAttributesAllowed() const {
+    const LangOptions &LO = getLangOpts();
+    return LO.CPlusPlus11 || (LO.C2x && LO.CAttributes);
+  }
+
+  // Check for the start of an attribute-specifier-seq in a context where an
+  // attribute is not allowed.
+  bool CheckProhibitedAttribute() {
     assert(Tok.is(tok::l_square));
-    if (!getLangOpts().CPlusPlus11 || NextToken().isNot(tok::l_square))
+    if (!standardAttributesAllowed() || NextToken().isNot(tok::l_square))
       return false;
-    return DiagnoseProhibitedCXX11Attribute();
+    return DiagnoseProhibitedAttribute();
   }
-  bool DiagnoseProhibitedCXX11Attribute();
-  void CheckMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
-                                    SourceLocation CorrectLocation) {
-    if (!getLangOpts().CPlusPlus11)
+
+  bool DiagnoseProhibitedAttribute();
+  void CheckMisplacedAttribute(ParsedAttributesWithRange &Attrs,
+                               SourceLocation CorrectLocation) {
+    if (!standardAttributesAllowed())
       return;
     if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
         Tok.isNot(tok::kw_alignas))
       return;
-    DiagnoseMisplacedCXX11Attribute(Attrs, CorrectLocation);
+    DiagnoseMisplacedAttribute(Attrs, CorrectLocation);
   }
-  void DiagnoseMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
-                                       SourceLocation CorrectLocation);
+  void DiagnoseMisplacedAttribute(ParsedAttributesWithRange &Attrs,
+                                  SourceLocation CorrectLocation);
 
   void stripTypeAttributesOffDeclSpec(ParsedAttributesWithRange &Attrs,
                                       DeclSpec &DS, Sema::TagUseKind TUK);
 
   void ProhibitAttributes(ParsedAttributesWithRange &attrs) {
-    if (!attrs.Range.isValid()) return;
+    if (attrs.Range.isInvalid())
+      return;
     DiagnoseProhibitedAttributes(attrs);
     attrs.clear();
   }
   void DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs);
 
-  // Forbid C++11 attributes that appear on certain syntactic 
-  // locations which standard permits but we don't supported yet, 
-  // for example, attributes appertain to decl specifiers.
-  void ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs,
-                               unsigned DiagID);
+  // Forbid C++11 and C2x attributes that appear on certain syntactic locations
+  // which standard permits but we don't supported yet, for example, attributes
+  // appertain to decl specifiers.
+  void ProhibitAttributes(ParsedAttributesWithRange &Attrs, unsigned DiagID);
 
-  /// \brief Skip C++11 attributes and return the end location of the last one.
+  /// \brief Skip C++11 and C2x attributes and return the end location of the
+  /// last one.
   /// \returns SourceLocation() if there are no attributes.
-  SourceLocation SkipCXX11Attributes();
+  SourceLocation SkipAttributes();
 
-  /// \brief Diagnose and skip C++11 attributes that appear in syntactic
+  /// \brief Diagnose and skip C++11 and C2x attributes that appear in syntactic
   /// locations where attributes are not allowed.
-  void DiagnoseAndSkipCXX11Attributes();
+  void DiagnoseAndSkipAttributes();
 
   /// \brief Parses syntax-generic attribute arguments for attributes which are
   /// known to the implementation, and adds them to the given ParsedAttributes
@@ -2254,43 +2262,41 @@
                           IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
                           AttributeList::Syntax Syntax);
 
-  void MaybeParseCXX11Attributes(Declarator &D) {
-    if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+  void MaybeParseAttributes(Declarator &D) {
+    if (standardAttributesAllowed() && isAttributeSpecifier()) {
       ParsedAttributesWithRange attrs(AttrFactory);
       SourceLocation endLoc;
-      ParseCXX11Attributes(attrs, &endLoc);
+      ParseAttributes(attrs, &endLoc);
       D.takeAttributes(attrs, endLoc);
     }
   }
-  void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
-                                 SourceLocation *endLoc = nullptr) {
-    if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
+  void MaybeParseAttributes(ParsedAttributes &attrs,
+                            SourceLocation *endLoc = nullptr) {
+    if (standardAttributesAllowed() && isAttributeSpecifier()) {
       ParsedAttributesWithRange attrsWithRange(AttrFactory);
-      ParseCXX11Attributes(attrsWithRange, endLoc);
+      ParseAttributes(attrsWithRange, endLoc);
       attrs.takeAllFrom(attrsWithRange);
     }
   }
-  void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
-                                 SourceLocation *endLoc = nullptr,
-                                 bool OuterMightBeMessageSend = false) {
-    if (getLangOpts().CPlusPlus11 &&
-        isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
-      ParseCXX11Attributes(attrs, endLoc);
+  void MaybeParseAttributes(ParsedAttributesWithRange &attrs,
+                            SourceLocation *endLoc = nullptr,
+                            bool OuterMightBeMessageSend = false) {
+    if (standardAttributesAllowed() &&
+        isAttributeSpecifier(false, OuterMightBeMessageSend))
+      ParseAttributes(attrs, endLoc);
   }
 
-  void ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
-                                    SourceLocation *EndLoc = nullptr);
-  void ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
-                            SourceLocation *EndLoc = nullptr);
-  /// \brief Parses a C++-style attribute argument list. Returns true if this
-  /// results in adding an attribute to the ParsedAttributes list.
-  bool ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
-                               SourceLocation AttrNameLoc,
-                               ParsedAttributes &Attrs, SourceLocation *EndLoc,
-                               IdentifierInfo *ScopeName,
-                               SourceLocation ScopeLoc);
+  void ParseAttributeSpecifier(ParsedAttributes &attrs,
+                               SourceLocation *EndLoc = nullptr);
+  void ParseAttributes(ParsedAttributesWithRange &attrs,
+                       SourceLocation *EndLoc = nullptr);
+  /// \brief Parses a C++11 (or C2x)-style attribute argument list. Returns true
+  /// if this results in adding an attribute to the ParsedAttributes list.
+  bool ParseAttributeArgs(IdentifierInfo *AttrName, SourceLocation AttrNameLoc,
+                          ParsedAttributes &Attrs, SourceLocation *EndLoc,
+                          IdentifierInfo *ScopeName, SourceLocation ScopeLoc);
 
-  IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc);
+  IdentifierInfo *TryParseAttributeIdentifier(SourceLocation &Loc);
 
   void MaybeParseMicrosoftAttributes(ParsedAttributes &attrs,
                                      SourceLocation *endLoc = nullptr) {
@@ -2489,9 +2495,8 @@
     /// is ill-formed by C++11 [dcl.attr.grammar]p6.
     CAK_InvalidAttributeSpecifier
   };
-  CXX11AttributeKind
-  isCXX11AttributeSpecifier(bool Disambiguate = false,
-                            bool OuterMightBeMessageSend = false);
+  CXX11AttributeKind isAttributeSpecifier(bool Disambiguate = false,
+                                          bool OuterMightBeMessageSend = false);
 
   void DiagnoseUnexpectedNamespace(NamedDecl *Context);
 
Index: include/clang/Frontend/LangStandards.def
===================================================================
--- include/clang/Frontend/LangStandards.def
+++ include/clang/Frontend/LangStandards.def
@@ -77,6 +77,11 @@
              LineComment | C99 | C11 | Digraphs | GNUMode | HexFloat)
 LANGSTANDARD_ALIAS_DEPR(gnu11, "gnu1x")
 
+// C2X modes
+LANGSTANDARD(c2x, "c2x",
+             C, "Working draft for ISO C 202x",
+             LineComment | C99 | C11 | C2x | Digraphs | HexFloat)
+
 // C++ modes
 LANGSTANDARD(cxx98, "c++98",
              CXX, "ISO C++ 1998 with amendments",
Index: include/clang/Frontend/LangStandard.h
===================================================================
--- include/clang/Frontend/LangStandard.h
+++ include/clang/Frontend/LangStandard.h
@@ -22,16 +22,17 @@
   LineComment = (1 << 0),
   C99 = (1 << 1),
   C11 = (1 << 2),
-  CPlusPlus = (1 << 3),
-  CPlusPlus11 = (1 << 4),
-  CPlusPlus14 = (1 << 5),
-  CPlusPlus1z = (1 << 6),
-  CPlusPlus2a = (1 << 7),
-  Digraphs = (1 << 8),
-  GNUMode = (1 << 9),
-  HexFloat = (1 << 10),
-  ImplicitInt = (1 << 11),
-  OpenCL = (1 << 12)
+  C2x = (1 << 3),
+  CPlusPlus = (1 << 4),
+  CPlusPlus11 = (1 << 5),
+  CPlusPlus14 = (1 << 6),
+  CPlusPlus1z = (1 << 7),
+  CPlusPlus2a = (1 << 8),
+  Digraphs = (1 << 9),
+  GNUMode = (1 << 10),
+  HexFloat = (1 << 11),
+  ImplicitInt = (1 << 12),
+  OpenCL = (1 << 13)
 };
 
 }
@@ -70,6 +71,9 @@
   /// isC11 - Language is a superset of C11.
   bool isC11() const { return Flags & frontend::C11; }
 
+  /// isC2x - Language is a superset of C2x.
+  bool isC2x() const { return Flags & frontend::C2x; }
+
   /// isCPlusPlus - Language is a C++ variant.
   bool isCPlusPlus() const { return Flags & frontend::CPlusPlus; }
 
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -604,6 +604,11 @@
 def fast : Flag<["-"], "fast">, Group<f_Group>;
 def fasynchronous_unwind_tables : Flag<["-"], "fasynchronous-unwind-tables">, Group<f_Group>;
 
+def fcattributes : Flag<["-"], "fc-attributes">, Group<f_Group>,
+  Flags<[DriverOption, CC1Option]>, HelpText<"Enable '[[]]' attributes in C">;
+def fno_cattributes : Flag<["-"], "fno-c-attributes">, Group<f_Group>,
+  Flags<[DriverOption]>, HelpText<"Disable '[[]]' attributes in C">;
+
 def fautolink : Flag <["-"], "fautolink">, Group<f_Group>;
 def fno_autolink : Flag <["-"], "fno-autolink">, Group<f_Group>,
   Flags<[DriverOption, CC1Option]>,
Index: include/clang/Basic/LangOptions.def
===================================================================
--- include/clang/Basic/LangOptions.def
+++ include/clang/Basic/LangOptions.def
@@ -82,6 +82,7 @@
 // FIXME: A lot of the BENIGN_ options should be COMPATIBLE_ instead.
 LANGOPT(C99               , 1, 0, "C99")
 LANGOPT(C11               , 1, 0, "C11")
+LANGOPT(C2x               , 1, 0, "C2x")
 LANGOPT(MSVCCompat        , 1, 0, "Microsoft Visual C++ full compatibility mode")
 LANGOPT(MicrosoftExt      , 1, 0, "Microsoft C++ extensions")
 LANGOPT(AsmBlocks         , 1, 0, "Microsoft inline asm blocks")
@@ -137,6 +138,9 @@
 LANGOPT(CoroutinesTS      , 1, 0, "C++ coroutines TS")
 LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
 
+// TODO: If attributes are accepted by WG14, default on in C2x or remove flag.
+LANGOPT(CAttributes       , 1, 0, "'[[]]' attributes extension to C")
+
 BENIGN_LANGOPT(ThreadsafeStatics , 1, 1, "thread-safe static initializers")
 LANGOPT(POSIXThreads      , 1, 0, "POSIX thread support")
 LANGOPT(Blocks            , 1, 0, "blocks extension to C")
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -567,6 +567,9 @@
 def warn_cxx98_compat_attribute : Warning<
   "C++11 attribute syntax is incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore;
+def warn_c11_compat_attribute : Warning<
+  "attribute syntax is incompatible with standards before C2x">,
+  InGroup<C11Compat>, DefaultIgnore;
 def err_cxx11_attribute_forbids_arguments : Error<
   "attribute %0 cannot have an argument list">;
 def err_attribute_requires_arguments : Error<
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -173,6 +173,8 @@
 def CXXPre2aCompatPedantic : DiagGroup<"c++98-c++11-c++14-c++17-compat-pedantic",
                                        [CXXPre2aCompat]>;
 
+def C11Compat : DiagGroup<"c11-compat">;
+
 def CXX98CompatBindToTemporaryCopy :
   DiagGroup<"c++98-compat-bind-to-temporary-copy">;
 def CXX98CompatLocalTypeTemplateArgs :
Index: include/clang/Basic/Attributes.h
===================================================================
--- include/clang/Basic/Attributes.h
+++ include/clang/Basic/Attributes.h
@@ -26,6 +26,8 @@
   Microsoft,
   // Is the identifier known as a C++-style attribute?
   CXX,
+  // Is the identifier known as a C-style attribute?
+  C2x,
   // Is the identifier known as a pragma attribute?
   Pragma
 };
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -210,6 +210,10 @@
   string Namespace = namespace;
   int Version = version;
 }
+class C2x<string namespace, string name> : Spelling<name, "C2x"> {
+  string Namespace = namespace;
+}
+
 class Keyword<string name> : Spelling<name, "Keyword">;
 class Pragma<string namespace, string name> : Spelling<name, "Pragma"> {
   string Namespace = namespace;
@@ -958,7 +962,7 @@
 
 def Deprecated : InheritableAttr {
   let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
-                   CXX11<"","deprecated", 201309>];
+                   CXX11<"","deprecated", 201309>, C2x<"", "deprecated">];
   let Args = [StringArgument<"Message", 1>,
               // An optional string argument that enables us to provide a
               // Fix-It.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to