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 : Spelling { + string Namespace = namespace; +} + class Keyword : Spelling; class Pragma : Spelling { 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. 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? + C, // Is the identifier known as a pragma attribute? Pragma }; Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -137,6 +137,8 @@ LANGOPT(CoroutinesTS , 1, 0, "C++ coroutines TS") LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") +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/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -604,6 +604,11 @@ def fast : Flag<["-"], "fast">, Group; def fasynchronous_unwind_tables : Flag<["-"], "fasynchronous-unwind-tables">, Group; +def fc_attributes : Flag<["-"], "fc-attributes">, Group, + Flags<[DriverOption, CC1Option]>, HelpText<"Enable '[[]]' attributes in C">; +def fno_c_attributes : Flag<["-"], "fno-c-attributes">, Group, + Flags<[DriverOption]>, HelpText<"Disable '[[]]' attributes in C">; + def fautolink : Flag <["-"], "fautolink">, Group; def fno_autolink : Flag <["-"], "fno-autolink">, Group, Flags<[DriverOption, CC1Option]>, Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2164,18 +2164,25 @@ 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. + /// Are C++11 or C2x attributes enabled? + bool standardAttributesAllowed() const { + const LangOptions &LO = getLangOpts(); + return LO.CPlusPlus11 || LO.CAttributes; + } + + // Check for the start of an attribute-specifier-seq in a context where an + // attribute is not allowed. bool CheckProhibitedCXX11Attribute() { 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(); } + bool DiagnoseProhibitedCXX11Attribute(); void CheckMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs, SourceLocation CorrectLocation) { - if (!getLangOpts().CPlusPlus11) + if (!standardAttributesAllowed()) return; if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) && Tok.isNot(tok::kw_alignas)) @@ -2195,17 +2202,18 @@ } 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. + // 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 ProhibitCXX11Attributes(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(); - /// \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(); @@ -2255,7 +2263,7 @@ AttributeList::Syntax Syntax); void MaybeParseCXX11Attributes(Declarator &D) { - if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) { + if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); SourceLocation endLoc; ParseCXX11Attributes(attrs, &endLoc); @@ -2264,7 +2272,7 @@ } void MaybeParseCXX11Attributes(ParsedAttributes &attrs, SourceLocation *endLoc = nullptr) { - if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) { + if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrsWithRange(AttrFactory); ParseCXX11Attributes(attrsWithRange, endLoc); attrs.takeAllFrom(attrsWithRange); @@ -2273,8 +2281,8 @@ void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs, SourceLocation *endLoc = nullptr, bool OuterMightBeMessageSend = false) { - if (getLangOpts().CPlusPlus11 && - isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) + if (standardAttributesAllowed() && + isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) ParseCXX11Attributes(attrs, endLoc); } @@ -2282,8 +2290,8 @@ 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. + /// \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 ParseCXX11AttributeArgs(IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, 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: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2134,6 +2134,7 @@ && Opts.OpenCLVersion >= 200); Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional); Opts.CoroutinesTS = Args.hasArg(OPT_fcoroutines_ts); + Opts.CAttributes = Args.hasArg(OPT_fc_attributes); Opts.ModulesTS = Args.hasArg(OPT_fmodules_ts); Opts.Modules = Args.hasArg(OPT_fmodules) || Opts.ModulesTS; Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse); Index: lib/Lex/Lexer.cpp =================================================================== --- lib/Lex/Lexer.cpp +++ lib/Lex/Lexer.cpp @@ -3613,7 +3613,7 @@ if (LangOpts.Digraphs && Char == '>') { Kind = tok::r_square; // ':>' -> ']' CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); - } else if (LangOpts.CPlusPlus && Char == ':') { + } else if ((LangOpts.CPlusPlus || LangOpts.CAttributes) && Char == ':') { Kind = tok::coloncolon; CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); } else { Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -2905,7 +2905,7 @@ case tok::l_square: case tok::kw_alignas: - if (!getLangOpts().CPlusPlus11 || !isCXX11AttributeSpecifier()) + if (!standardAttributesAllowed() || !isCXX11AttributeSpecifier()) goto DoneWithDeclSpec; ProhibitAttributes(attrs); @@ -3754,7 +3754,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 +3779,11 @@ return ParseStructDeclaration(DS, FieldsCallback); } + // Parse leading attributes. + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + DS.takeAttributesFrom(Attrs); + // Parse the common specifier-qualifiers-list piece. ParseSpecifierQualifierList(DS); @@ -4209,6 +4215,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,7 +4398,7 @@ ParsedAttributesWithRange attrs(AttrFactory); MaybeParseGNUAttributes(attrs); ProhibitAttributes(attrs); // GNU-style attributes are prohibited. - if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) { + if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) { if (!getLangOpts().CPlusPlus1z) Diag(Tok.getLocation(), diag::warn_cxx14_compat_attribute) << 1 /*enumerator*/; @@ -4997,7 +5007,7 @@ DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed, bool IdentifierRequired, Optional> CodeCompletionHandler) { - if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) && + if (standardAttributesAllowed() && (AttrReqs & AR_CXX11AttributesParsed) && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); ParseCXX11Attributes(attrs); @@ -5934,7 +5944,7 @@ SmallVector 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 +5965,11 @@ RParenLoc = Tracker.getCloseLocation(); LocalEndLoc = RParenLoc; EndLoc = RParenLoc; + + // If there are attributes following the identifier list, parse them and + // prohibit them. + MaybeParseCXX11Attributes(FnAttrs); + ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, @@ -6060,6 +6075,8 @@ TrailingReturnType = ParseTrailingReturnType(Range); EndLoc = Range.getEnd(); } + } else if (standardAttributesAllowed()) { + MaybeParseCXX11Attributes(FnAttrs); } } Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -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: @@ -3852,11 +3852,15 @@ 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::C, + ScopeName, AttrName, getTargetInfo(), getLangOpts())) { // Eat the left paren, then skip to the ending right paren. ConsumeParen(); SkipUntil(tok::r_paren); @@ -3867,7 +3871,7 @@ // 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; } @@ -3876,11 +3880,11 @@ 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 +3910,7 @@ return true; } -/// ParseCXX11AttributeSpecifier - Parse a C++11 attribute-specifier. +/// ParseCXX11Attributespecifier - Parse a C++11 or C2x attribute-specifier. /// /// [C++11] attribute-specifier: /// '[' '[' attribute-list ']' ']' @@ -3932,14 +3936,15 @@ /// identifier void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, SourceLocation *endLoc) { - if (Tok.is(tok::kw_alignas)) { + 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); @@ -3948,7 +3953,7 @@ 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); @@ -4015,10 +4020,11 @@ 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) @@ -4033,13 +4039,13 @@ SkipUntil(tok::r_square); } -/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq. +/// ParseCXX11Attributes - 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); + assert(standardAttributesAllowed()); SourceLocation StartLoc = Tok.getLocation(), Loc; if (!endLoc) Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -589,7 +589,7 @@ Parser::CXX11AttributeKind Parser::isCXX11AttributeSpecifier(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)) 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: 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 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 enum alignas(O) alignas(P) Y::E : char { e }; 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 -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{{[^ ]*}} "" "" + +enum [[deprecated("Frobble")]] Test2 { + Test3 [[deprecated]] +}; +// CHECK: EnumDecl{{.*}}Test2 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "Frobble" "" +// CHECK-NEXT: EnumConstantDecl{{.*}}Test3 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" + +struct [[deprecated]] Test4 { + [[deprecated("Frobble")]] int Test5, Test6; + int Test7 [[deprecated]] : 12; +}; +// CHECK: RecordDecl{{.*}}Test4 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" +// CHECK-NEXT: FieldDecl{{.*}}Test5 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "Frobble" "" +// CHECK-NEXT: FieldDecl{{.*}}Test6 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "Frobble" "" +// CHECK-NEXT: FieldDecl{{.*}}Test7 +// CHECK-NEXT: IntegerLiteral{{.*}}'int' 12 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" + +struct [[deprecated]] Test8; +// CHECK: RecordDecl{{.*}}Test8 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" + +[[deprecated]] void Test9(int Test10 [[deprecated]]); +// CHECK: FunctionDecl{{.*}}Test9 +// CHECK-NEXT: ParmVarDecl{{.*}}Test10 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" + +void Test11 [[deprecated]](void); +// CHECK: FunctionDecl{{.*}}Test11 +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" + +void Test12(void) [[deprecated]] {} +// CHECK: FunctionDecl{{.*}}Test12 +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} "" "" Index: test/Parser/c2x-attributes.c =================================================================== --- test/Parser/c2x-attributes.c +++ test/Parser/c2x-attributes.c @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -fsyntax-only -fc-attributes -verify %s + +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}} + +// Ensure that asm statements properly handle double colons. +void test_asm(void) { + asm("ret" :::); + asm("foo" :: "r" (xx)); // expected-error {{use of undeclared identifier 'xx'}} +} Index: test/Parser/cxx0x-attributes.cpp =================================================================== --- test/Parser/cxx0x-attributes.cpp +++ test/Parser/cxx0x-attributes.cpp @@ -175,15 +175,15 @@ template <> struct [[]] Template; 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/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 -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: 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.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.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 Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector Declspec, Microsoft, GNU, Pragma; - std::map> CXX; + std::map> 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(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(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> &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(Name)\n"; + GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first); + OS << "}"; + } + OS << "\n}\n"; + }; + fn("CXX11", "CXX", CXX); + fn("C2x", "C", C2x); OS << "}\n"; } @@ -2809,10 +2820,11 @@ << StringSwitch(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 Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector GNU, Declspec, Microsoft, CXX11, - Keywords, Pragma; + Keywords, Pragma, C2x; std::set 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(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,7 +3774,7 @@ // 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"; @@ -3761,6 +3781,8 @@ OS << "\",\""; if (SupportedSpellings & CXX11) OS << "X"; OS << "\",\""; + if (SupportedSpellings & C2x) OS << "X"; + OS << "\",\""; if (SupportedSpellings & Declspec) OS << "X"; OS << "\",\""; if (SupportedSpellings & Keyword) OS << "X";