diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index afa8a893e921aa..81a8d213419381 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -150,6 +150,21 @@ class AttributeCommonInfo { bool isAlignasAttribute() const { // FIXME: Use a better mechanism to determine this. + // We use this in `isCXX11Attribute` below, so it _should_ only return + // true for the `alignas` spelling, but it currently also returns true + // for the `_Alignas` spelling, which only exists in C11. Distinguishing + // between the two is important because they behave differently: + // - `alignas` may only appear in the attribute-specifier-seq before + // the decl-specifier-seq and is therefore associated with the + // declaration. + // - `_Alignas` may appear anywhere within the declaration-specifiers + // and is therefore associated with the `DeclSpec`. + // It's not clear how best to fix this: + // - We have the necessary information in the form of the `SpellingIndex`, + // but we would need to compare against AlignedAttr::Keyword_alignas, + // and we can't depend on clang/AST/Attr.h here. + // - We could test `getAttrName()->getName() == "alignas"`, but this is + // inefficient. return getParsedKind() == AT_Aligned && isKeywordAttribute(); } diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index c140a33694f379..39dee7e683ff9f 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -163,6 +163,8 @@ def err_opencl_unknown_type_specifier : Error< def warn_unknown_attribute_ignored : Warning< "unknown attribute %0 ignored">, InGroup; +def warn_attribute_ignored : Warning<"%0 attribute ignored">, + InGroup; def err_use_of_tag_name_without_tag : Error< "must use '%1' tag to refer to type %0%select{| in this scope}2">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0e6573241b55e5..f2190613d740e2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3354,8 +3354,6 @@ def warn_redeclaration_without_import_attribute : Warning< def warn_dllimport_dropped_from_inline_function : Warning< "%q0 redeclared inline; %1 attribute ignored">, InGroup; -def warn_attribute_ignored : Warning<"%0 attribute ignored">, - InGroup; def warn_nothrow_attribute_ignored : Warning<"'nothrow' attribute conflicts with" " exception specification; attribute ignored">, InGroup; @@ -3392,8 +3390,11 @@ def note_attribute_has_no_effect_on_compile_time_if_here : Note< "annotating the 'if %select{constexpr|consteval}0' statement here">; def err_decl_attribute_invalid_on_stmt : Error< "%0 attribute cannot be applied to a statement">; -def err_stmt_attribute_invalid_on_decl : Error< +def err_attribute_invalid_on_decl : Error< "%0 attribute cannot be applied to a declaration">; +def warn_type_attribute_deprecated_on_decl : Warning< + "applying attribute %0 to a declaration is deprecated; apply it to the type instead">, + InGroup; def warn_declspec_attribute_ignored : Warning< "attribute %0 is ignored, place it after " "\"%select{class|struct|interface|union|enum}1\" to apply attribute to " diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 5bf7b276c328d7..0eb6f7104a55bc 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2075,7 +2075,8 @@ class Parser : public CodeCompletionHandler { SourceLocation *TrailingElseLoc = nullptr); StmtResult ParseStatementOrDeclarationAfterAttributes( StmtVector &Stmts, ParsedStmtContext StmtCtx, - SourceLocation *TrailingElseLoc, ParsedAttributes &Attrs); + SourceLocation *TrailingElseLoc, ParsedAttributes &DeclAttrs, + ParsedAttributes &DeclSpecAttrs); StmtResult ParseExprStatement(ParsedStmtContext StmtCtx); StmtResult ParseLabeledStatement(ParsedAttributes &Attrs, ParsedStmtContext StmtCtx); @@ -2324,15 +2325,18 @@ class Parser : public CodeCompletionHandler { DeclGroupPtrTy ParseDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, - ParsedAttributes &Attrs, + ParsedAttributes &DeclAttrs, + ParsedAttributes &DeclSpecAttrs, SourceLocation *DeclSpecStart = nullptr); DeclGroupPtrTy ParseSimpleDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, - ParsedAttributes &Attrs, bool RequireSemi, + ParsedAttributes &DeclAttrs, + ParsedAttributes &DeclSpecAttrs, bool RequireSemi, ForRangeInit *FRI = nullptr, SourceLocation *DeclSpecStart = nullptr); bool MightBeDeclarator(DeclaratorContext Context); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &Attrs, SourceLocation *DeclEnd = nullptr, ForRangeInit *FRI = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, diff --git a/clang/include/clang/Parse/RAIIObjectsForParser.h b/clang/include/clang/Parse/RAIIObjectsForParser.h index 8e6e03685c507d..5ae609e6007345 100644 --- a/clang/include/clang/Parse/RAIIObjectsForParser.h +++ b/clang/include/clang/Parse/RAIIObjectsForParser.h @@ -201,9 +201,11 @@ namespace clang { ParsingDeclRAIIObject ParsingRAII; public: - ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, DeclaratorContext C) - : Declarator(DS, C), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { - } + ParsingDeclarator(Parser &P, const ParsingDeclSpec &DS, + const ParsedAttributes &DeclarationAttrs, + DeclaratorContext C) + : Declarator(DS, DeclarationAttrs, C), + ParsingRAII(P, &DS.getDelayedDiagnosticPool()) {} const ParsingDeclSpec &getDeclSpec() const { return static_cast(Declarator::getDeclSpec()); @@ -228,9 +230,10 @@ namespace clang { ParsingDeclRAIIObject ParsingRAII; public: - ParsingFieldDeclarator(Parser &P, const ParsingDeclSpec &DS) - : FieldDeclarator(DS), ParsingRAII(P, &DS.getDelayedDiagnosticPool()) { - } + ParsingFieldDeclarator(Parser &P, const ParsingDeclSpec &DS, + const ParsedAttributes &DeclarationAttrs) + : FieldDeclarator(DS, DeclarationAttrs), + ParsingRAII(P, &DS.getDelayedDiagnosticPool()) {} const ParsingDeclSpec &getDeclSpec() const { return static_cast(D.getDeclSpec()); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index dc1fbf098a8acd..21d4a53d8e22b5 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1852,9 +1852,13 @@ class Declarator { /// Indicates whether this declarator has an initializer. unsigned HasInitializer : 1; - /// Attrs - Attributes. + /// Attributes attached to the declarator. ParsedAttributes Attrs; + /// Attributes attached to the declaration. See also documentation for the + /// corresponding constructor parameter. + const ParsedAttributesView &DeclarationAttrs; + /// The asm label, if specified. Expr *AsmLabel; @@ -1893,16 +1897,40 @@ class Declarator { friend struct DeclaratorChunk; public: - Declarator(const DeclSpec &ds, DeclaratorContext C) - : DS(ds), Range(ds.getSourceRange()), Context(C), + /// `DS` and `DeclarationAttrs` must outlive the `Declarator`. In particular, + /// take care not to pass temporary objects for these parameters. + /// + /// `DeclarationAttrs` contains [[]] attributes from the + /// attribute-specifier-seq at the beginning of a declaration, which appertain + /// to the declared entity itself. Attributes with other syntax (e.g. GNU) + /// should not be placed in this attribute list; if they occur at the + /// beginning of a declaration, they apply to the `DeclSpec` and should be + /// attached to that instead. + /// + /// Here is an example of an attribute associated with a declaration: + /// + /// [[deprecated]] int x, y; + /// + /// This attribute appertains to all of the entities declared in the + /// declaration, i.e. `x` and `y` in this case. + Declarator(const DeclSpec &DS, const ParsedAttributesView &DeclarationAttrs, + DeclaratorContext C) + : DS(DS), Range(DS.getSourceRange()), Context(C), InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), GroupingParens(false), FunctionDefinition(static_cast( FunctionDefinitionKind::Declaration)), Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), - HasInitializer(false), Attrs(ds.getAttributePool().getFactory()), - AsmLabel(nullptr), TrailingRequiresClause(nullptr), - InventedTemplateParameterList(nullptr) {} + HasInitializer(false), Attrs(DS.getAttributePool().getFactory()), + DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr), + TrailingRequiresClause(nullptr), + InventedTemplateParameterList(nullptr) { + assert(llvm::all_of(DeclarationAttrs, + [](const ParsedAttr &AL) { + return AL.isStandardAttributeSyntax(); + }) && + "DeclarationAttrs may only contain [[]] attributes"); + } ~Declarator() { clear(); @@ -2531,9 +2559,14 @@ class Declarator { const ParsedAttributes &getAttributes() const { return Attrs; } ParsedAttributes &getAttributes() { return Attrs; } + const ParsedAttributesView &getDeclarationAttributes() const { + return DeclarationAttrs; + } + /// hasAttributes - do we contain any attributes? bool hasAttributes() const { - if (!getAttributes().empty() || getDeclSpec().hasAttributes()) + if (!getAttributes().empty() || !getDeclarationAttributes().empty() || + getDeclSpec().hasAttributes()) return true; for (unsigned i = 0, e = getNumTypeObjects(); i != e; ++i) if (!getTypeObject(i).getAttrs().empty()) @@ -2615,8 +2648,10 @@ class Declarator { struct FieldDeclarator { Declarator D; Expr *BitfieldSize; - explicit FieldDeclarator(const DeclSpec &DS) - : D(DS, DeclaratorContext::Member), BitfieldSize(nullptr) {} + explicit FieldDeclarator(const DeclSpec &DS, + const ParsedAttributes &DeclarationAttrs) + : D(DS, DeclarationAttrs, DeclaratorContext::Member), + BitfieldSize(nullptr) {} }; /// Represents a C++11 virt-specifier-seq. diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 554d73b8901d7b..2fe5d7c5c71551 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -651,6 +651,18 @@ class ParsedAttr final bool isKnownToGCC() const; bool isSupportedByPragmaAttribute() const; + /// Returns whether a [[]] attribute, if specified ahead of a declaration, + /// should be applied to the decl-specifier-seq instead (i.e. whether it + /// "slides" to the decl-specifier-seq). + /// + /// By the standard, attributes specified before the declaration always + /// appertain to the declaration, but historically we have allowed some of + /// these attributes to slide to the decl-specifier-seq, so we need to keep + /// supporting this behavior. + /// + /// This may only be called if isStandardAttributeSyntax() returns true. + bool slidesFromDeclToDeclSpecLegacyBehavior() const; + /// If the parsed attribute has a semantic equivalent, and it would /// have a semantic Spelling enumeration (due to having semantically-distinct /// spelling variations), return the value of that semantic spelling. If the @@ -901,6 +913,12 @@ class ParsedAttributesView { public: SourceRange Range; + + static const ParsedAttributesView &none() { + static const ParsedAttributesView Attrs; + return Attrs; + } + bool empty() const { return AttrList.empty(); } SizeType size() const { return AttrList.size(); } ParsedAttr &operator[](SizeType pos) { return *AttrList[pos]; } @@ -1103,6 +1121,11 @@ class ParsedAttributes : public ParsedAttributesView { mutable AttributePool pool; }; +/// Consumes the attributes from `First` and `Second` and concatenates them into +/// `Result`. Sets `Result.Range` to the combined range of `First` and `Second`. +void takeAndConcatenateAttrs(ParsedAttributes &First, ParsedAttributes &Second, + ParsedAttributes &Result); + /// These constants match the enumerated choices of /// err_attribute_argument_n_type and err_attribute_argument_type. enum AttributeArgumentNType { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index aeb0a43e872e4c..8c2a56ab282b12 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4429,8 +4429,38 @@ class Sema final { // Helper for delayed processing of attributes. void ProcessDeclAttributeDelayed(Decl *D, const ParsedAttributesView &AttrList); - void ProcessDeclAttributeList(Scope *S, Decl *D, const ParsedAttributesView &AL, - bool IncludeCXX11Attributes = true); + + // Options for ProcessDeclAttributeList(). + struct ProcessDeclAttributeOptions { + ProcessDeclAttributeOptions() + : IncludeCXX11Attributes(true), IgnoreTypeAttributes(false) {} + + ProcessDeclAttributeOptions WithIncludeCXX11Attributes(bool Val) { + ProcessDeclAttributeOptions Result = *this; + Result.IncludeCXX11Attributes = Val; + return Result; + } + + ProcessDeclAttributeOptions WithIgnoreTypeAttributes(bool Val) { + ProcessDeclAttributeOptions Result = *this; + Result.IgnoreTypeAttributes = Val; + return Result; + } + + // Should C++11 attributes be processed? + bool IncludeCXX11Attributes; + + // Should any type attributes encountered be ignored? + // If this option is false, a diagnostic will be emitted for any type + // attributes of a kind that does not "slide" from the declaration to + // the decl-specifier-seq. + bool IgnoreTypeAttributes; + }; + + void ProcessDeclAttributeList(Scope *S, Decl *D, + const ParsedAttributesView &AttrList, + const ProcessDeclAttributeOptions &Options = + ProcessDeclAttributeOptions()); bool ProcessAccessDeclAttributeList(AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 12af519c0919f4..a90d5d9cc48561 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -57,7 +57,7 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context, *OwnedType = DS.isTypeSpecOwned() ? DS.getRepAsDecl() : nullptr; // Parse the abstract-declarator, if present. - Declarator DeclaratorInfo(DS, Context); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), Context); ParseDeclarator(DeclaratorInfo); if (Range) *Range = DeclaratorInfo.getSourceRange(); @@ -1760,7 +1760,8 @@ void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributes &Attrs, /// Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context, SourceLocation &DeclEnd, - ParsedAttributes &Attrs, + ParsedAttributes &DeclAttrs, + ParsedAttributes &DeclSpecAttrs, SourceLocation *DeclSpecStart) { ParenBraceBracketBalancer BalancerRAIIObj(*this); // Must temporarily exit the objective-c container scope for @@ -1771,32 +1772,40 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context, switch (Tok.getKind()) { case tok::kw_template: case tok::kw_export: - ProhibitAttributes(Attrs); - SingleDecl = ParseDeclarationStartingWithTemplate(Context, DeclEnd, Attrs); + ProhibitAttributes(DeclAttrs); + ProhibitAttributes(DeclSpecAttrs); + SingleDecl = + ParseDeclarationStartingWithTemplate(Context, DeclEnd, DeclAttrs); break; case tok::kw_inline: // Could be the start of an inline namespace. Allowed as an ext in C++03. if (getLangOpts().CPlusPlus && NextToken().is(tok::kw_namespace)) { - ProhibitAttributes(Attrs); + ProhibitAttributes(DeclAttrs); + ProhibitAttributes(DeclSpecAttrs); SourceLocation InlineLoc = ConsumeToken(); return ParseNamespace(Context, DeclEnd, InlineLoc); } - return ParseSimpleDeclaration(Context, DeclEnd, Attrs, true, nullptr, - DeclSpecStart); + return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs, + true, nullptr, DeclSpecStart); case tok::kw_namespace: - ProhibitAttributes(Attrs); + ProhibitAttributes(DeclAttrs); + ProhibitAttributes(DeclSpecAttrs); return ParseNamespace(Context, DeclEnd); - case tok::kw_using: + case tok::kw_using: { + ParsedAttributes Attrs(AttrFactory); + takeAndConcatenateAttrs(DeclAttrs, DeclSpecAttrs, Attrs); return ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(), DeclEnd, Attrs); + } case tok::kw_static_assert: case tok::kw__Static_assert: - ProhibitAttributes(Attrs); + ProhibitAttributes(DeclAttrs); + ProhibitAttributes(DeclSpecAttrs); SingleDecl = ParseStaticAssertDeclaration(DeclEnd); break; default: - return ParseSimpleDeclaration(Context, DeclEnd, Attrs, true, nullptr, - DeclSpecStart); + return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs, + true, nullptr, DeclSpecStart); } // This routine returns a DeclGroup, if the thing we parsed only contains a @@ -1826,10 +1835,17 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context, /// the Declaration. The SourceLocation for this Decl is set to /// DeclSpecStart if DeclSpecStart is non-null. Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( - DeclaratorContext Context, SourceLocation &DeclEnd, ParsedAttributes &Attrs, + DeclaratorContext Context, SourceLocation &DeclEnd, + ParsedAttributes &DeclAttrs, ParsedAttributes &DeclSpecAttrs, bool RequireSemi, ForRangeInit *FRI, SourceLocation *DeclSpecStart) { + // Need to retain these for diagnostics before we add them to the DeclSepc. + ParsedAttributesView OriginalDeclSpecAttrs; + OriginalDeclSpecAttrs.addAll(DeclSpecAttrs.begin(), DeclSpecAttrs.end()); + OriginalDeclSpecAttrs.Range = DeclSpecAttrs.Range; + // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this); + DS.takeAttributesFrom(DeclSpecAttrs); DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext); @@ -1843,7 +1859,7 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.is(tok::semi)) { - ProhibitAttributes(Attrs); + ProhibitAttributes(DeclAttrs); DeclEnd = Tok.getLocation(); if (RequireSemi) ConsumeToken(); RecordDecl *AnonRecord = nullptr; @@ -1860,8 +1876,7 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); - DS.takeAttributesFrom(Attrs); - return ParseDeclGroup(DS, Context, &DeclEnd, FRI); + return ParseDeclGroup(DS, Context, DeclAttrs, &DeclEnd, FRI); } /// Returns true if this might be the start of a declarator, or a common typo @@ -2016,10 +2031,16 @@ void Parser::SkipMalformedDecl() { /// result. Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &Attrs, SourceLocation *DeclEnd, ForRangeInit *FRI) { // Parse the first declarator. - ParsingDeclarator D(*this, DS, Context); + // Consume all of the attributes from `Attrs` by moving them to our own local + // list. This ensures that we will not attempt to interpret them as statement + // attributes higher up the callchain. + ParsedAttributes LocalAttrs(AttrFactory); + LocalAttrs.takeAllFrom(Attrs); + ParsingDeclarator D(*this, DS, LocalAttrs, Context); ParseDeclarator(D); // Bail out if the first declarator didn't seem well-formed. @@ -3176,11 +3197,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if (!AttrsLastTime) ProhibitAttributes(attrs); else { - // Reject most C++11 / C2x attributes on the decl-specifier-seq, but - // allow `annotate_type` as a special case. - // FIXME: We should more generally allow type attributes to be placed - // on the decl-specifier-seq; https://reviews.llvm.org/D126061 will - // make this change. + // Reject C++11 / C2x attributes that aren't type attributes. for (const ParsedAttr &PA : attrs) { if (!PA.isCXX11Attribute() && !PA.isC2xAttribute()) continue; @@ -3188,7 +3205,18 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // We will warn about the unknown attribute elsewhere (in // SemaDeclAttr.cpp) continue; - if (PA.getKind() == ParsedAttr::AT_AnnotateType) + // GCC ignores this attribute when placed on the DeclSpec in [[]] + // syntax, so we do the same. + if (PA.getKind() == ParsedAttr::AT_VectorSize) { + Diag(PA.getLoc(), diag::warn_attribute_ignored) << PA; + PA.setInvalid(); + continue; + } + // We reject AT_LifetimeBound and AT_AnyX86NoCfCheck, even though they + // are type attributes, because we historically haven't allowed these + // to be used as type attributes in C++11 / C2x syntax. + if (PA.isTypeAttr() && PA.getKind() != ParsedAttr::AT_LifetimeBound && + PA.getKind() != ParsedAttr::AT_AnyX86NoCfCheck) continue; Diag(PA.getLoc(), diag::err_attribute_not_type_attr) << PA; PA.setInvalid(); @@ -4317,7 +4345,6 @@ void Parser::ParseStructDeclaration( // Parse leading attributes. ParsedAttributes Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); - DS.takeAttributesFrom(Attrs); // Parse the common specifier-qualifiers-list piece. ParseSpecifierQualifierList(DS); @@ -4325,6 +4352,11 @@ void Parser::ParseStructDeclaration( // If there are no declarators, this is a free-standing declaration // specifier. Let the actions module cope with it. if (Tok.is(tok::semi)) { + // C2x 6.7.2.1p9 : "The optional attribute specifier sequence in a + // member declaration appertains to each of the members declared by the + // member declarator list; it shall not appear if the optional member + // declarator list is omitted." + ProhibitAttributes(Attrs); RecordDecl *AnonRecord = nullptr; Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS, AnonRecord); @@ -4337,7 +4369,7 @@ void Parser::ParseStructDeclaration( bool FirstDeclarator = true; SourceLocation CommaLoc; while (true) { - ParsingFieldDeclarator DeclaratorInfo(*this, DS); + ParsingFieldDeclarator DeclaratorInfo(*this, DS, Attrs); DeclaratorInfo.D.setCommaLoc(CommaLoc); // Attributes are only allowed here on successive declarators. @@ -4696,7 +4728,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // declares 'enum E : int; E *p;' not 'enum E : int*; E p;'. DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS, AS, DeclSpecContext::DSC_type_specifier); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd()); @@ -6618,9 +6651,9 @@ void Parser::InitCXXThisScopeForDeclaratorIfRelevant( /// declarator D up to a paren, which indicates that we are parsing function /// arguments. /// -/// If FirstArgAttrs is non-null, then the caller parsed those arguments -/// immediately after the open paren - they should be considered to be the -/// first argument of a parameter. +/// If FirstArgAttrs is non-null, then the caller parsed those attributes +/// immediately after the open paren - they will be applied to the DeclSpec +/// of the first parameter. /// /// If RequiresArg is true, then the first argument of the function is required /// to be present and required to not be an identifier list. @@ -6922,7 +6955,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// /// DeclContext is the context of the declarator being parsed. If FirstArgAttrs /// is non-null, then the caller parsed those attributes immediately after the -/// open paren - they should be considered to be part of the first parameter. +/// open paren - they will be applied to the DeclSpec of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6974,33 +7007,37 @@ void Parser::ParseParameterDeclarationClause( // Just use the ParsingDeclaration "scope" of the declarator. DeclSpec DS(AttrFactory); - // Parse any C++11 attributes. - MaybeParseCXX11Attributes(DS.getAttributes()); + ParsedAttributes ArgDeclAttrs(AttrFactory); + ParsedAttributes ArgDeclSpecAttrs(AttrFactory); + + if (FirstArgAttrs.Range.isValid()) { + // If the caller parsed attributes for the first argument, add them now. + // Take them so that we only apply the attributes to the first parameter. + // We have already started parsing the decl-specifier sequence, so don't + // parse any parameter-declaration pieces that precede it. + ArgDeclSpecAttrs.takeAllFrom(FirstArgAttrs); + } else { + // Parse any C++11 attributes. + MaybeParseCXX11Attributes(ArgDeclAttrs); - // Skip any Microsoft attributes before a param. - MaybeParseMicrosoftAttributes(DS.getAttributes()); + // Skip any Microsoft attributes before a param. + MaybeParseMicrosoftAttributes(ArgDeclSpecAttrs); + } SourceLocation DSStart = Tok.getLocation(); - // If the caller parsed attributes for the first argument, add them now. - // Take them so that we only apply the attributes to the first parameter. - // FIXME: If we can leave the attributes in the token stream somehow, we can - // get rid of a parameter (FirstArgAttrs) and this statement. It might be - // too much hassle. - DS.takeAttributesFrom(FirstArgAttrs); - ParseDeclarationSpecifiers(DS); - + DS.takeAttributesFrom(ArgDeclSpecAttrs); // Parse the declarator. This is "PrototypeContext" or // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. - Declarator ParmDeclarator( - DS, DeclaratorCtx == DeclaratorContext::RequiresExpr - ? DeclaratorContext::RequiresExpr - : DeclaratorCtx == DeclaratorContext::LambdaExpr - ? DeclaratorContext::LambdaExprParameter - : DeclaratorContext::Prototype); + Declarator ParmDeclarator(DS, ArgDeclAttrs, + DeclaratorCtx == DeclaratorContext::RequiresExpr + ? DeclaratorContext::RequiresExpr + : DeclaratorCtx == DeclaratorContext::LambdaExpr + ? DeclaratorContext::LambdaExprParameter + : DeclaratorContext::Prototype); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -7299,7 +7336,8 @@ void Parser::ParseMisplacedBracketDeclarator(Declarator &D) { assert(!D.mayOmitIdentifier() && "Declarator cannot omit identifier"); SourceLocation StartBracketLoc = Tok.getLocation(); - Declarator TempDeclarator(D.getDeclSpec(), D.getContext()); + Declarator TempDeclarator(D.getDeclSpec(), ParsedAttributesView::none(), + D.getContext()); while (Tok.is(tok::l_square)) { ParseBracketDeclarator(TempDeclarator); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 1afe1995237a87..8b55b4c57aa65f 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -355,8 +355,8 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) { getCurScope(), DS.getSourceRange().getBegin(), Lang.get(), Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation()); - ParsedAttributes attrs(AttrFactory); - MaybeParseCXX11Attributes(attrs); + ParsedAttributes DeclAttrs(AttrFactory); + MaybeParseCXX11Attributes(DeclAttrs); if (Tok.isNot(tok::l_brace)) { // Reset the source range in DS, as the leading "extern" @@ -365,7 +365,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) { DS.SetRangeEnd(SourceLocation()); // ... but anyway remember that such an "extern" was seen. DS.setExternInLinkageSpec(true); - ParseExternalDeclaration(attrs, &DS); + ParseExternalDeclaration(DeclAttrs, &DS); return LinkageSpec ? Actions.ActOnFinishLinkageSpecification( getCurScope(), LinkageSpec, SourceLocation()) : nullptr; @@ -373,7 +373,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) { DS.abort(); - ProhibitAttributes(attrs); + ProhibitAttributes(DeclAttrs); BalancedDelimiterTracker T(*this, tok::l_brace); T.consumeOpen(); @@ -1217,7 +1217,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, EndLocation = ParseDecltypeSpecifier(DS); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); } @@ -1309,7 +1310,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, DS.SetTypeSpecType(TST_typename, IdLoc, PrevSpec, DiagID, Type, Actions.getASTContext().getPrintingPolicy()); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); } @@ -2680,23 +2682,15 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, TemplateInfo, TemplateDiags); } - ParsedAttributes attrs(AttrFactory); - ParsedAttributesView FnAttrs; + ParsedAttributes DeclAttrs(AttrFactory); // Optional C++11 attribute-specifier - MaybeParseCXX11Attributes(attrs); + MaybeParseCXX11Attributes(DeclAttrs); // The next token may be an OpenMP pragma annotation token. That would // normally be handled from ParseCXXClassMemberDeclarationWithPragmas, but in // this case, it came from an *attribute* rather than a pragma. Handle it now. if (Tok.is(tok::annot_attr_openmp)) - return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, attrs); - - // We need to keep these attributes for future diagnostic - // before they are taken over by declaration specifier. - FnAttrs.addAll(attrs.begin(), attrs.end()); - FnAttrs.Range = attrs.Range; - - MaybeParseMicrosoftAttributes(attrs); + return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, DeclAttrs); if (Tok.is(tok::kw_using)) { // Eat 'using'. @@ -2717,16 +2711,20 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, SourceLocation DeclEnd; // Otherwise, it must be a using-declaration or an alias-declaration. return ParseUsingDeclaration(DeclaratorContext::Member, TemplateInfo, - UsingLoc, DeclEnd, attrs, AS); + UsingLoc, DeclEnd, DeclAttrs, AS); } + ParsedAttributes DeclSpecAttrs(AttrFactory); + MaybeParseMicrosoftAttributes(DeclSpecAttrs); + // Hold late-parsed attributes so we can attach a Decl to them later. LateParsedAttrList CommonLateParsedAttrs; // decl-specifier-seq: // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this, TemplateDiags); - DS.takeAttributesFrom(attrs); + DS.takeAttributesFrom(DeclSpecAttrs); + if (MalformedTypeSpec) DS.SetTypeSpecError(); @@ -2764,7 +2762,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, if (TryConsumeToken(tok::semi)) { if (DS.isFriendSpecified()) - ProhibitAttributes(FnAttrs); + ProhibitAttributes(DeclAttrs); RecordDecl *AnonRecord = nullptr; Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec( @@ -2777,7 +2775,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, return Actions.ConvertDeclToDeclGroup(TheDecl); } - ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::Member); + ParsingDeclarator DeclaratorInfo(*this, DS, DeclAttrs, + DeclaratorContext::Member); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; @@ -2869,7 +2868,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DS.isFriendSpecified()) { // Diagnose attributes that appear before decl specifier: // [[]] friend int foo(); - ProhibitAttributes(FnAttrs); + ProhibitAttributes(DeclAttrs); } if (DefinitionKind != FunctionDefinitionKind::Declaration) { diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 6d0fd7e2f60146..a6a946d7f31b18 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1212,7 +1212,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, DS.SetTypeSpecType(TST_typename, ILoc, PrevSpec, DiagID, Typ, Actions.getASTContext().getPrintingPolicy()); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); if (Ty.isInvalid()) @@ -1490,7 +1491,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, PrevSpec, DiagID, Type, Actions.getASTContext().getPrintingPolicy()); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); if (Ty.isInvalid()) break; @@ -2296,7 +2298,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, if (isTypeIdUnambiguously()) { DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); SourceLocation LParenLoc = PP.getLocForEndOfToken(OpTok.getLocation()); @@ -2959,7 +2962,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // Parse the type declarator. DeclSpec DS(AttrFactory); ParseSpecifierQualifierList(DS); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); // If our type is followed by an identifier and either ':' or ']', then @@ -3494,7 +3498,8 @@ void Parser::ParseBlockId(SourceLocation CaretLoc) { ParseSpecifierQualifierList(DS); // Parse the block-declarator. - Declarator DeclaratorInfo(DS, DeclaratorContext::BlockLiteral); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::BlockLiteral); DeclaratorInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition); ParseDeclarator(DeclaratorInfo); @@ -3533,7 +3538,8 @@ ExprResult Parser::ParseBlockLiteralExpression() { // Parse the return type if present. DeclSpec DS(AttrFactory); - Declarator ParamInfo(DS, DeclaratorContext::BlockLiteral); + Declarator ParamInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::BlockLiteral); ParamInfo.setFunctionDefinitionKind(FunctionDefinitionKind::Definition); // FIXME: Since the return type isn't actually parsed, it can't be used to // fill ParamInfo with an initial valid range, so do it manually. diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 7b66f6c46339ae..c2dc71f5f8752f 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1248,7 +1248,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( // Parse lambda-declarator[opt]. DeclSpec DS(AttrFactory); - Declarator D(DS, DeclaratorContext::LambdaExpr); + Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::LambdaExpr); TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); Actions.PushLambdaScope(); @@ -1523,7 +1523,8 @@ ExprResult Parser::ParseCXXCasts() { ParseSpecifierQualifierList(DS); // Parse the abstract-declarator, if present. - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); SourceLocation RAngleBracketLoc = Tok.getLocation(); @@ -1849,7 +1850,8 @@ ExprResult Parser::ParseCXXThis() { /// In C++1z onwards, the type specifier can also be a template-name. ExprResult Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { - Declarator DeclaratorInfo(DS, DeclaratorContext::FunctionalCast); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::FunctionalCast); ParsedType TypeRep = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo).get(); assert((Tok.is(tok::l_paren) || @@ -2048,9 +2050,11 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, if (Tok.is(tok::kw_using)) DG = ParseAliasDeclarationInInitStatement( DeclaratorContext::SelectionInit, attrs); - else + else { + ParsedAttributes DeclSpecAttrs(AttrFactory); DG = ParseSimpleDeclaration(DeclaratorContext::SelectionInit, DeclEnd, - attrs, /*RequireSemi=*/true); + attrs, DeclSpecAttrs, /*RequireSemi=*/true); + } *InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd); return ParseCXXCondition(nullptr, Loc, CK, MissingOK); } @@ -2061,8 +2065,9 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, // permitted here. assert(FRI && "should not parse a for range declaration here"); SourceLocation DeclStart = Tok.getLocation(), DeclEnd; - DeclGroupPtrTy DG = ParseSimpleDeclaration(DeclaratorContext::ForInit, - DeclEnd, attrs, false, FRI); + ParsedAttributes DeclSpecAttrs(AttrFactory); + DeclGroupPtrTy DG = ParseSimpleDeclaration( + DeclaratorContext::ForInit, DeclEnd, attrs, DeclSpecAttrs, false, FRI); FRI->LoopVar = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); assert((FRI->ColonLoc.isValid() || !DG) && "cannot find for range declaration"); @@ -2079,11 +2084,10 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, // type-specifier-seq DeclSpec DS(AttrFactory); - DS.takeAttributesFrom(attrs); ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition); // declarator - Declarator DeclaratorInfo(DS, DeclaratorContext::Condition); + Declarator DeclaratorInfo(DS, attrs, DeclaratorContext::Condition); ParseDeclarator(DeclaratorInfo); // simple-asm-expr[opt] @@ -2736,7 +2740,8 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, // Parse the conversion-declarator, which is merely a sequence of // ptr-operators. - Declarator D(DS, DeclaratorContext::ConversionId); + Declarator D(DS, ParsedAttributesView::none(), + DeclaratorContext::ConversionId); ParseDeclaratorInternal(D, /*DirectDeclParser=*/nullptr); // Finish up the type. @@ -3094,7 +3099,8 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) { SourceRange TypeIdParens; DeclSpec DS(AttrFactory); - Declarator DeclaratorInfo(DS, DeclaratorContext::CXXNew); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::CXXNew); if (Tok.is(tok::l_paren)) { // If it turns out to be a placement, we change the type location. BalancedDelimiterTracker T(*this, tok::l_paren); @@ -3945,7 +3951,8 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, if (ParseAs >= CompoundLiteral) { // Parse the type declarator. DeclSpec DS(AttrFactory); - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); { ColonProtectionRAIIObject InnerColonProtection(*this); ParseSpecifierQualifierList(DS); @@ -4023,7 +4030,8 @@ ExprResult Parser::ParseBuiltinBitCast() { ParseSpecifierQualifierList(DS); // Parse the abstract-declarator, if present. - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); ParseDeclarator(DeclaratorInfo); if (ExpectAndConsume(tok::comma)) { diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 15cec81ef1b3fe..734c66f65dc29c 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -650,19 +650,21 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, if (Tok.is(tok::r_brace)) break; - ParsedAttributes attrs(AttrFactory); + ParsedAttributes EmptyAttrs(AttrFactory); // Since we call ParseDeclarationOrFunctionDefinition() instead of // ParseExternalDeclaration() below (so that this doesn't parse nested // @interfaces), this needs to duplicate some code from the latter. if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) { SourceLocation DeclEnd; - allTUVariables.push_back( - ParseDeclaration(DeclaratorContext::File, DeclEnd, attrs)); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + allTUVariables.push_back(ParseDeclaration( + DeclaratorContext::File, DeclEnd, EmptyAttrs, EmptyDeclSpecAttrs)); continue; } - allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs)); + allTUVariables.push_back( + ParseDeclarationOrFunctionDefinition(EmptyAttrs)); continue; } @@ -1225,6 +1227,10 @@ static void takeDeclAttributes(ParsedAttributesView &attrs, /// declarator and add them to the given list. static void takeDeclAttributes(ParsedAttributes &attrs, Declarator &D) { + // This gets called only from Parser::ParseObjCTypeName(), and that should + // never add declaration attributes to the Declarator. + assert(D.getDeclarationAttributes().empty()); + // First, take ownership of all attributes. attrs.getPool().takeAllFrom(D.getAttributePool()); attrs.getPool().takeAllFrom(D.getDeclSpec().getAttributePool()); @@ -1268,7 +1274,7 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, if (context == DeclaratorContext::ObjCResult) dsContext = DeclSpecContext::DSC_objc_method_result; ParseSpecifierQualifierList(declSpec, AS_none, dsContext); - Declarator declarator(declSpec, context); + Declarator declarator(declSpec, ParsedAttributesView::none(), context); ParseDeclarator(declarator); // If that's not invalid, extract a type. @@ -1487,7 +1493,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); // Parse the declarator. - Declarator ParmDecl(DS, DeclaratorContext::Prototype); + Declarator ParmDecl(DS, ParsedAttributesView::none(), + DeclaratorContext::Prototype); ParseDeclarator(ParmDecl); IdentifierInfo *ParmII = ParmDecl.getIdentifier(); Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDecl); @@ -1693,7 +1700,8 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( typeArg, Actions.getASTContext().getPrintingPolicy()); // Form a declarator to turn this into a type. - Declarator D(DS, DeclaratorContext::TypeName); + Declarator D(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D); if (fullTypeArg.isUsable()) { typeArgs.push_back(fullTypeArg.get()); @@ -2538,7 +2546,8 @@ StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { if (Tok.isNot(tok::ellipsis)) { DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); - Declarator ParmDecl(DS, DeclaratorContext::ObjCCatch); + Declarator ParmDecl(DS, ParsedAttributesView::none(), + DeclaratorContext::ObjCCatch); ParseDeclarator(ParmDecl); // Inform the actions module about the declarator, so it @@ -2954,7 +2963,8 @@ bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) { // We have a class message. Turn the simple-type-specifier or // typename-specifier we parsed into a type and parse the // remainder of the class message. - Declarator DeclaratorInfo(DS, DeclaratorContext::TypeName); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), + DeclaratorContext::TypeName); TypeResult Type = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); if (Type.isInvalid()) return true; diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 0385d5f1586e90..e8d0c1528b5265 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -639,7 +639,7 @@ TypeResult Parser::parseOpenMPDeclareMapperVarDecl(SourceRange &Range, // Parse the declarator. DeclaratorContext Context = DeclaratorContext::Prototype; - Declarator DeclaratorInfo(DS, Context); + Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), Context); ParseDeclarator(DeclaratorInfo); Range = DeclaratorInfo.getSourceRange(); if (DeclaratorInfo.getIdentifier() == nullptr) { diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index ce5541a966d17a..f43e5b4c8922a1 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -105,15 +105,21 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, // statement are different from [[]] attributes that follow an __attribute__ // at the start of the statement. Thus, we're not using MaybeParseAttributes // here because we don't want to allow arbitrary orderings. - ParsedAttributes Attrs(AttrFactory); - MaybeParseCXX11Attributes(Attrs, /*MightBeObjCMessageSend*/ true); + ParsedAttributes CXX11Attrs(AttrFactory); + MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true); + ParsedAttributes GNUAttrs(AttrFactory); if (getLangOpts().OpenCL) - MaybeParseGNUAttributes(Attrs); + MaybeParseGNUAttributes(GNUAttrs); StmtResult Res = ParseStatementOrDeclarationAfterAttributes( - Stmts, StmtCtx, TrailingElseLoc, Attrs); + Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUAttrs); MaybeDestroyTemplateIds(); + // Attributes that are left should all go on the statement, so concatenate the + // two lists. + ParsedAttributes Attrs(AttrFactory); + takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs); + assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) && "attributes on empty statement"); @@ -158,7 +164,8 @@ class StatementFilterCCC final : public CorrectionCandidateCallback { StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( StmtVector &Stmts, ParsedStmtContext StmtCtx, - SourceLocation *TrailingElseLoc, ParsedAttributes &Attrs) { + SourceLocation *TrailingElseLoc, ParsedAttributes &CXX11Attrs, + ParsedAttributes &GNUAttrs) { const char *SemiError = nullptr; StmtResult Res; SourceLocation GNUAttributeLoc; @@ -184,6 +191,12 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( case tok::identifier: { Token Next = NextToken(); if (Next.is(tok::colon)) { // C99 6.8.1: labeled-statement + // Both C++11 and GNU attributes preceding the label appertain to the + // label, so put them in a single list to pass on to + // ParseLabeledStatement(). + ParsedAttributes Attrs(AttrFactory); + takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs); + // identifier ':' statement return ParseLabeledStatement(Attrs, StmtCtx); } @@ -213,25 +226,33 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( } default: { + bool HaveAttrs = !CXX11Attrs.empty() || !GNUAttrs.empty(); + auto IsStmtAttr = [](ParsedAttr &Attr) { return Attr.isStmtAttr(); }; + bool AllAttrsAreStmtAttrs = llvm::all_of(CXX11Attrs, IsStmtAttr) && + llvm::all_of(GNUAttrs, IsStmtAttr); if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt || (StmtCtx & ParsedStmtContext::AllowDeclarationsInC) != ParsedStmtContext()) && - ((GNUAttributeLoc.isValid() && - !(!Attrs.empty() && - llvm::all_of( - Attrs, [](ParsedAttr &Attr) { return Attr.isStmtAttr(); }))) || + ((GNUAttributeLoc.isValid() && !(HaveAttrs && AllAttrsAreStmtAttrs)) || isDeclarationStatement())) { SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy Decl; if (GNUAttributeLoc.isValid()) { DeclStart = GNUAttributeLoc; - Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, Attrs, - &GNUAttributeLoc); + Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs, + GNUAttrs, &GNUAttributeLoc); } else { - Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, Attrs); + Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs, + GNUAttrs); } - if (Attrs.Range.getBegin().isValid()) - DeclStart = Attrs.Range.getBegin(); + if (CXX11Attrs.Range.getBegin().isValid()) { + // The caller must guarantee that the CXX11Attrs appear before the + // GNUAttrs, and we rely on that here. + assert(GNUAttrs.Range.getBegin().isInvalid() || + GNUAttrs.Range.getBegin() > CXX11Attrs.Range.getBegin()); + DeclStart = CXX11Attrs.Range.getBegin(); + } else if (GNUAttrs.Range.getBegin().isValid()) + DeclStart = GNUAttrs.Range.getBegin(); return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd); } @@ -245,7 +266,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( case tok::kw___attribute: { GNUAttributeLoc = Tok.getLocation(); - ParseGNUAttributes(Attrs); + ParseGNUAttributes(GNUAttrs); goto Retry; } @@ -297,7 +318,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( break; case tok::kw_asm: { - ProhibitAttributes(Attrs); + for (const ParsedAttr &AL : CXX11Attrs) + Diag(AL.getRange().getBegin(), diag::warn_attribute_ignored) << AL; + // Prevent these from being interpreted as statement attributes later on. + CXX11Attrs.clear(); + ProhibitAttributes(GNUAttrs); bool msAsm = false; Res = ParseAsmStatement(msAsm); Res = Actions.ActOnFinishFullStmt(Res.get()); @@ -308,7 +333,8 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( case tok::kw___if_exists: case tok::kw___if_not_exists: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); ParseMicrosoftIfExistsStatement(Stmts); // An __if_exists block is like a compound statement, but it doesn't create // a new scope. @@ -318,7 +344,8 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( return ParseCXXTryBlock(); case tok::kw___try: - ProhibitAttributes(Attrs); // TODO: is it correct? + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); return ParseSEHTryBlock(); case tok::kw___leave: @@ -327,55 +354,65 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( break; case tok::annot_pragma_vis: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaVisibility(); return StmtEmpty(); case tok::annot_pragma_pack: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaPack(); return StmtEmpty(); case tok::annot_pragma_msstruct: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaMSStruct(); return StmtEmpty(); case tok::annot_pragma_align: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaAlign(); return StmtEmpty(); case tok::annot_pragma_weak: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaWeak(); return StmtEmpty(); case tok::annot_pragma_weakalias: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaWeakAlias(); return StmtEmpty(); case tok::annot_pragma_redefine_extname: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaRedefineExtname(); return StmtEmpty(); case tok::annot_pragma_fp_contract: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); Diag(Tok, diag::err_pragma_file_or_compound_scope) << "fp_contract"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_fp: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); Diag(Tok, diag::err_pragma_file_or_compound_scope) << "clang fp"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_fenv_access: case tok::annot_pragma_fenv_access_ms: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); Diag(Tok, diag::err_pragma_file_or_compound_scope) << (Kind == tok::annot_pragma_fenv_access ? "STDC FENV_ACCESS" : "fenv_access"); @@ -383,53 +420,62 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( return StmtEmpty(); case tok::annot_pragma_fenv_round: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); Diag(Tok, diag::err_pragma_file_or_compound_scope) << "STDC FENV_ROUND"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_float_control: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); Diag(Tok, diag::err_pragma_file_or_compound_scope) << "float_control"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_opencl_extension: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaOpenCLExtension(); return StmtEmpty(); case tok::annot_pragma_captured: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); return HandlePragmaCaptured(); case tok::annot_pragma_openmp: // Prohibit attributes that are not OpenMP attributes, but only before // processing a #pragma omp clause. - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); LLVM_FALLTHROUGH; case tok::annot_attr_openmp: // Do not prohibit attributes if they were OpenMP attributes. return ParseOpenMPDeclarativeOrExecutableDirective(StmtCtx); case tok::annot_pragma_ms_pointers_to_members: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaMSPointersToMembers(); return StmtEmpty(); case tok::annot_pragma_ms_pragma: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaMSPragma(); return StmtEmpty(); case tok::annot_pragma_ms_vtordisp: - ProhibitAttributes(Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); HandlePragmaMSVtorDisp(); return StmtEmpty(); case tok::annot_pragma_loop_hint: - ProhibitAttributes(Attrs); - return ParsePragmaLoopHint(Stmts, StmtCtx, TrailingElseLoc, Attrs); + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); + return ParsePragmaLoopHint(Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs); case tok::annot_pragma_dump: HandlePragmaDump(); @@ -658,8 +704,9 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs, Attrs.takeAllFrom(TempAttrs); else { StmtVector Stmts; - SubStmt = ParseStatementOrDeclarationAfterAttributes(Stmts, StmtCtx, - nullptr, TempAttrs); + ParsedAttributes EmptyCXX11Attrs(AttrFactory); + SubStmt = ParseStatementOrDeclarationAfterAttributes( + Stmts, StmtCtx, nullptr, EmptyCXX11Attrs, TempAttrs); if (!TempAttrs.empty() && !SubStmt.isInvalid()) SubStmt = Actions.ActOnAttributedStmt(TempAttrs, SubStmt.get()); } @@ -1128,8 +1175,9 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { ExtensionRAIIObject O(Diags); SourceLocation DeclStart = Tok.getLocation(), DeclEnd; - DeclGroupPtrTy Res = - ParseDeclaration(DeclaratorContext::Block, DeclEnd, attrs); + ParsedAttributes DeclSpecAttrs(AttrFactory); + DeclGroupPtrTy Res = ParseDeclaration(DeclaratorContext::Block, DeclEnd, + attrs, DeclSpecAttrs); R = Actions.ActOnDeclStmt(Res, DeclStart, DeclEnd); } else { // Otherwise this was a unary __extension__ marker. @@ -1975,8 +2023,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt); SourceLocation DeclStart = Tok.getLocation(), DeclEnd; + ParsedAttributes DeclSpecAttrs(AttrFactory); DG = ParseSimpleDeclaration( - DeclaratorContext::ForInit, DeclEnd, attrs, false, + DeclaratorContext::ForInit, DeclEnd, attrs, DeclSpecAttrs, false, MightBeForRangeStmt ? &ForRangeInfo : nullptr); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); if (ForRangeInfo.ParsedForRangeDecl()) { @@ -2347,8 +2396,9 @@ StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, // Get the next statement. MaybeParseCXX11Attributes(Attrs); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); StmtResult S = ParseStatementOrDeclarationAfterAttributes( - Stmts, StmtCtx, TrailingElseLoc, Attrs); + Stmts, StmtCtx, TrailingElseLoc, Attrs, EmptyDeclSpecAttrs); Attrs.takeAllFrom(TempAttrs); @@ -2584,12 +2634,11 @@ StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) { MaybeParseCXX11Attributes(Attributes); DeclSpec DS(AttrFactory); - DS.takeAttributesFrom(Attributes); if (ParseCXXTypeSpecifierSeq(DS)) return StmtError(); - Declarator ExDecl(DS, DeclaratorContext::CXXCatch); + Declarator ExDecl(DS, Attributes, DeclaratorContext::CXXCatch); ParseDeclarator(ExDecl); ExceptionDecl = Actions.ActOnExceptionDeclarator(getCurScope(), ExDecl); } else diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index e66329e3bfc7e2..97be8115989e23 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -245,11 +245,10 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( // Move the attributes from the prefix into the DS. if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) ProhibitAttributes(prefixAttrs); - else - DS.takeAttributesFrom(prefixAttrs); // Parse the declarator. - ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + ParsingDeclarator DeclaratorInfo(*this, DS, prefixAttrs, + (DeclaratorContext)Context); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); @@ -669,7 +668,8 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { // probably meant to write the type of a NTTP. DeclSpec DS(getAttrFactory()); DS.SetTypeSpecError(); - Declarator D(DS, DeclaratorContext::TemplateParam); + Declarator D(DS, ParsedAttributesView::none(), + DeclaratorContext::TemplateParam); D.SetIdentifier(nullptr, Tok.getLocation()); D.setInvalidType(true); NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter( @@ -993,7 +993,8 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { DeclSpecContext::DSC_template_param); // Parse this as a typename. - Declarator ParamDecl(DS, DeclaratorContext::TemplateParam); + Declarator ParamDecl(DS, ParsedAttributesView::none(), + DeclaratorContext::TemplateParam); ParseDeclarator(ParamDecl); if (DS.getTypeSpecType() == DeclSpec::TST_unspecified) { Diag(Tok.getLocation(), diag::err_expected_template_parameter); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 7ffaf057623f6f..7fad3b2388ce32 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -740,6 +740,9 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, /// ParseExternalDeclaration: /// +/// The `Attrs` that are passed in are C++11 attributes and appertain to the +/// declaration. +/// /// external-declaration: [C99 6.9], declaration: [C++ dcl.dcl] /// function-definition /// declaration @@ -929,7 +932,9 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, // A function definition cannot start with any of these keywords. { SourceLocation DeclEnd; - return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs, + EmptyDeclSpecAttrs); } case tok::kw_static: @@ -939,7 +944,9 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored) << 0; SourceLocation DeclEnd; - return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs, + EmptyDeclSpecAttrs); } goto dont_know; @@ -950,7 +957,9 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, // Inline namespaces. Allowed as an extension even in C++03. if (NextKind == tok::kw_namespace) { SourceLocation DeclEnd; - return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs, + EmptyDeclSpecAttrs); } // Parse (then ignore) 'inline' prior to a template instantiation. This is @@ -959,7 +968,9 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored) << 1; SourceLocation DeclEnd; - return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs); + ParsedAttributes EmptyDeclSpecAttrs(AttrFactory); + return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs, + EmptyDeclSpecAttrs); } } goto dont_know; @@ -1112,8 +1123,6 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( return Actions.ConvertDeclToDeclGroup(TheDecl); } - DS.takeAttributesFrom(Attrs); - // ObjC2 allows prefix attributes on class interfaces and protocols. // FIXME: This still needs better diagnostics. We should only accept // attributes here, no types, etc. @@ -1128,6 +1137,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( } DS.abort(); + DS.takeAttributesFrom(Attrs); const char *PrevSpec = nullptr; unsigned DiagID; @@ -1151,11 +1161,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( if (getLangOpts().CPlusPlus && isTokenStringLiteral() && DS.getStorageClassSpec() == DeclSpec::SCS_extern && DS.getParsedSpecifiers() == DeclSpec::PQ_StorageClassSpecifier) { + ProhibitAttributes(Attrs); Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File); return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, DeclaratorContext::File); + return ParseDeclGroup(DS, DeclaratorContext::File, Attrs); } Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( @@ -1473,7 +1484,8 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { } // Parse the first declarator attached to this declspec. - Declarator ParmDeclarator(DS, DeclaratorContext::KNRTypeList); + Declarator ParmDeclarator(DS, ParsedAttributesView::none(), + DeclaratorContext::KNRTypeList); ParseDeclarator(ParmDeclarator); // Handle the full declarator list. diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp index c2742c7db0dcaa..4b9a694270c59e 100644 --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -212,6 +212,39 @@ bool ParsedAttr::isSupportedByPragmaAttribute() const { return getInfo().IsSupportedByPragmaAttribute; } +bool ParsedAttr::slidesFromDeclToDeclSpecLegacyBehavior() const { + assert(isStandardAttributeSyntax()); + + // We have historically allowed some type attributes with standard attribute + // syntax to slide to the decl-specifier-seq, so we have to keep supporting + // it. This property is consciously not defined as a flag in Attr.td because + // we don't want new attributes to specify it. + // + // Note: No new entries should be added to this list. Entries should be + // removed from this list after a suitable deprecation period, provided that + // there are no compatibility considerations with other compilers. If + // possible, we would like this list to go away entirely. + switch (getParsedKind()) { + case AT_AddressSpace: + case AT_OpenCLPrivateAddressSpace: + case AT_OpenCLGlobalAddressSpace: + case AT_OpenCLGlobalDeviceAddressSpace: + case AT_OpenCLGlobalHostAddressSpace: + case AT_OpenCLLocalAddressSpace: + case AT_OpenCLConstantAddressSpace: + case AT_OpenCLGenericAddressSpace: + case AT_NeonPolyVectorType: + case AT_NeonVectorType: + case AT_ArmMveStrictPolymorphism: + case AT_BTFTypeTag: + case AT_ObjCGC: + case AT_MatrixType: + return true; + default: + return false; + } +} + bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; } unsigned ParsedAttr::getSemanticSpelling() const { @@ -265,3 +298,20 @@ bool ParsedAttr::checkAtMostNumArgs(Sema &S, unsigned Num) const { diag::err_attribute_too_many_arguments, std::greater()); } + +void clang::takeAndConcatenateAttrs(ParsedAttributes &First, + ParsedAttributes &Second, + ParsedAttributes &Result) { + // Note that takeAllFrom() puts the attributes at the beginning of the list, + // so to obtain the correct ordering, we add `Second`, then `First`. + Result.takeAllFrom(Second); + Result.takeAllFrom(First); + if (First.Range.getBegin().isValid()) + Result.Range.setBegin(First.Range.getBegin()); + else + Result.Range.setBegin(Second.Range.getBegin()); + if (Second.Range.getEnd().isValid()) + Result.Range.setEnd(Second.Range.getEnd()); + else + Result.Range.setEnd(First.Range.getEnd()); +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cfba17d6e23d8e..d8f2f332857f7c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5461,7 +5461,7 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, Diag(DS.getBeginLoc(), diag::ext_no_declarators) << DS.getSourceRange(); // Mock up a declarator. - Declarator Dc(DS, DeclaratorContext::Member); + Declarator Dc(DS, ParsedAttributesView::none(), DeclaratorContext::Member); TypeSourceInfo *TInfo = GetTypeForDeclarator(Dc, S); assert(TInfo && "couldn't build declarator info for anonymous struct/union"); @@ -5558,7 +5558,7 @@ Decl *Sema::BuildMicrosoftCAnonymousStruct(Scope *S, DeclSpec &DS, assert(Record && "expected a record!"); // Mock up a declarator. - Declarator Dc(DS, DeclaratorContext::TypeName); + Declarator Dc(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); TypeSourceInfo *TInfo = GetTypeForDeclarator(Dc, S); assert(TInfo && "couldn't build declarator info for anonymous struct"); @@ -7000,7 +7000,8 @@ static bool hasParsedAttr(Scope *S, const Declarator &PD, } // Finally, check attributes on the decl itself. - return PD.getAttributes().hasAttribute(Kind); + return PD.getAttributes().hasAttribute(Kind) || + PD.getDeclarationAttributes().hasAttribute(Kind); } /// Adjust the \c DeclContext for a function or variable that might be a @@ -13332,7 +13333,7 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, DS.SetTypeSpecType(DeclSpec::TST_auto, IdentLoc, PrevSpec, DiagID, getPrintingPolicy()); - Declarator D(DS, DeclaratorContext::ForInit); + Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::ForInit); D.SetIdentifier(Ident, IdentLoc); D.takeAttributes(Attrs); @@ -14330,7 +14331,8 @@ void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D, // Use the identifier location for the type source range. DS.SetRangeStart(FTI.Params[i].IdentLoc); DS.SetRangeEnd(FTI.Params[i].IdentLoc); - Declarator ParamD(DS, DeclaratorContext::KNRTypeList); + Declarator ParamD(DS, ParsedAttributesView::none(), + DeclaratorContext::KNRTypeList); ParamD.SetIdentifier(FTI.Params[i].Ident, FTI.Params[i].IdentLoc); FTI.Params[i].Param = ActOnParamDeclarator(S, ParamD); } @@ -15352,7 +15354,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, (void)Error; // Silence warning. assert(!Error && "Error setting up implicit decl!"); SourceLocation NoLoc; - Declarator D(DS, DeclaratorContext::Block); + Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::Block); D.AddTypeInfo(DeclaratorChunk::getFunction(/*HasProto=*/false, /*IsAmbiguous=*/false, /*LParenLoc=*/NoLoc, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 2e62cc0c242ae3..af8dc853eba695 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8316,15 +8316,21 @@ static bool MustDelayAttributeArguments(const ParsedAttr &AL) { /// ProcessDeclAttribute - Apply the specific attribute to the specified decl if /// the attribute applies to decls. If the attribute is a type attribute, just /// silently ignore it if a GNU attribute. -static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, - const ParsedAttr &AL, - bool IncludeCXX11Attributes) { +static void +ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, + const Sema::ProcessDeclAttributeOptions &Options) { if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute) return; // Ignore C++11 attributes on declarator chunks: they appertain to the type // instead. - if (AL.isCXX11Attribute() && !IncludeCXX11Attributes) + // FIXME: We currently check the attribute syntax directly instead of using + // isCXX11Attribute(), which currently erroneously classifies the C11 + // `_Alignas` attribute as a C++11 attribute. `_Alignas` can appear on the + // `DeclSpec`, so we need to let it through here to make sure it is processed + // appropriately. Once the behavior of isCXX11Attribute() is fixed, we can + // go back to using that here. + if (AL.getSyntax() == ParsedAttr::AS_CXX11 && !Options.IncludeCXX11Attributes) return; // Unknown attributes are automatically warned on. Target-specific attributes @@ -8357,14 +8363,76 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, if (AL.getInfo().handleDeclAttribute(S, D, AL) != ParsedAttrInfo::NotHandled) break; if (!AL.isStmtAttr()) { - // Type attributes are handled elsewhere; silently move on. assert(AL.isTypeAttr() && "Non-type attribute not handled"); - break; + } + if (AL.isTypeAttr()) { + if (Options.IgnoreTypeAttributes) + break; + if (!AL.isStandardAttributeSyntax()) { + // Non-[[]] type attributes are handled in processTypeAttrs(); silently + // move on. + break; + } + + // According to the C and C++ standards, we should never see a + // [[]] type attribute on a declaration. However, we have in the past + // allowed some type attributes to "slide" to the `DeclSpec`, so we need + // to continue to support this legacy behavior. We only do this, however, + // if + // - we actually have a `DeclSpec`, i.e. if we're looking at a + // `DeclaratorDecl`, or + // - we are looking at an alias-declaration, where historically we have + // allowed type attributes after the identifier to slide to the type. + if (AL.slidesFromDeclToDeclSpecLegacyBehavior() && + isa(D)) { + // Suggest moving the attribute to the type instead, but only for our + // own vendor attributes; moving other vendors' attributes might hurt + // portability. + if (AL.isClangScope()) { + S.Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl) + << AL << D->getLocation(); + } + + // Allow this type attribute to be handled in processTypeAttrs(); + // silently move on. + break; + } + + if (AL.getKind() == ParsedAttr::AT_Regparm) { + // `regparm` is a special case: It's a type attribute but we still want + // to treat it as if it had been written on the declaration because that + // way we'll be able to handle it directly in `processTypeAttr()`. + // If we treated `regparm` it as if it had been written on the + // `DeclSpec`, the logic in `distributeFunctionTypeAttrFromDeclSepc()` + // would try to move it to the declarator, but that doesn't work: We + // can't remove the attribute from the list of declaration attributes + // because it might be needed by other declarators in the same + // declaration. + break; + } + + if (AL.getKind() == ParsedAttr::AT_VectorSize) { + // `vector_size` is a special case: It's a type attribute semantically, + // but GCC expects the [[]] syntax to be written on the declaration (and + // warns that the attribute has no effect if it is placed on the + // decl-specifier-seq). + // Silently move on and allow the attribute to be handled in + // processTypeAttr(). + break; + } + + if (AL.getKind() == ParsedAttr::AT_NoDeref) { + // FIXME: `noderef` currently doesn't work correctly in [[]] syntax. + // See https://github.com/llvm/llvm-project/issues/55790 for details. + // We allow processTypeAttrs() to emit a warning and silently move on. + break; + } } // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a // statement attribute is not written on a declaration, but this code is - // needed for attributes in Attr.td that do not list any subjects. - S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl) + // needed for type attributes as well as statement attributes in Attr.td + // that do not list any subjects. + S.Diag(AL.getLoc(), diag::err_attribute_invalid_on_decl) << AL << D->getLocation(); break; case ParsedAttr::AT_Interrupt: @@ -9010,14 +9078,14 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, /// ProcessDeclAttributeList - Apply all the decl attributes in the specified /// attribute list to the specified decl, ignoring any type attributes. -void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, - const ParsedAttributesView &AttrList, - bool IncludeCXX11Attributes) { +void Sema::ProcessDeclAttributeList( + Scope *S, Decl *D, const ParsedAttributesView &AttrList, + const ProcessDeclAttributeOptions &Options) { if (AttrList.empty()) return; for (const ParsedAttr &AL : AttrList) - ProcessDeclAttribute(*this, S, D, AL, IncludeCXX11Attributes); + ProcessDeclAttribute(*this, S, D, AL, Options); // FIXME: We should be able to handle these cases in TableGen. // GCC accepts @@ -9105,7 +9173,8 @@ bool Sema::ProcessAccessDeclAttributeList( AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList) { for (const ParsedAttr &AL : AttrList) { if (AL.getKind() == ParsedAttr::AT_Annotate) { - ProcessDeclAttribute(*this, nullptr, ASDecl, AL, AL.isCXX11Attribute()); + ProcessDeclAttribute(*this, nullptr, ASDecl, AL, + ProcessDeclAttributeOptions()); } else { Diag(AL.getLoc(), diag::err_only_annotate_after_access_spec); return true; @@ -9138,6 +9207,7 @@ static void checkUnusedDeclAttributes(Sema &S, const ParsedAttributesView &A) { /// used to build a declaration, complain about any decl attributes /// which might be lying around on it. void Sema::checkUnusedDeclAttributes(Declarator &D) { + ::checkUnusedDeclAttributes(*this, D.getDeclarationAttributes()); ::checkUnusedDeclAttributes(*this, D.getDeclSpec().getAttributes()); ::checkUnusedDeclAttributes(*this, D.getAttributes()); for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) @@ -9246,17 +9316,43 @@ void Sema::ProcessPragmaWeak(Scope *S, Decl *D) { /// it, apply them to D. This is a bit tricky because PD can have attributes /// specified in many different places, and we need to find and apply them all. void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { + // Ordering of attributes can be important, so we take care to process + // attributes in the order in which they appeared in the source code. + + // First, process attributes that appeared on the declaration itself (but + // only if they don't have the legacy behavior of "sliding" to the DeclSepc). + ParsedAttributesView NonSlidingAttrs; + for (ParsedAttr &AL : PD.getDeclarationAttributes()) { + if (AL.slidesFromDeclToDeclSpecLegacyBehavior()) { + // Skip processing the attribute, but do check if it appertains to the + // declaration. This is needed for the `MatrixType` attribute, which, + // despite being a type attribute, defines a `SubjectList` that only + // allows it to be used on typedef declarations. + AL.diagnoseAppertainsTo(*this, D); + } else { + NonSlidingAttrs.addAtEnd(&AL); + } + } + ProcessDeclAttributeList(S, D, NonSlidingAttrs); + // Apply decl attributes from the DeclSpec if present. - if (!PD.getDeclSpec().getAttributes().empty()) - ProcessDeclAttributeList(S, D, PD.getDeclSpec().getAttributes()); + if (!PD.getDeclSpec().getAttributes().empty()) { + ProcessDeclAttributeList(S, D, PD.getDeclSpec().getAttributes(), + ProcessDeclAttributeOptions() + .WithIncludeCXX11Attributes(false) + .WithIgnoreTypeAttributes(true)); + } // Walk the declarator structure, applying decl attributes that were in a type // position to the decl itself. This handles cases like: // int *__attr__(x)** D; // when X is a decl attribute. - for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) + for (unsigned i = 0, e = PD.getNumTypeObjects(); i != e; ++i) { ProcessDeclAttributeList(S, D, PD.getTypeObject(i).getAttrs(), - /*IncludeCXX11Attributes=*/false); + ProcessDeclAttributeOptions() + .WithIncludeCXX11Attributes(false) + .WithIgnoreTypeAttributes(true)); + } // Finally, apply any attributes on the decl itself. ProcessDeclAttributeList(S, D, PD.getAttributes()); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 214332e53c0f09..6c8704cd0b789f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16860,7 +16860,8 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS, // Try to convert the decl specifier to a type. This works for // friend templates because ActOnTag never produces a ClassTemplateDecl // for a TUK_Friend. - Declarator TheDeclarator(DS, DeclaratorContext::Member); + Declarator TheDeclarator(DS, ParsedAttributesView::none(), + DeclaratorContext::Member); TypeSourceInfo *TSI = GetTypeForDeclarator(TheDeclarator, S); QualType T = TSI->getType(); if (TheDeclarator.isInvalidType()) diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 5c3903e652a514..aa0034e616f27c 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1588,7 +1588,7 @@ void Sema::actOnObjCTypeArgsOrProtocolQualifiers( DS.SetRangeEnd(loc); // Form the declarator. - Declarator D(DS, DeclaratorContext::TypeName); + Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); // If we have a typedef of an Objective-C class type that is missing a '*', // add the '*'. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index e487c892e569dc..0c84673b3479cb 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -629,15 +629,6 @@ static void distributeFunctionTypeAttrFromDeclSpec(TypeProcessingState &state, QualType &declSpecType) { state.saveDeclSpecAttrs(); - // C++11 attributes before the decl specifiers actually appertain to - // the declarators. Move them straight there. We don't support the - // 'put them wherever you like' semantics we allow for GNU attributes. - if (attr.isStandardAttributeSyntax()) { - moveAttrFromListToList(attr, state.getCurrentAttributes(), - state.getDeclarator().getAttributes()); - return; - } - // Try to distribute to the innermost. if (distributeFunctionTypeAttrToInnermost( state, attr, state.getCurrentAttributes(), declSpecType)) @@ -648,8 +639,10 @@ static void distributeFunctionTypeAttrFromDeclSpec(TypeProcessingState &state, state.addIgnoredTypeAttr(attr); } -/// A function type attribute was written on the declarator. Try to -/// apply it somewhere. +/// A function type attribute was written on the declarator or declaration. +/// Try to apply it somewhere. +/// `Attrs` is the attribute list containing the declaration (either of the +/// declarator or the declaration). static void distributeFunctionTypeAttrFromDeclarator(TypeProcessingState &state, ParsedAttr &attr, QualType &declSpecType) { @@ -666,7 +659,7 @@ static void distributeFunctionTypeAttrFromDeclarator(TypeProcessingState &state, state.addIgnoredTypeAttr(attr); } -/// Given that there are attributes written on the declarator +/// Given that there are attributes written on the declarator or declaration /// itself, try to distribute any type attributes to the appropriate /// declarator chunk. /// @@ -675,11 +668,11 @@ static void distributeFunctionTypeAttrFromDeclarator(TypeProcessingState &state, /// int (f ATTR)(); /// but not necessarily this: /// int f() ATTR; +/// +/// `Attrs` is the attribute list containing the declaration (either of the +/// declarator or the declaration). static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, QualType &declSpecType) { - // Collect all the type attributes from the declarator itself. - assert(!state.getDeclarator().getAttributes().empty() && - "declarator has no attrs!"); // The called functions in this loop actually remove things from the current // list, so iterating over the existing list isn't possible. Instead, make a // non-owning copy and iterate over that. @@ -1792,8 +1785,42 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // list of type attributes to be temporarily saved while the type // attributes are pushed around. // pipe attributes will be handled later ( at GetFullTypeForDeclarator ) - if (!DS.isTypeSpecPipe()) + if (!DS.isTypeSpecPipe()) { + // We also apply declaration attributes that "slide" to the decl spec. + // Ordering can be important for attributes. The decalaration attributes + // come syntactically before the decl spec attributes, so we process them + // in that order. + ParsedAttributesView SlidingAttrs; + for (ParsedAttr &AL : declarator.getDeclarationAttributes()) { + if (AL.slidesFromDeclToDeclSpecLegacyBehavior()) { + SlidingAttrs.addAtEnd(&AL); + + // For standard syntax attributes, which would normally appertain to the + // declaration here, suggest moving them to the type instead. But only + // do this for our own vendor attributes; moving other vendors' + // attributes might hurt portability. + // There's one special case that we need to deal with here: The + // `MatrixType` attribute may only be used in a typedef declaration. If + // it's being used anywhere else, don't output the warning as + // ProcessDeclAttributes() will output an error anyway. + if (AL.isStandardAttributeSyntax() && AL.isClangScope() && + !(AL.getKind() == ParsedAttr::AT_MatrixType && + DS.getStorageClassSpec() != DeclSpec::SCS_typedef)) { + S.Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl) + << AL; + } + } + } + // During this call to processTypeAttrs(), + // TypeProcessingState::getCurrentAttributes() will erroneously return a + // reference to the DeclSpec attributes, rather than the declaration + // attributes. However, this doesn't matter, as getCurrentAttributes() + // is only called when distributing attributes from one attribute list + // to another. Declaration attributes are always C++11 attributes, and these + // are never distributed. + processTypeAttrs(state, Result, TAL_DeclSpec, SlidingAttrs); processTypeAttrs(state, Result, TAL_DeclSpec, DS.getAttributes()); + } // Apply const/volatile/restrict qualifiers to T. if (unsigned TypeQuals = DS.getTypeQualifiers()) { @@ -3400,8 +3427,10 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, break; } - if (!D.getAttributes().empty()) - distributeTypeAttrsFromDeclarator(state, T); + // Note: We don't need to distribute declaration attributes (i.e. + // D.getDeclarationAttributes()) because those are always C++11 attributes, + // and those don't get distributed. + distributeTypeAttrsFromDeclarator(state, T); // Find the deduced type in this type. Look in the trailing return type if we // have one, otherwise in the DeclSpec type. @@ -4702,7 +4731,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, AttrList.hasAttribute(ParsedAttr::AT_CFReturnsNotRetained); }; if (const auto *InnermostChunk = D.getInnermostNonParenChunk()) { - if (hasCFReturnsAttr(D.getAttributes()) || + if (hasCFReturnsAttr(D.getDeclarationAttributes()) || + hasCFReturnsAttr(D.getAttributes()) || hasCFReturnsAttr(InnermostChunk->getAttrs()) || hasCFReturnsAttr(D.getDeclSpec().getAttributes())) { inferNullability = NullabilityKind::Nullable; @@ -5273,7 +5303,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // function is marked with the "overloadable" attribute. Scan // for this attribute now. if (!FTI.NumParams && FTI.isVariadic && !LangOpts.CPlusPlus) - if (!D.getAttributes().hasAttribute(ParsedAttr::AT_Overloadable) && + if (!D.getDeclarationAttributes().hasAttribute( + ParsedAttr::AT_Overloadable) && + !D.getAttributes().hasAttribute(ParsedAttr::AT_Overloadable) && !D.getDeclSpec().getAttributes().hasAttribute( ParsedAttr::AT_Overloadable)) S.Diag(FTI.getEllipsisLoc(), diag::err_ellipsis_first_param); @@ -5689,7 +5721,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } - // Apply any undistributed attributes from the declarator. + // Apply any undistributed attributes from the declaration or declarator. + ParsedAttributesView NonSlidingAttrs; + for (ParsedAttr &AL : D.getDeclarationAttributes()) { + if (!AL.slidesFromDeclToDeclSpecLegacyBehavior()) { + NonSlidingAttrs.addAtEnd(&AL); + } + } + processTypeAttrs(state, T, TAL_DeclName, NonSlidingAttrs); processTypeAttrs(state, T, TAL_DeclName, D.getAttributes()); // Diagnose any ignored type attributes. @@ -8234,12 +8273,14 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, if (!IsTypeAttr) continue; } - } else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr) && - attr.getKind() != ParsedAttr::AT_AnnotateType) { + } else if (TAL != TAL_DeclSpec && TAL != TAL_DeclChunk && + !attr.isTypeAttr()) { // Otherwise, only consider type processing for a C++11 attribute if - // it's actually been applied to a type. - // We also allow C++11 address_space and annotate_type and - // OpenCL language address space attributes to pass through. + // - it has actually been applied to a type (decl-specifier-seq or + // declarator chunk), or + // - it is a type attribute, irrespective of where it was applied (so + // that we can support the legacy behavior of some type attributes + // that can be applied to the declaration name). continue; } } @@ -8257,10 +8298,14 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, break; case ParsedAttr::UnknownAttribute: - if (attr.isStandardAttributeSyntax() && TAL == TAL_DeclChunk) + if (attr.isStandardAttributeSyntax()) { state.getSema().Diag(attr.getLoc(), diag::warn_unknown_attribute_ignored) << attr << attr.getRange(); + // Mark the attribute as invalid so we don't emit the same diagnostic + // multiple times. + attr.setInvalid(); + } break; case ParsedAttr::IgnoredAttribute: @@ -8329,6 +8374,15 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, break; case ParsedAttr::AT_NoDeref: { + // FIXME: `noderef` currently doesn't work correctly in [[]] syntax. + // See https://github.com/llvm/llvm-project/issues/55790 for details. + // For the time being, we simply emit a warning that the attribute is + // ignored. + if (attr.isStandardAttributeSyntax()) { + state.getSema().Diag(attr.getLoc(), diag::warn_attribute_ignored) + << attr; + break; + } ASTContext &Ctx = state.getSema().Context; type = state.getAttributedType(createSimpleAttr(Ctx, attr), type, type); @@ -8406,6 +8460,16 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, FUNCTION_TYPE_ATTRS_CASELIST: attr.setUsedAsTypeAttr(); + // Attributes with standard syntax have strict rules for what they + // appertain to and hence should not use the "distribution" logic below. + if (attr.isStandardAttributeSyntax()) { + if (!handleFunctionTypeAttr(state, attr, type)) { + diagnoseBadTypeAttribute(state.getSema(), attr, type); + attr.setInvalid(); + } + break; + } + // Never process function type attributes as part of the // declaration-specifiers. if (TAL == TAL_DeclSpec) diff --git a/clang/test/AST/language_address_space_attribute.cpp b/clang/test/AST/language_address_space_attribute.cpp index 93a23304d9a637..fcc293ab5e705c 100644 --- a/clang/test/AST/language_address_space_attribute.cpp +++ b/clang/test/AST/language_address_space_attribute.cpp @@ -14,42 +14,63 @@ void langas() { // CHECK: VarDecl {{.*}} x_global '__global int *' __attribute__((opencl_global)) int *x_global; + // CHECK: VarDecl {{.*}} y_global '__global int *' + int [[clang::opencl_global]] *y_global; + // CHECK: VarDecl {{.*}} z_global '__global int *' [[clang::opencl_global]] int *z_global; // CHECK: VarDecl {{.*}} x_global_device '__global_device int *' __attribute__((opencl_global_device)) int *x_global_device; + // CHECK: VarDecl {{.*}} y_global_device '__global_device int *' + int [[clang::opencl_global_device]] *y_global_device; + // CHECK: VarDecl {{.*}} z_global_device '__global_device int *' [[clang::opencl_global_device]] int *z_global_device; // CHECK: VarDecl {{.*}} x_global_host '__global_host int *' __attribute__((opencl_global_host)) int *x_global_host; + // CHECK: VarDecl {{.*}} y_global_host '__global_host int *' + int [[clang::opencl_global_host]] *y_global_host; + // CHECK: VarDecl {{.*}} z_global_host '__global_host int *' [[clang::opencl_global_host]] int *z_global_host; // CHECK: VarDecl {{.*}} x_local '__local int *' __attribute__((opencl_local)) int *x_local; + // CHECK: VarDecl {{.*}} y_local '__local int *' + int [[clang::opencl_local]] *y_local; + // CHECK: VarDecl {{.*}} z_local '__local int *' [[clang::opencl_local]] int *z_local; // CHECK: VarDecl {{.*}} x_constant '__constant int *' __attribute__((opencl_constant)) int *x_constant; + // CHECK: VarDecl {{.*}} y_constant '__constant int *' + int [[clang::opencl_constant]] *y_constant; + // CHECK: VarDecl {{.*}} z_constant '__constant int *' [[clang::opencl_constant]] int *z_constant; // CHECK: VarDecl {{.*}} x_private '__private int *' __attribute__((opencl_private)) int *x_private; + // CHECK: VarDecl {{.*}} y_private '__private int *' + int [[clang::opencl_private]] *y_private; + // CHECK: VarDecl {{.*}} z_private '__private int *' [[clang::opencl_private]] int *z_private; // CHECK: VarDecl {{.*}} x_generic '__generic int *' __attribute__((opencl_generic)) int *x_generic; + // CHECK: VarDecl {{.*}} y_generic '__generic int *' + int [[clang::opencl_generic]] *y_generic; + // CHECK: VarDecl {{.*}} z_generic '__generic int *' [[clang::opencl_generic]] int *z_generic; } diff --git a/clang/test/CodeGen/attr-btf_type_tag-func.c b/clang/test/CodeGen/attr-btf_type_tag-func.c index 016e1064eb861a..a4a22f36c1bb96 100644 --- a/clang/test/CodeGen/attr-btf_type_tag-func.c +++ b/clang/test/CodeGen/attr-btf_type_tag-func.c @@ -1,9 +1,17 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -S -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -fdouble-square-bracket-attributes -debug-info-kind=limited -S -emit-llvm -o - %s | FileCheck %s +#if DOUBLE_BRACKET_ATTRS +#define __tag1 [[clang::btf_type_tag("tag1")]] +#define __tag2 [[clang::btf_type_tag("tag2")]] +#define __tag3 [[clang::btf_type_tag("tag3")]] +#define __tag4 [[clang::btf_type_tag("tag4")]] +#else #define __tag1 __attribute__((btf_type_tag("tag1"))) #define __tag2 __attribute__((btf_type_tag("tag2"))) #define __tag3 __attribute__((btf_type_tag("tag3"))) #define __tag4 __attribute__((btf_type_tag("tag4"))) +#endif int __tag1 * __tag2 *foo(int __tag1 * __tag2 *arg) { return arg; } diff --git a/clang/test/CodeGen/attr-btf_type_tag-var.c b/clang/test/CodeGen/attr-btf_type_tag-var.c index 3493d8f6d36669..0bb0a617281092 100644 --- a/clang/test/CodeGen/attr-btf_type_tag-var.c +++ b/clang/test/CodeGen/attr-btf_type_tag-var.c @@ -1,5 +1,16 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -S -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -fdouble-square-bracket-attributes -debug-info-kind=limited -S -emit-llvm -o - %s | FileCheck %s +#if DOUBLE_BRACKET_ATTRS +#define __tag1 [[clang::btf_type_tag("tag1")]] +#define __tag2 [[clang::btf_type_tag("tag2")]] +#define __tag3 [[clang::btf_type_tag("tag3")]] +#define __tag4 [[clang::btf_type_tag("tag4")]] +#define __tag5 [[clang::btf_type_tag("tag5")]] +#define __tag6 [[clang::btf_type_tag("tag6")]] + +const volatile int __tag1 __tag2 * __tag3 __tag4 const volatile * __tag5 __tag6 const volatile * g; +#else #define __tag1 __attribute__((btf_type_tag("tag1"))) #define __tag2 __attribute__((btf_type_tag("tag2"))) #define __tag3 __attribute__((btf_type_tag("tag3"))) @@ -8,6 +19,7 @@ #define __tag6 __attribute__((btf_type_tag("tag6"))) const int __tag1 __tag2 volatile * const __tag3 __tag4 volatile * __tag5 __tag6 const volatile * g; +#endif // CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L6:[0-9]+]] // CHECK: ![[L6]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L7:[0-9]+]], size: [[#]], annotations: ![[L22:[0-9]+]] diff --git a/clang/test/Frontend/noderef.c b/clang/test/Frontend/noderef.c index 6b9a1431681726..daf2d6006b2d75 100644 --- a/clang/test/Frontend/noderef.c +++ b/clang/test/Frontend/noderef.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Wno-unused-value -verify %s +// RUN: %clang_cc1 -Wno-unused-value -fdouble-square-bracket-attributes -verify %s #define NODEREF __attribute__((noderef)) @@ -221,3 +221,24 @@ int test(void) { do {} while (*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} return *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} } + +// FIXME: Currently, [[]] syntax does not work for the `noderef` atribute. +// For the time being, test that we consistently diagnose the attribute as +// ignored. +// For details see https://github.com/llvm/llvm-project/issues/55790 +void test_standard_syntax() { + [[clang::noderef]] int i; // expected-warning {{'noderef' attribute ignored}} + + [[clang::noderef]] int *p1; // expected-warning {{'noderef' attribute ignored}} + *p1; + + int *p2 [[clang::noderef]]; // expected-warning {{'noderef' attribute ignored}} + *p2; + + int * [[clang::noderef]] p3; // expected-warning {{'noderef' attribute ignored}} + *p3; + + typedef int* IntPtr; + [[clang::noderef]] IntPtr p4; // expected-warning {{'noderef' attribute ignored}} + *p4; +} diff --git a/clang/test/Parser/MicrosoftExtensions.cpp b/clang/test/Parser/MicrosoftExtensions.cpp index c625bf3d6befc2..01e59aa1945a86 100644 --- a/clang/test/Parser/MicrosoftExtensions.cpp +++ b/clang/test/Parser/MicrosoftExtensions.cpp @@ -57,6 +57,14 @@ struct __declspec(uuid("000000A0-0000-0000-C000-000000000046")) struct_with_uuid { }; struct struct_without_uuid { }; +struct base { + int a; +}; +struct derived : base { + // Can't apply a UUID to a using declaration. + [uuid("000000A0-0000-0000-C000-00000000004A")] using base::a; // expected-error {{expected member name}} +}; + struct __declspec(uuid("000000A0-0000-0000-C000-000000000049")) struct_with_uuid2; diff --git a/clang/test/Parser/asm.c b/clang/test/Parser/asm.c index 37b3ba3f46561a..480acb7b6ffd9e 100644 --- a/clang/test/Parser/asm.c +++ b/clang/test/Parser/asm.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -fdouble-square-bracket-attributes -verify %s #if !__has_extension(gnu_asm) #error Extension 'gnu_asm' should be available by default @@ -12,6 +12,9 @@ void f1(void) { void f2(void) { asm("foo" : "=r" (a)); // expected-error {{use of undeclared identifier 'a'}} asm("foo" : : "r" (b)); // expected-error {{use of undeclared identifier 'b'}} + + [[]] asm(""); + [[gnu::deprecated]] asm(""); // expected-warning {{'deprecated' attribute ignored}} } void a(void) __asm__(""); // expected-error {{cannot use an empty string literal in 'asm'}} diff --git a/clang/test/Parser/asm.cpp b/clang/test/Parser/asm.cpp index 9f64dfea476ed2..de156877ec470d 100644 --- a/clang/test/Parser/asm.cpp +++ b/clang/test/Parser/asm.cpp @@ -7,3 +7,8 @@ int foo4 asm (u"bar4"); // expected-error {{cannot use unicode string literal in int foo5 asm (U"bar5"); // expected-error {{cannot use unicode string literal in 'asm'}} int foo6 asm ("bar6"_x); // expected-error {{string literal with user-defined suffix cannot be used here}} int foo6 asm ("" L"bar7"); // expected-error {{cannot use wide string literal in 'asm'}} + +void f() { + [[]] asm(""); + [[gnu::deprecated]] asm(""); // expected-warning {{'deprecated' attribute ignored}} +} diff --git a/clang/test/Parser/attributes.c b/clang/test/Parser/attributes.c index 9f017c6958c2a7..fe22c7e98a537e 100644 --- a/clang/test/Parser/attributes.c +++ b/clang/test/Parser/attributes.c @@ -113,3 +113,10 @@ __attribute__((,,,const)) int PR38352_1(void); __attribute__((const,,,)) int PR38352_2(void); __attribute__((const,,,const)) int PR38352_3(void); __attribute__((,,,const,,,const,,,)) int PR38352_4(void); + +// Test that we allow attributes on free-standing decl-specifier-seqs. +// GCC appears to allow this. +__attribute__(()) struct t; +void f5() { + __attribute__(()) struct t; +} diff --git a/clang/test/Parser/c2x-attributes.c b/clang/test/Parser/c2x-attributes.c index e1265f73a47674..f8ed1eda148862 100644 --- a/clang/test/Parser/c2x-attributes.c +++ b/clang/test/Parser/c2x-attributes.c @@ -34,6 +34,8 @@ struct [[]] S1 { int [[]] : 0; // OK, attribute applies to the type. int p, [[]] : 0; // expected-error {{an attribute list cannot appear here}} int q, [[]] r; // expected-error {{an attribute list cannot appear here}} + [[]] int; // expected-error {{an attribute list cannot appear here}} \ + // expected-warning {{declaration does not declare anything}} }; [[]] struct S2 { int a; }; // expected-error {{misplaced attributes}} diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp index 25113d35b8544e..3cb4e180e5f5f1 100644 --- a/clang/test/Parser/cxx0x-attributes.cpp +++ b/clang/test/Parser/cxx0x-attributes.cpp @@ -126,6 +126,7 @@ class foo { void const_after_attr () [[]] const; // expected-error {{expected ';'}} }; extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}} +[[]] extern "C++" { } // expected-error {{an attribute list cannot appear here}} [[]] template void before_template_attr (); // expected-error {{an attribute list cannot appear here}} [[]] namespace ns { int i; } // expected-error {{an attribute list cannot appear here}} expected-note {{declared here}} [[]] static_assert(true, ""); //expected-error {{an attribute list cannot appear here}} @@ -421,3 +422,26 @@ class FriendClassesWithAttributes { // prefered "protected" vendor namespace. We support __clang__ only for // people expecting it to behave the same as __gnu__. [[__clang__::annotate("test")]] void annotate3(); // expected-warning {{'__clang__' is a predefined macro name, not an attribute scope specifier; did you mean '_Clang' instead?}} + +// Check ordering: C++11 attributes must appear before GNU attributes. +class Ordering { + void f1( + int ([[]] __attribute__(()) int n) + ) { + } + + void f2( + int (*)([[]] __attribute__(()) int n) + ) { + } + + void f3( + int (__attribute__(()) [[]] int n) // expected-error {{an attribute list cannot appear here}} + ) { + } + + void f4( + int (*)(__attribute__(()) [[]] int n) // expected-error {{an attribute list cannot appear here}} + ) { + } +}; diff --git a/clang/test/Sema/annotate-type.c b/clang/test/Sema/annotate-type.c index bbf26dd1793bbd..266e13885389c2 100644 --- a/clang/test/Sema/annotate-type.c +++ b/clang/test/Sema/annotate-type.c @@ -17,11 +17,8 @@ void foo(float *[[clang::annotate_type("foo")]] a) { int *__attribute__((annotate_type("bar"))) y2; // expected-warning {{unknown attribute 'annotate_type' ignored}} // Various error cases - // FIXME: We would want to prohibit the attribute on the following two lines. - // However, Clang currently generally doesn't prohibit type-only C++11 - // attributes on declarations. This should be fixed more generally. - [[clang::annotate_type("bar")]] int *z1; - int *z2 [[clang::annotate_type("bar")]]; + [[clang::annotate_type("bar")]] int *z1; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + int *z2 [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} [[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a statement}} int *[[clang::annotate_type(1)]] z3; // expected-error {{'annotate_type' attribute requires a string}} int *[[clang::annotate_type()]] z4; // expected-error {{'annotate_type' attribute takes at least 1 argument}} @@ -33,15 +30,13 @@ void foo(float *[[clang::annotate_type("foo")]] a) { } // More error cases: Prohibit adding the attribute to declarations. // Different declarations hit different code paths, so they need separate tests. -// FIXME: Clang currently generally doesn't prohibit type-only C++11 -// attributes on declarations. -[[clang::annotate_type("bar")]] int *global; -void annotated_function([[clang::annotate_type("bar")]] int); -void g([[clang::annotate_type("bar")]] int); -struct [[clang::annotate_type("foo")]] S; -struct [[clang::annotate_type("foo")]] S{ - [[clang::annotate_type("foo")]] int member; - [[clang::annotate_type("foo")]] union { +[[clang::annotate_type("bar")]] int *global; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +[[clang::annotate_type("bar")]] void annotated_function(); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +void g([[clang::annotate_type("bar")]] int); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +struct [[clang::annotate_type("foo")]] S; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +struct [[clang::annotate_type("foo")]] S{ // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + [[clang::annotate_type("foo")]] int member; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + [[clang::annotate_type("foo")]] union { // expected-error {{an attribute list cannot appear here}} int i; float f; }; diff --git a/clang/test/Sema/attr-declspec-ignored.c b/clang/test/Sema/attr-declspec-ignored.c index 6fd35c0bd78355..7e5cebf5f45c53 100644 --- a/clang/test/Sema/attr-declspec-ignored.c +++ b/clang/test/Sema/attr-declspec-ignored.c @@ -7,6 +7,16 @@ __attribute__((visibility("hidden"))) __attribute__((aligned)) union B; // expe __attribute__((visibility("hidden"))) __attribute__((aligned)) enum C {C}; // expected-warning{{attribute 'visibility' is ignored, place it after "enum" to apply attribute to type declaration}} \ // expected-warning{{attribute 'aligned' is ignored, place it after "enum" to apply attribute to type declaration}} +// Make sure that we produce the same warnings on block declarations. +void func() { + __attribute__((visibility("hidden"))) __attribute__((aligned)) struct A; // expected-warning{{attribute 'visibility' is ignored, place it after "struct" to apply attribute to type declaration}} \ + // expected-warning{{attribute 'aligned' is ignored, place it after "struct" to apply attribute to type declaration}} + __attribute__((visibility("hidden"))) __attribute__((aligned)) union B; // expected-warning{{attribute 'visibility' is ignored, place it after "union" to apply attribute to type declaration}} \ + // expected-warning{{attribute 'aligned' is ignored, place it after "union" to apply attribute to type declaration}} + __attribute__((visibility("hidden"))) __attribute__((aligned)) enum C {C}; // expected-warning{{attribute 'visibility' is ignored, place it after "enum" to apply attribute to type declaration}} \ + // expected-warning{{attribute 'aligned' is ignored, place it after "enum" to apply attribute to type declaration}} +} + __attribute__((visibility("hidden"))) __attribute__((aligned)) struct D {} d; __attribute__((visibility("hidden"))) __attribute__((aligned)) union E {} e; __attribute__((visibility("hidden"))) __attribute__((aligned)) enum F {F} f; diff --git a/clang/test/Sema/attr-regparm.c b/clang/test/Sema/attr-regparm.c index d50dd113f70f98..c18367993d65e2 100644 --- a/clang/test/Sema/attr-regparm.c +++ b/clang/test/Sema/attr-regparm.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -fdouble-square-bracket-attributes -verify %s __attribute((regparm(2))) int x0(void); __attribute((regparm(1.0))) int x1(void); // expected-error{{'regparm' attribute requires an integer constant}} @@ -9,3 +9,9 @@ __attribute((regparm(5,3))) int x4(void); // expected-error{{'regparm' attribute void __attribute__((regparm(3))) x5(int); void x5(int); // expected-note{{previous declaration is here}} void __attribute__((regparm(2))) x5(int); // expected-error{{function declared with regparm(2) attribute was previously declared with the regparm(3) attribute}} + +[[gnu::regparm(3)]] void x6(int); // expected-note{{previous declaration is here}} +[[gnu::regparm(2)]] void x6(int); // expected-error{{function declared with regparm(2) attribute was previously declared with the regparm(3) attribute}} +void x6 [[gnu::regparm(3)]] (int); +void [[gnu::regparm(3)]] x6(int); // expected-warning{{'regparm' only applies to function types; type here is 'void'}} +void x6(int) [[gnu::regparm(3)]]; // expected-warning{{GCC does not allow the 'regparm' attribute to be written on a type}} diff --git a/clang/test/Sema/matrix-type-builtins.c b/clang/test/Sema/matrix-type-builtins.c index c58384613e79c1..afe85d31a0980f 100644 --- a/clang/test/Sema/matrix-type-builtins.c +++ b/clang/test/Sema/matrix-type-builtins.c @@ -1,10 +1,26 @@ -// RUN: %clang_cc1 %s -fenable-matrix -pedantic -verify -triple=x86_64-apple-darwin9 +// RUN: %clang_cc1 %s -fenable-matrix -fdouble-square-bracket-attributes -pedantic -verify -triple=x86_64-apple-darwin9 typedef float sx5x10_t __attribute__((matrix_type(5, 10))); typedef int ix3x2_t __attribute__((matrix_type(3, 2))); typedef double dx3x3 __attribute__((matrix_type(3, 3))); typedef unsigned ix3x3 __attribute__((matrix_type(3, 3))); +// Verify that we can use the [[]] spelling of the attribute. +// We intentionally use the same type alias name to check that both versions +// define the same type. +typedef float [[clang::matrix_type(5, 10)]] sx5x10_t; +typedef int [[clang::matrix_type(3, 2)]] ix3x2_t; +[[clang::matrix_type(5, 10)]] typedef float sx5x10_t; +// expected-warning@-1 {{applying attribute 'matrix_type' to a declaration is deprecated; apply it to the type instead}} +[[clang::matrix_type(3, 2)]] typedef int ix3x2_t; +// expected-warning@-1 {{applying attribute 'matrix_type' to a declaration is deprecated; apply it to the type instead}} + +// Attribute may not be used outside typedefs. +[[clang::matrix_type(3, 2)]] int ix3x2_var; +// expected-error@-1 {{'matrix_type' attribute only applies to typedefs}} +int [[clang::matrix_type(3, 2)]] ix3x2_var; +// expected-error@-1 {{'matrix_type' attribute only applies to typedefs}} + void transpose(sx5x10_t a, ix3x2_t b, dx3x3 c, int *d, int e) { a = __builtin_matrix_transpose(b); // expected-error@-1 {{assigning to 'sx5x10_t' (aka 'float __attribute__((matrix_type(5, 10)))') from incompatible type 'int __attribute__((matrix_type(2, 3)))'}} diff --git a/clang/test/Sema/neon-vector-types.c b/clang/test/Sema/neon-vector-types.c index 03f669eb8416cf..c79c73919a927f 100644 --- a/clang/test/Sema/neon-vector-types.c +++ b/clang/test/Sema/neon-vector-types.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 %s -triple armv7 -target-feature +neon -fsyntax-only -verify -// RUN: %clang_cc1 %s -triple armv8 -target-feature +neon -fsyntax-only -verify +// RUN: %clang_cc1 %s -triple armv7 -target-feature +neon -fsyntax-only -fdouble-square-bracket-attributes -verify +// RUN: %clang_cc1 %s -triple armv8 -target-feature +neon -fsyntax-only -fdouble-square-bracket-attributes -verify typedef float float32_t; typedef signed char poly8_t; @@ -16,6 +16,16 @@ typedef __attribute__((neon_vector_type(4))) float32_t float32x4_t; typedef __attribute__((neon_polyvector_type(16))) poly8_t poly8x16_t; typedef __attribute__((neon_polyvector_type(8))) poly16_t poly16x8_t; +// Verify that we can use the [[]] spelling of the attributes. +// We intentionally use the same type alias names to check that both versions +// define the same type. +typedef int [[clang::neon_vector_type(2)]] int32x2_t; +typedef poly8_t [[clang::neon_polyvector_type(16)]] poly8x16_t; + +// Verify that we can use the attributes outside of a typedef. +int [[clang::neon_vector_type(2)]] int32x2_var; +poly8_t [[clang::neon_polyvector_type(16)]] poly8x16_var; + // The attributes must have a single argument. typedef __attribute__((neon_vector_type(2, 4))) int only_one_arg; // expected-error{{'neon_vector_type' attribute takes one argument}} diff --git a/clang/test/Sema/overload-arm-mve.c b/clang/test/Sema/overload-arm-mve.c index 3b1a4430747f12..9ad1bfcc882f76 100644 --- a/clang/test/Sema/overload-arm-mve.c +++ b/clang/test/Sema/overload-arm-mve.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple thumbv8.1m.main-none-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -Werror -emit-llvm -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple thumbv8.1m.main-none-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -verify -fsyntax-only -DERROR_CHECK %s +// RUN: %clang_cc1 -triple thumbv8.1m.main-none-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -fdouble-square-bracket-attributes -Werror -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple thumbv8.1m.main-none-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -fdouble-square-bracket-attributes -verify -fsyntax-only -DERROR_CHECK %s typedef signed short int16_t; typedef signed int int32_t; @@ -15,6 +15,14 @@ typedef __attribute__((neon_vector_type(8), __clang_arm_mve_strict_polymorphism) typedef __attribute__((neon_vector_type(4), __clang_arm_mve_strict_polymorphism)) uint32_t uint32x4_t; typedef __attribute__((neon_vector_type(2), __clang_arm_mve_strict_polymorphism)) uint64_t uint64x2_t; +// Verify that we can use the [[]] spelling of the attribute. +// We intentionally use the same type alias name to check that both versions +// define the same type. +typedef int16_t [[clang::neon_vector_type(8), clang::__clang_arm_mve_strict_polymorphism]] int16x8_t; + +// Verify that we can use the attribute outside of a typedef. +void test_param(int16_t [[clang::neon_vector_type(8), clang::__clang_arm_mve_strict_polymorphism]] int16x8); + __attribute__((overloadable)) int overload(int16x8_t x, int16_t y); // expected-note {{candidate function}} __attribute__((overloadable)) diff --git a/clang/test/Sema/vector-gcc-compat.c b/clang/test/Sema/vector-gcc-compat.c index 78132f7f096352..36feb7fd1422c9 100644 --- a/clang/test/Sema/vector-gcc-compat.c +++ b/clang/test/Sema/vector-gcc-compat.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only -Weverything -Wno-unused-but-set-variable -triple x86_64-apple-darwin10 +// RUN: %clang_cc1 %s -verify -fsyntax-only -fdouble-square-bracket-attributes -Weverything -Wno-unused-but-set-variable -triple x86_64-apple-darwin10 // Test the compatibility of clang's vector extensions with gcc's vector // extensions for C. Notably &&, ||, ?: and ! are not available. @@ -17,6 +17,32 @@ typedef double v2f64 __attribute__((vector_size(16))); typedef double v4f64 __attribute__((vector_size(32))); typedef int v4i32 __attribute((vector_size(16))); +// Verify that we can use the [[]] spelling of the attribute. +// We intentionally use the same type alias name to check that both versions +// define the same type. +// FIXME: Warnings are nuisance warnings due to the `-Weverything` flag, but +// we shouldn't really be emitting them in C mode with the +// `-fdouble-square-bracket-attributes` flag. +typedef long long v2i64 [[gnu::vector_size(16)]]; // expected-warning{{C++11 attribute syntax is incompatible with C++98}} +typedef int v2i32 [[gnu::vector_size(8)]]; // expected-warning{{C++11 attribute syntax is incompatible with C++98}} + +// Check various positions where the [[]] spelling can or cannot be used. +[[gnu::vector_size(16)]] typedef long long v2i64; // expected-warning{{C++11 attribute syntax is incompatible with C++98}} +typedef long long [[gnu::vector_size(16)]] v2i64_ignored; + // expected-warning@-1{{'vector_size' attribute ignored}} + // expected-warning@-2{{C++11 attribute syntax is incompatible with C++98}} +// FIXME: Contrary to the error message that we emit, GCC does actually allow +// the attribute in the following position. Somewhat surprisingly, the attribute +// is applied not to the pointer but to the base type, i.e. this declaration has +// the same effect in GCC as the other declarations for `v2i64`. +typedef long long *[[gnu::vector_size(16)]] v2i64_doesnt_work; + // expected-error@-1{{invalid vector element type 'long long *'}} + // expected-warning@-2{{GCC does not allow the 'vector_size' attribute to be written on a type}} + // expected-warning@-3{{C++11 attribute syntax is incompatible with C++98}} + +// Verify that we can use the attribute outside of a typedef. +static int v2i32_var [[gnu::vector_size(8)]]; // expected-warning{{C++11 attribute syntax is incompatible with C++98}} + void arithmeticTest(void); void logicTest(void); void comparisonTest(void); diff --git a/clang/test/SemaCXX/address-space-placement.cpp b/clang/test/SemaCXX/address-space-placement.cpp new file mode 100644 index 00000000000000..2d8b94041259c8 --- /dev/null +++ b/clang/test/SemaCXX/address-space-placement.cpp @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify %s + +// Check that we emit the correct warnings in various situations where the C++11 +// spelling of the `address_space` attribute is applied to a declaration instead +// of a type. Also check that the attribute can instead be applied to the type. + +void f([[clang::address_space(1)]] int* param) { // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + [[clang::address_space(1)]] int* local1; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + int* local2 [[clang::address_space(1)]]; // expected-error {{automatic variable qualified with an address space}} expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + int [[clang::address_space(1)]] * local3; + int* [[clang::address_space(1)]] local4; // expected-error {{automatic variable qualified with an address space}} + + for ([[clang::address_space(1)]] int* p = nullptr; p; ++p) {} // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + for (; [[clang::address_space(1)]] int* p = nullptr; ) {} // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + while([[clang::address_space(1)]] int* p = nullptr) {} // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + if ([[clang::address_space(1)]] int* p = nullptr) {} // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + try { + } catch([[clang::address_space(1)]] int& i) { // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + } + + for (int [[clang::address_space(1)]] * p = nullptr; p; ++p) {} + for (; int [[clang::address_space(1)]] * p = nullptr; ) {} + while(int [[clang::address_space(1)]] * p = nullptr) {} + if (int [[clang::address_space(1)]] * p = nullptr) {} + try { + } catch(int [[clang::address_space(1)]] & i) { + } +} + +[[clang::address_space(1)]] int* return_value(); // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +int [[clang::address_space(1)]] * return_value(); + +[[clang::address_space(1)]] int global1; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +int global2 [[clang::address_space(1)]]; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +int [[clang::address_space(1)]] global3; +int [[clang::address_space(1)]] global4; + +struct [[clang::address_space(1)]] S { // expected-error {{'address_space' attribute cannot be applied to a declaration}} + [[clang::address_space(1)]] int* member_function_1(); // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + int [[clang::address_space(1)]] * member_function_2(); +}; + +template +[[clang::address_space(1)]] T var_template_1; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +template +T [[clang::address_space(1)]] var_template_2; + +using void_ptr [[clang::address_space(1)]] = void *; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +// Intentionally using the same alias name to check that the aliases define the +// same type. +using void_ptr = void [[clang::address_space(1)]] *; + +namespace N {} +[[clang::address_space(1)]] using namespace N; // expected-error {{'address_space' attribute cannot be applied to a declaration}} diff --git a/clang/test/SemaCXX/annotate-type.cpp b/clang/test/SemaCXX/annotate-type.cpp index c76b1767e7d3c0..545b3ccfdef8e1 100644 --- a/clang/test/SemaCXX/annotate-type.cpp +++ b/clang/test/SemaCXX/annotate-type.cpp @@ -2,10 +2,7 @@ struct S1 { void f() [[clang::annotate_type("foo")]]; - // FIXME: We would want to prohibit the attribute in the following location. - // However, Clang currently generally doesn't prohibit type-only C++11 - // attributes on declarations. This should be fixed more generally. - [[clang::annotate_type("foo")]] void g(); + [[clang::annotate_type("foo")]] void g(); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} }; template struct is_same { @@ -48,23 +45,21 @@ void f3() { // More error cases: Prohibit adding the attribute to declarations. // Different declarations hit different code paths, so they need separate tests. -// FIXME: Clang currently generally doesn't prohibit type-only C++11 -// attributes on declarations. -namespace [[clang::annotate_type("foo")]] my_namespace {} -struct [[clang::annotate_type("foo")]] S3; -struct [[clang::annotate_type("foo")]] S3{ - [[clang::annotate_type("foo")]] int member; +namespace [[clang::annotate_type("foo")]] my_namespace {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +struct [[clang::annotate_type("foo")]] S3; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +struct [[clang::annotate_type("foo")]] S3{ // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + [[clang::annotate_type("foo")]] int member; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} }; void f4() { - for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {} - for (; [[clang::annotate_type("foo")]] bool b = false;) {} - while ([[clang::annotate_type("foo")]] bool b = false) {} - if ([[clang::annotate_type("foo")]] bool b = false) {} + for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + for (; [[clang::annotate_type("foo")]] bool b = false;) {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + while ([[clang::annotate_type("foo")]] bool b = false) {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} + if ([[clang::annotate_type("foo")]] bool b = false) {} // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} try { - } catch ([[clang::annotate_type("foo")]] int i) { + } catch ([[clang::annotate_type("foo")]] int i) { // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} } } template -[[clang::annotate_type("foo")]] T var_template; -[[clang::annotate_type("foo")]] extern "C" int extern_c_func(); -extern "C" [[clang::annotate_type("foo")]] int extern_c_func(); +[[clang::annotate_type("foo")]] T var_template; // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} +[[clang::annotate_type("foo")]] extern "C" int extern_c_func(); // expected-error {{an attribute list cannot appear here}} +extern "C" [[clang::annotate_type("foo")]] int extern_c_func(); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} diff --git a/clang/test/SemaObjC/attr-objc-gc.m b/clang/test/SemaObjC/attr-objc-gc.m index 303dce0d8752b2..e9dcbd993cb36f 100644 --- a/clang/test/SemaObjC/attr-objc-gc.m +++ b/clang/test/SemaObjC/attr-objc-gc.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -fdouble-square-bracket-attributes -verify %s static id __attribute((objc_gc(weak))) a; static id __attribute((objc_gc(strong))) b; @@ -18,6 +18,10 @@ /* expected-warning {{'__weak' only applies to Objective-C object or block pointer types; type here is 'int'}}*/ static __we\ ak int i; +static id [[clang::objc_gc(weak)]] j; +[[clang::objc_gc(weak)]] static id k; // expected-warning {{applying attribute 'objc_gc' to a declaration is deprecated; apply it to the type instead}} +static id l [[clang::objc_gc(weak)]]; // expected-warning {{applying attribute 'objc_gc' to a declaration is deprecated; apply it to the type instead}} + // rdar://problem/9126213 void test2(id __attribute((objc_gc(strong))) *strong, id __attribute((objc_gc(weak))) *weak) { diff --git a/clang/test/SemaOpenCL/address-spaces.cl b/clang/test/SemaOpenCL/address-spaces.cl index 41f5a336532545..70b6d0753528ee 100644 --- a/clang/test/SemaOpenCL/address-spaces.cl +++ b/clang/test/SemaOpenCL/address-spaces.cl @@ -266,9 +266,9 @@ void func_multiple_addr2(void) { __attribute__((opencl_private)) private_int_t var5; // expected-warning {{multiple identical address spaces specified for type}} __attribute__((opencl_private)) private_int_t *var6; // expected-warning {{multiple identical address spaces specified for type}} #if __OPENCL_CPP_VERSION__ - [[clang::opencl_private]] __global int var7; // expected-error {{multiple address spaces specified for type}} - [[clang::opencl_private]] __global int *var8; // expected-error {{multiple address spaces specified for type}} - [[clang::opencl_private]] private_int_t var9; // expected-warning {{multiple identical address spaces specified for type}} - [[clang::opencl_private]] private_int_t *var10; // expected-warning {{multiple identical address spaces specified for type}} + __global int [[clang::opencl_private]] var7; // expected-error {{multiple address spaces specified for type}} + __global int [[clang::opencl_private]] *var8; // expected-error {{multiple address spaces specified for type}} + private_int_t [[clang::opencl_private]] var9; // expected-warning {{multiple identical address spaces specified for type}} + private_int_t [[clang::opencl_private]] *var10; // expected-warning {{multiple identical address spaces specified for type}} #endif // !__OPENCL_CPP_VERSION__ } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index fae821f265e5ad..1a73b300cf9dff 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3739,7 +3739,7 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { if (!StmtSubjects.empty()) { OS << "bool diagAppertainsToDecl(Sema &S, const ParsedAttr &AL, "; OS << "const Decl *D) const override {\n"; - OS << " S.Diag(AL.getLoc(), diag::err_stmt_attribute_invalid_on_decl)\n"; + OS << " S.Diag(AL.getLoc(), diag::err_attribute_invalid_on_decl)\n"; OS << " << AL << D->getLocation();\n"; OS << " return false;\n"; OS << "}\n\n";