Index: clang/include/clang/Basic/AttributeCommonInfo.h =================================================================== --- clang/include/clang/Basic/AttributeCommonInfo.h +++ clang/include/clang/Basic/AttributeCommonInfo.h @@ -146,6 +146,7 @@ bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; } bool isGNUScope() const; + bool isClangScope() const; bool isAlignasAttribute() const { // FIXME: Use a better mechanism to determine this. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3381,8 +3381,11 @@ "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 " Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2316,6 +2316,7 @@ 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, @@ -2611,6 +2612,9 @@ void stripTypeAttributesOffDeclSpec(ParsedAttributes &Attrs, DeclSpec &DS, Sema::TagUseKind TUK); + void SlideAttrsToDeclSpec(ParsedAttributes &DeclAttrs, + DeclSpec &DS); + // FixItLoc = possible correct location for the attributes void ProhibitAttributes(ParsedAttributes &Attrs, SourceLocation FixItLoc = SourceLocation()) { @@ -3015,7 +3019,8 @@ unsigned int index, SourceLocation &InlineLoc, ParsedAttributes &attrs, BalancedDelimiterTracker &Tracker); - Decl *ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context); + Decl *ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &DeclAttrs); Decl *ParseExportDeclaration(); DeclGroupPtrTy ParseUsingDirectiveOrDeclaration( DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, Index: clang/include/clang/Parse/RAIIObjectsForParser.h =================================================================== --- clang/include/clang/Parse/RAIIObjectsForParser.h +++ clang/include/clang/Parse/RAIIObjectsForParser.h @@ -459,6 +459,35 @@ } void skipToEnd(); }; + + // Helper class that allows "lending" a list of attributes to a Declarator, + // then taking it back later. + // This is not strictly an RAII class, as the destructor does not + // automatically return the attributes, but it is related in spirit to the + // other classes in this file. + class AttributeLender { + public: + // Transfers all attributes in `Attrs` to `D` and records which attributes + // were transferred. + void lend(ParsedAttributes &Attrs, Declarator &D) { + for (ParsedAttr &AL : Attrs) { + LentAttrs.push_back(&AL); + } + D.takeAttributes(Attrs); + } + + // Takes the attributes that a previous call to `lend` transferred to `D` + // and transfers them back to `Attrs`. + void giveBack(ParsedAttributes &Attrs, Declarator &D) { + for (ParsedAttr *AL : LentAttrs) { + Attrs.takeOneFrom(D.getAttributes(), AL); + } + LentAttrs.clear(); + } + + private: + llvm::SmallVector LentAttrs; + }; } // end namespace clang #endif Index: clang/include/clang/Sema/ParsedAttr.h =================================================================== --- clang/include/clang/Sema/ParsedAttr.h +++ clang/include/clang/Sema/ParsedAttr.h @@ -651,6 +651,16 @@ bool isKnownToGCC() const; bool isSupportedByPragmaAttribute() const; + /// Returns whether the 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). + /// Attributes with GNU, __declspec or keyword syntax generally slide + /// to the decl-specifier-seq. C++11 attributes specified ahead of the + /// declaration always appertain to the declaration according to the standard, + /// but historically we have allowed some of these attributes to slide to + /// the decl-specifier-seq too, so we need to keep supporting this behavior. + bool slidesFromDeclToDeclSpec() 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 @@ -906,6 +916,22 @@ ParsedAttr &operator[](SizeType pos) { return *AttrList[pos]; } const ParsedAttr &operator[](SizeType pos) const { return *AttrList[pos]; } + void updateRange() { + Range = SourceRange(); + for (const ParsedAttr &PA : *this) { + if (Range.isInvalid()) { + Range = PA.getRange(); + continue; + } + if (PA.getRange().getBegin() < Range.getBegin()) { + Range.setBegin(PA.getRange().getBegin()); + } + if (PA.getRange().getEnd() > Range.getEnd()) { + Range.setEnd(PA.getRange().getEnd()); + } + } + } + void addAtEnd(ParsedAttr *newAttr) { assert(newAttr); AttrList.push_back(newAttr); Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4423,8 +4423,38 @@ // 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); Index: clang/lib/Basic/Attributes.cpp =================================================================== --- clang/lib/Basic/Attributes.cpp +++ clang/lib/Basic/Attributes.cpp @@ -85,6 +85,10 @@ return ScopeName && (ScopeName->isStr("gnu") || ScopeName->isStr("__gnu__")); } +bool AttributeCommonInfo::isClangScope() const { + return ScopeName && (ScopeName->isStr("clang") || ScopeName->isStr("_Clang")); +} + #include "clang/Sema/AttrParsedAttrKinds.inc" static SmallString<64> normalizeName(const IdentifierInfo *Name, Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -1732,6 +1732,38 @@ } } +// Given attributes `DeclAttrs` that were specified ahead of a declaration, +// identifies those attributes that should "slide" to the decl-specifier-seq and +// moves them to `DS`. +// If `DS` already contains attributes, the "sliding" attributes are added at +// the beginning of the list; this is the same behavior as +// DeclSpec::takeAttributesFrom(). +void Parser::SlideAttrsToDeclSpec(ParsedAttributes &DeclAttrs, + DeclSpec &DS) { + llvm::SmallVector ToBeMoved; + for (ParsedAttr &AL : DeclAttrs) { + if (AL.slidesFromDeclToDeclSpec()) { + ToBeMoved.push_back(&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. + if (AL.isStandardAttributeSyntax() && AL.isClangScope()) { + Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl) << AL; + } + } + } + // We need to move sliding attributes to an intermediate list so that we can + // add them to the beginning of the `DS` attributes; doing + // `DS.getAttributes().takeOneFrom()` directly would add them to the end. + ParsedAttributes AttrsToMove(AttrFactory); + for (ParsedAttr *AL : ToBeMoved) { + AttrsToMove.takeOneFrom(DeclAttrs, AL); + } + DS.takeAttributesFrom(AttrsToMove); +} + /// ParseDeclaration - Parse a full 'declaration', which consists of /// declaration-specifiers, some number of declarators, and a semicolon. /// 'Context' should be a DeclaratorContext value. This returns the @@ -1850,8 +1882,8 @@ if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); - DS.takeAttributesFrom(Attrs); - return ParseDeclGroup(DS, Context, &DeclEnd, FRI); + SlideAttrsToDeclSpec(Attrs, DS); + return ParseDeclGroup(DS, Context, Attrs, &DeclEnd, FRI); } /// Returns true if this might be the start of a declarator, or a common typo @@ -2006,10 +2038,13 @@ /// result. Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &Attrs, SourceLocation *DeclEnd, ForRangeInit *FRI) { // Parse the first declarator. ParsingDeclarator D(*this, DS, Context); + AttributeLender AttrLender; + AttrLender.lend(Attrs, D); ParseDeclarator(D); // Bail out if the first declarator didn't seem well-formed. @@ -2172,7 +2207,9 @@ } // Parse the next declarator. + AttrLender.giveBack(Attrs, D); D.clear(); + AttrLender.lend(Attrs, D); D.setCommaLoc(CommaLoc); // Accept attributes in an init-declarator. In the first declarator in a @@ -4300,7 +4337,7 @@ // Parse leading attributes. ParsedAttributes Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); - DS.takeAttributesFrom(Attrs); + SlideAttrsToDeclSpec(Attrs, DS); // Parse the common specifier-qualifiers-list piece. ParseSpecifierQualifierList(DS); @@ -4308,6 +4345,12 @@ // 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 draft 6.7.2.1/9 : "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." + Attrs.updateRange(); + ProhibitAttributes(Attrs); RecordDecl *AnonRecord = nullptr; Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, DS, AnonRecord); @@ -4321,6 +4364,8 @@ SourceLocation CommaLoc; while (true) { ParsingFieldDeclarator DeclaratorInfo(*this, DS); + AttributeLender AttrLender; + AttrLender.lend(Attrs, DeclaratorInfo.D); DeclaratorInfo.D.setCommaLoc(CommaLoc); // Attributes are only allowed here on successive declarators. @@ -4361,6 +4406,8 @@ return; FirstDeclarator = false; + + AttrLender.giveBack(Attrs, DeclaratorInfo.D); } } @@ -6953,20 +7000,24 @@ // Just use the ParsingDeclaration "scope" of the declarator. DeclSpec DS(AttrFactory); - // Parse any C++11 attributes. - MaybeParseCXX11Attributes(DS.getAttributes()); - - // Skip any Microsoft attributes before a param. - MaybeParseMicrosoftAttributes(DS.getAttributes()); - - SourceLocation DSStart = Tok.getLocation(); + ParsedAttributes ArgAttrs(AttrFactory); // 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); + ArgAttrs.takeAllFrom(FirstArgAttrs); + + // Parse any C++11 attributes. + MaybeParseCXX11Attributes(ArgAttrs); + + // Skip any Microsoft attributes before a param. + MaybeParseMicrosoftAttributes(ArgAttrs); + + SlideAttrsToDeclSpec(ArgAttrs, DS); + + SourceLocation DSStart = Tok.getLocation(); ParseDeclarationSpecifiers(DS); @@ -6980,6 +7031,7 @@ : DeclaratorCtx == DeclaratorContext::LambdaExpr ? DeclaratorContext::LambdaExprParameter : DeclaratorContext::Prototype); + ParmDeclarator.takeAttributes(ArgAttrs); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -345,7 +345,8 @@ /// 'extern' string-literal '{' declaration-seq[opt] '}' /// 'extern' string-literal declaration /// -Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) { +Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context, + ParsedAttributes &DeclAttrs) { assert(isTokenStringLiteral() && "Not a string literal!"); ExprResult Lang = ParseStringLiteralExpression(false); @@ -357,8 +358,7 @@ getCurScope(), DS.getSourceRange().getBegin(), Lang.get(), Tok.is(tok::l_brace) ? Tok.getLocation() : SourceLocation()); - ParsedAttributes attrs(AttrFactory); - MaybeParseCXX11Attributes(attrs); + MaybeParseCXX11Attributes(DeclAttrs); if (Tok.isNot(tok::l_brace)) { // Reset the source range in DS, as the leading "extern" @@ -367,7 +367,7 @@ 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; @@ -375,7 +375,7 @@ DS.abort(); - ProhibitAttributes(attrs); + ProhibitAttributes(DeclAttrs); BalancedDelimiterTracker T(*this, tok::l_brace); T.consumeOpen(); @@ -2680,7 +2680,6 @@ } ParsedAttributes attrs(AttrFactory); - ParsedAttributesView FnAttrs; // Optional C++11 attribute-specifier MaybeParseCXX11Attributes(attrs); @@ -2690,11 +2689,6 @@ 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); if (Tok.is(tok::kw_using)) { @@ -2725,7 +2719,27 @@ // decl-specifier-seq: // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this, TemplateDiags); - DS.takeAttributesFrom(attrs); + SlideAttrsToDeclSpec(attrs, DS); + + // Remember the list of "sliding" attributes before more attributes are + // potentially added to the `DeclSpec`. We need this list to be able to issue + // diagnostics below. We don't include the declaration attributes that have + // remained in `attrs` because diagnostics for those will be issued elsewhere. + ParsedAttributesView FnAttrs; + FnAttrs.addAll(DS.getAttributes().begin(), DS.getAttributes().end()); + // Update the `SourceRange` for `FnAttrs` because we will need that below to + // decide whether any attributes were specified and whether we should + // therefore issue a diagnostic. + // There's an important special case here: If the code contained an empty + // C++11 attribute-specifier-seq (`[[]]`), `attrs` will be empty, but + // `attrs.Range` will not, and we similarly want FnAttrs.Range to be + // non-empty. + if (attrs.empty()) { + FnAttrs.Range = attrs.Range; + } else { + FnAttrs.updateRange(); + } + if (MalformedTypeSpec) DS.SetTypeSpecError(); @@ -2777,6 +2791,8 @@ } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::Member); + AttributeLender AttrLender; + AttrLender.lend(attrs, DeclaratorInfo); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; @@ -3075,7 +3091,9 @@ } // Parse the next declarator. + AttrLender.giveBack(attrs, DeclaratorInfo); DeclaratorInfo.clear(); + AttrLender.lend(attrs, DeclaratorInfo); VS.clear(); BitfieldSize = ExprResult(/*Invalid=*/false); EqualLoc = PureSpecLoc = SourceLocation(); Index: clang/lib/Parse/ParseExprCXX.cpp =================================================================== --- clang/lib/Parse/ParseExprCXX.cpp +++ clang/lib/Parse/ParseExprCXX.cpp @@ -2079,11 +2079,12 @@ // type-specifier-seq DeclSpec DS(AttrFactory); - DS.takeAttributesFrom(attrs); + SlideAttrsToDeclSpec(attrs, DS); ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_condition); // declarator Declarator DeclaratorInfo(DS, DeclaratorContext::Condition); + DeclaratorInfo.takeAttributes(attrs); ParseDeclarator(DeclaratorInfo); // simple-asm-expr[opt] Index: clang/lib/Parse/ParseStmt.cpp =================================================================== --- clang/lib/Parse/ParseStmt.cpp +++ clang/lib/Parse/ParseStmt.cpp @@ -2584,12 +2584,13 @@ MaybeParseCXX11Attributes(Attributes); DeclSpec DS(AttrFactory); - DS.takeAttributesFrom(Attributes); + SlideAttrsToDeclSpec(Attributes, DS); if (ParseCXXTypeSpecifierSeq(DS)) return StmtError(); Declarator ExDecl(DS, DeclaratorContext::CXXCatch); + ExDecl.takeAttributes(Attributes); ParseDeclarator(ExDecl); ExceptionDecl = Actions.ActOnExceptionDeclarator(getCurScope(), ExDecl); } else Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ clang/lib/Parse/ParseTemplate.cpp @@ -243,10 +243,11 @@ if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) ProhibitAttributes(prefixAttrs); else - DS.takeAttributesFrom(prefixAttrs); + SlideAttrsToDeclSpec(prefixAttrs, DS); // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + DeclaratorInfo.takeAttributes(prefixAttrs); if (TemplateInfo.TemplateParams) DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -1112,8 +1112,6 @@ 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 +1126,7 @@ } DS.abort(); + DS.takeAttributesFrom(Attrs); const char *PrevSpec = nullptr; unsigned DiagID; @@ -1145,17 +1144,19 @@ ParseObjCAtInterfaceDeclaration(AtLoc, DS.getAttributes())); } + SlideAttrsToDeclSpec(Attrs, DS); + // If the declspec consisted only of 'extern' and we have a string // literal following it, this must be a C++ linkage specifier like // 'extern "C"'. if (getLangOpts().CPlusPlus && isTokenStringLiteral() && DS.getStorageClassSpec() == DeclSpec::SCS_extern && DS.getParsedSpecifiers() == DeclSpec::PQ_StorageClassSpecifier) { - Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File); + Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File, Attrs); return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, DeclaratorContext::File); + return ParseDeclGroup(DS, DeclaratorContext::File, Attrs); } Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( Index: clang/lib/Sema/ParsedAttr.cpp =================================================================== --- clang/lib/Sema/ParsedAttr.cpp +++ clang/lib/Sema/ParsedAttr.cpp @@ -212,6 +212,38 @@ return getInfo().IsSupportedByPragmaAttribute; } +bool ParsedAttr::slidesFromDeclToDeclSpec() const { + if (!isStandardAttributeSyntax()) + return true; + + // We have historically allowed some 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. + 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_Regparm: + case AT_NoDeref: + case AT_ObjCGC: + case AT_VectorSize: + case AT_MatrixType: + return true; + default: + return false; + } +} + bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; } unsigned ParsedAttr::getSemanticSpelling() const { Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -8312,15 +8312,15 @@ /// 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) + if (AL.isCXX11Attribute() && !Options.IncludeCXX11Attributes) return; // Unknown attributes are automatically warned on. Target-specific attributes @@ -8353,14 +8353,29 @@ 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.slidesFromDeclToDeclSpec()) { + if (AL.isStandardAttributeSyntax() && AL.isClangScope()) { + // 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. + S.Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl) + << AL << D->getLocation(); + } + // GNU type attributes are handled elsewhere; 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: @@ -9004,14 +9019,14 @@ /// 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 @@ -9099,7 +9114,9 @@ AccessSpecDecl *ASDecl, const ParsedAttributesView &AttrList) { for (const ParsedAttr &AL : AttrList) { if (AL.getKind() == ParsedAttr::AT_Annotate) { - ProcessDeclAttribute(*this, nullptr, ASDecl, AL, AL.isCXX11Attribute()); + ProcessDeclAttributeOptions Options; + Options.IncludeCXX11Attributes = AL.isCXX11Attribute(); + ProcessDeclAttribute(*this, nullptr, ASDecl, AL, Options); } else { Diag(AL.getLoc(), diag::err_only_annotate_after_access_spec); return true; @@ -9241,16 +9258,22 @@ /// specified in many different places, and we need to find and apply them all. void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // 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().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()); Index: clang/test/Sema/annotate-type.c =================================================================== --- clang/test/Sema/annotate-type.c +++ clang/test/Sema/annotate-type.c @@ -17,11 +17,8 @@ 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 @@ } // 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; }; Index: clang/test/SemaCXX/address-space-placement.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/address-space-placement.cpp @@ -0,0 +1,32 @@ +// 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. + +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}} + + 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}} + } +} + +[[clang::address_space(1)]] int* return_value(); // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + +[[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}} + +struct [[clang::address_space(1)]] S { // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + [[clang::address_space(1)]] int* member_function(); // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} +}; + +template +[[clang::address_space(1)]] T var_template; // expected-warning {{applying attribute 'address_space' to a declaration is deprecated; apply it to the type instead}} + +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}} Index: clang/test/SemaCXX/annotate-type.cpp =================================================================== --- clang/test/SemaCXX/annotate-type.cpp +++ 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 @@ // 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 {{'annotate_type' attribute cannot be applied to a declaration}} +extern "C" [[clang::annotate_type("foo")]] int extern_c_func(); // expected-error {{'annotate_type' attribute cannot be applied to a declaration}} Index: clang/test/SemaOpenCL/address-spaces.cl =================================================================== --- clang/test/SemaOpenCL/address-spaces.cl +++ clang/test/SemaOpenCL/address-spaces.cl @@ -266,9 +266,9 @@ __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__ } Index: clang/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangAttrEmitter.cpp +++ clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3734,7 +3734,7 @@ 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";