Index: clang/docs/InternalsManual.rst =================================================================== --- clang/docs/InternalsManual.rst +++ clang/docs/InternalsManual.rst @@ -3033,6 +3033,13 @@ the ``AdditionalMembers`` field specifies code to be copied verbatim into the semantic attribute class object, with ``public`` access. +If two or more attributes cannot be used in combination on the same declaration +or statement, a ``MutualExclusions`` definition can be supplied to automatically +generate diagnostic code. This will disallow the attribute combinations +regardless of spellings used. Additionally, it will diagnose combinations within +the same attribute list, different attribute list, and redeclarations, as +appropriate. + Boilerplate ^^^^^^^^^^^ All semantic processing of declaration attributes happens in `lib/Sema/SemaDeclAttr.cpp Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -554,6 +554,11 @@ list Documentation; } +/// Used to define a set of mutually exclusive attributes. +class MutualExclusions Ex> { + list Exclusions = Ex; +} + /// A type attribute is not processed on a declaration or a statement. class TypeAttr : Attr; @@ -918,6 +923,7 @@ let Spellings = [Clang<"cf_audited_transfer">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } // cf_unknown_transfer is an explicit opt-out of cf_audited_transfer. @@ -927,7 +933,9 @@ let Spellings = [Clang<"cf_unknown_transfer">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } +def : MutualExclusions<[CFAuditedTransfer, CFUnknownTransfer]>; def CFReturnsRetained : InheritableAttr { let Spellings = [Clang<"cf_returns_retained">]; @@ -1009,6 +1017,7 @@ let Spellings = [GCC<"cold">]; let Subjects = SubjectList<[Function]>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } def Common : InheritableAttr { @@ -1094,6 +1103,7 @@ let Subjects = SubjectList<[CXXRecord]>; let Documentation = [CUDADeviceBuiltinSurfaceTypeDocs]; let MeaningfulToClassTemplateDefinition = 1; + let SimpleHandler = 1; } def CUDADeviceBuiltinTextureType : InheritableAttr { @@ -1103,7 +1113,10 @@ let Subjects = SubjectList<[CXXRecord]>; let Documentation = [CUDADeviceBuiltinTextureTypeDocs]; let MeaningfulToClassTemplateDefinition = 1; + let SimpleHandler = 1; } +def : MutualExclusions<[CUDADeviceBuiltinSurfaceType, + CUDADeviceBuiltinTextureType]>; def CUDAGlobal : InheritableAttr { let Spellings = [GNU<"global">, Declspec<"__global__">]; @@ -1111,13 +1124,16 @@ let LangOpts = [CUDA]; let Documentation = [Undocumented]; } +def : MutualExclusions<[CUDADevice, CUDAGlobal]>; def CUDAHost : InheritableAttr { let Spellings = [GNU<"host">, Declspec<"__host__">]; let Subjects = SubjectList<[Function]>; let LangOpts = [CUDA]; let Documentation = [Undocumented]; + let SimpleHandler = 1; } +def : MutualExclusions<[CUDAGlobal, CUDAHost]>; def HIPManaged : InheritableAttr { let Spellings = [GNU<"managed">, Declspec<"__managed__">]; @@ -1150,6 +1166,7 @@ let LangOpts = [CUDA]; let Documentation = [Undocumented]; } +def : MutualExclusions<[CUDAConstant, CUDAShared, HIPManaged]>; def SYCLKernel : InheritableAttr { let Spellings = [Clang<"sycl_kernel">]; @@ -1342,6 +1359,7 @@ let Spellings = [CXX11<"", "unlikely", 201803>, C2x<"clang", "unlikely">]; let Documentation = [LikelihoodDocs]; } +def : MutualExclusions<[Likely, Unlikely]>; def NoMerge : DeclOrStmtAttr { let Spellings = [Clang<"nomerge">]; @@ -1433,7 +1451,9 @@ // An AST node is created for this attribute, but not actually used beyond // semantic checking for mutual exclusion with the Cold attribute. let Documentation = [Undocumented]; + let SimpleHandler = 1; } +def : MutualExclusions<[Hot, Cold]>; def IBAction : InheritableAttr { let Spellings = [Clang<"ibaction">]; @@ -1544,6 +1564,7 @@ let Spellings = [GCC<"mips16">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } def MipsInterrupt : InheritableAttr, TargetSpecificAttr { @@ -1562,24 +1583,30 @@ let ParseKind = "Interrupt"; let Documentation = [MipsInterruptDocs]; } +def : MutualExclusions<[Mips16, MipsInterrupt]>; def MicroMips : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"micromips">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [MicroMipsDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[Mips16, MicroMips]>; def MipsLongCall : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"long_call">, GCC<"far">]; let Subjects = SubjectList<[Function]>; let Documentation = [MipsLongCallStyleDocs]; + let SimpleHandler = 1; } def MipsShortCall : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"short_call">, GCC<"near">]; let Subjects = SubjectList<[Function]>; let Documentation = [MipsShortCallStyleDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[MipsLongCall, MipsShortCall]>; def M68kInterrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's @@ -1656,7 +1683,9 @@ let Spellings = [Clang<"disable_tail_calls">]; let Subjects = SubjectList<[Function, ObjCMethod]>; let Documentation = [DisableTailCallsDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[Naked, DisableTailCalls]>; def NoAlias : InheritableAttr { let Spellings = [Declspec<"noalias">]; @@ -1930,7 +1959,9 @@ let Spellings = [Clang<"not_tail_called">]; let Subjects = SubjectList<[Function]>; let Documentation = [NotTailCalledDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[AlwaysInline, NotTailCalled]>; def NoStackProtector : InheritableAttr { let Spellings = [Clang<"no_stack_protector">]; @@ -3248,6 +3279,7 @@ let Args = [TypeArgument<"DerefType", /*opt=*/1>]; let Documentation = [LifetimePointerDocs]; } +def : MutualExclusions<[Owner, Pointer]>; // Microsoft-related attributes @@ -3620,6 +3652,7 @@ let Subjects = SubjectList<[Var, Function, CXXRecord]>; let Documentation = [InternalLinkageDocs]; } +def : MutualExclusions<[Common, InternalLinkage]>; def ExcludeFromExplicitInstantiation : InheritableAttr { let Spellings = [Clang<"exclude_from_explicit_instantiation">]; @@ -3647,18 +3680,22 @@ let Subjects = SubjectList<[Var]>; let Documentation = [AlwaysDestroyDocs]; } +def : MutualExclusions<[NoDestroy, AlwaysDestroy]>; def SpeculativeLoadHardening : InheritableAttr { let Spellings = [Clang<"speculative_load_hardening">]; let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; let Documentation = [SpeculativeLoadHardeningDocs]; + let SimpleHandler = 1; } def NoSpeculativeLoadHardening : InheritableAttr { let Spellings = [Clang<"no_speculative_load_hardening">]; let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; let Documentation = [NoSpeculativeLoadHardeningDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[SpeculativeLoadHardening, NoSpeculativeLoadHardening]>; def Uninitialized : InheritableAttr { let Spellings = [Clang<"uninitialized", 0>]; Index: clang/include/clang/Sema/ParsedAttr.h =================================================================== --- clang/include/clang/Sema/ParsedAttr.h +++ clang/include/clang/Sema/ParsedAttr.h @@ -86,6 +86,18 @@ const Stmt *St) const { return true; } + /// Check if the given attribute is mutually exclusive with other attributes + /// already applied to the given declaration. + virtual bool diagMutualExclusion(Sema &S, const ParsedAttr &A, + const Decl *D) const { + return true; + } + /// Check if the given attribute is mutually exclusive with other attributes + /// already applied to the given statement. + virtual bool diagMutualExclusion(Sema &S, const ParsedAttr &A, + const Stmt *St) const { + return true; + } /// Check if this attribute is allowed by the language we are compiling, and /// issue a diagnostic if not. virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const { @@ -599,6 +611,8 @@ bool hasVariadicArg() const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const; + bool diagnoseMutualExclusion(class Sema &S, const Decl *D) const; + bool diagnoseMutualExclusion(class Sema &S, const Stmt *St) const; bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const; void getMatchRules(const LangOptions &LangOpts, SmallVectorImpl> Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3304,12 +3304,6 @@ const AttributeCommonInfo &CI, const IdentifierInfo *Ident); MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI); - NoSpeculativeLoadHardeningAttr * - mergeNoSpeculativeLoadHardeningAttr(Decl *D, - const NoSpeculativeLoadHardeningAttr &AL); - SpeculativeLoadHardeningAttr * - mergeSpeculativeLoadHardeningAttr(Decl *D, - const SpeculativeLoadHardeningAttr &AL); SwiftNameAttr *mergeSwiftNameAttr(Decl *D, const SwiftNameAttr &SNA, StringRef Name); OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, @@ -3317,8 +3311,6 @@ InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL); InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const InternalLinkageAttr &AL); - CommonAttr *mergeCommonAttr(Decl *D, const ParsedAttr &AL); - CommonAttr *mergeCommonAttr(Decl *D, const CommonAttr &AL); WebAssemblyImportNameAttr *mergeImportNameAttr( Decl *D, const WebAssemblyImportNameAttr &AL); WebAssemblyImportModuleAttr *mergeImportModuleAttr( Index: clang/lib/Sema/ParsedAttr.cpp =================================================================== --- clang/lib/Sema/ParsedAttr.cpp +++ clang/lib/Sema/ParsedAttr.cpp @@ -163,6 +163,14 @@ return getInfo().diagAppertainsToStmt(S, *this, St); } +bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Decl *D) const { + return getInfo().diagMutualExclusion(S, *this, D); +} + +bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Stmt *St) const { + return getInfo().diagMutualExclusion(S, *this, St); +} + bool ParsedAttr::appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const { return checkAttributeMatchRuleAppliesTo(D, MatchRule); Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -1206,6 +1206,10 @@ // Check whether the attribute appertains to the given subject. if (!A.diagnoseAppertainsTo(S, Node)) return true; + // Check whether the attribute is mutually exclusive with other attributes + // that have already been applied to the declaration. + if (!A.diagnoseMutualExclusion(S, Node)) + return true; // Check whether the attribute exists in the target architecture. if (S.CheckAttrTarget(A)) return true; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2540,9 +2540,18 @@ return AnyAdded; } +#define WANT_MERGE_LOGIC +#include "clang/Sema/AttrParsedAttrImpl.inc" +#undef WANT_MERGE_LOGIC + static bool mergeDeclAttribute(Sema &S, NamedDecl *D, const InheritableAttr *Attr, Sema::AvailabilityMergeKind AMK) { + // Diagnose any mutual exclusions between the attribute that we want to add + // and attributes that already exist on the declaration. + if (!DiagnoseMutualExclusions(S, D, Attr)) + return false; + // This function copies an attribute Attr from a previous declaration to the // new declaration D if the new declaration doesn't itself have that attribute // yet or if that attribute allows duplicates. @@ -2592,8 +2601,6 @@ NewAttr = S.mergeOptimizeNoneAttr(D, *OA); else if (const auto *InternalLinkageA = dyn_cast(Attr)) NewAttr = S.mergeInternalLinkageAttr(D, *InternalLinkageA); - else if (const auto *CommonA = dyn_cast(Attr)) - NewAttr = S.mergeCommonAttr(D, *CommonA); else if (isa(Attr)) // AlignedAttrs are handled separately, because we need to handle all // such attributes on a declaration at the same time. @@ -2604,10 +2611,6 @@ NewAttr = nullptr; else if (const auto *UA = dyn_cast(Attr)) NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl()); - else if (const auto *SLHA = dyn_cast(Attr)) - NewAttr = S.mergeSpeculativeLoadHardeningAttr(D, *SLHA); - else if (const auto *SLHA = dyn_cast(Attr)) - NewAttr = S.mergeNoSpeculativeLoadHardeningAttr(D, *SLHA); else if (const auto *IMA = dyn_cast(Attr)) NewAttr = S.mergeImportModuleAttr(D, *IMA); else if (const auto *INA = dyn_cast(Attr)) Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -407,24 +407,6 @@ handleSimpleAttribute(S, D, CI); } -template -static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, - const ParsedAttr &AL) { - handleSimpleAttribute(S, D, AL); -} - -/// Applies the given attribute to the Decl so long as the Decl doesn't -/// already have one of the given incompatible attributes. -template -static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, - const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL)) - return; - handleSimpleAttributeWithExclusions(S, D, - AL); -} - /// Check if the passed-in expression is of type int or bool. static bool isIntOrBool(Expr *Exp) { QualType QT = Exp->getType(); @@ -2021,8 +2003,7 @@ return; } - if (CommonAttr *CA = S.mergeCommonAttr(D, AL)) - D->addAttr(CA); + D->addAttr(::new (S.Context) CommonAttr(S.Context, AL)); } static void handleCmseNSEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -2041,9 +2022,6 @@ } static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL)) - return; - if (AL.isDeclspecAttribute()) { const auto &Triple = S.getASTContext().getTargetInfo().getTriple(); const auto &Arch = Triple.getArch(); @@ -4302,20 +4280,6 @@ return ::new (Context) AlwaysInlineAttr(Context, CI); } -CommonAttr *Sema::mergeCommonAttr(Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - - return ::new (Context) CommonAttr(Context, AL); -} - -CommonAttr *Sema::mergeCommonAttr(Decl *D, const CommonAttr &AL) { - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - - return ::new (Context) CommonAttr(Context, AL); -} - InternalLinkageAttr *Sema::mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL) { if (const auto *VD = dyn_cast(D)) { @@ -4334,9 +4298,6 @@ } } - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - return ::new (Context) InternalLinkageAttr(Context, AL); } InternalLinkageAttr * @@ -4357,9 +4318,6 @@ } } - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - return ::new (Context) InternalLinkageAttr(Context, AL); } @@ -4376,14 +4334,6 @@ return ::new (Context) MinSizeAttr(Context, CI); } -NoSpeculativeLoadHardeningAttr *Sema::mergeNoSpeculativeLoadHardeningAttr( - Decl *D, const NoSpeculativeLoadHardeningAttr &AL) { - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - - return ::new (Context) NoSpeculativeLoadHardeningAttr(Context, AL); -} - SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, const SwiftNameAttr &SNA, StringRef Name) { if (const auto *PrevSNA = D->getAttr()) { @@ -4417,18 +4367,7 @@ return ::new (Context) OptimizeNoneAttr(Context, CI); } -SpeculativeLoadHardeningAttr *Sema::mergeSpeculativeLoadHardeningAttr( - Decl *D, const SpeculativeLoadHardeningAttr &AL) { - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - - return ::new (Context) SpeculativeLoadHardeningAttr(Context, AL); -} - static void handleAlwaysInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL)) - return; - if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr(D, AL, AL.getAttrName())) D->addAttr(Inline); @@ -4445,9 +4384,6 @@ } static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL) || - checkAttrMutualExclusion(S, D, AL)) - return; const auto *VD = cast(D); if (VD->hasLocalStorage()) { S.Diag(AL.getLoc(), diag::err_cuda_nonstatic_constdev); @@ -4457,9 +4393,6 @@ } static void handleSharedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL) || - checkAttrMutualExclusion(S, D, AL)) - return; const auto *VD = cast(D); // extern __shared__ is only allowed on arrays with no length (e.g. // "int x[]"). @@ -4476,10 +4409,6 @@ } static void handleGlobalAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL) || - checkAttrMutualExclusion(S, D, AL)) { - return; - } const auto *FD = cast(D); if (!FD->getReturnType()->isVoidType() && !FD->getReturnType()->getAs() && @@ -4513,10 +4442,6 @@ } static void handleDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL)) { - return; - } - if (const auto *VD = dyn_cast(D)) { if (VD->hasLocalStorage()) { S.Diag(AL.getLoc(), diag::err_cuda_nonstatic_constdev); @@ -4533,11 +4458,6 @@ } static void handleManagedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion(S, D, AL) || - checkAttrMutualExclusion(S, D, AL)) { - return; - } - if (const auto *VD = dyn_cast(D)) { if (VD->hasLocalStorage()) { S.Diag(AL.getLoc(), diag::err_cuda_nonstatic_constdev); @@ -4682,7 +4602,10 @@ } // To check if earlier decl attributes do not conflict the newly parsed ones - // we always add (and check) the attribute to the cannonical decl. + // we always add (and check) the attribute to the cannonical decl. We need + // to repeat the check for attribute mutual exclusion because we're attaching + // all of the attributes to the canonical declaration rather than the current + // declaration. D = D->getCanonicalDecl(); if (AL.getKind() == ParsedAttr::AT_Owner) { if (checkAttrMutualExclusion(S, D, AL)) @@ -6580,6 +6503,8 @@ return; } + // We still have to do this manually because the Interrupt attributes are + // a bit special due to sharing their spellings across targets. if (checkAttrMutualExclusion(S, D, AL)) return; @@ -7446,9 +7371,9 @@ } if (A.getKind() == ParsedAttr::AT_AlwaysDestroy) - handleSimpleAttributeWithExclusions(S, D, A); + handleSimpleAttribute(S, D, A); else - handleSimpleAttributeWithExclusions(S, D, A); + handleSimpleAttribute(S, D, A); } static void handleUninitializedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -7740,21 +7665,6 @@ case ParsedAttr::AT_DLLImport: handleDLLAttr(S, D, AL); break; - case ParsedAttr::AT_Mips16: - handleSimpleAttributeWithExclusions(S, D, AL); - break; - case ParsedAttr::AT_MicroMips: - handleSimpleAttributeWithExclusions(S, D, AL); - break; - case ParsedAttr::AT_MipsLongCall: - handleSimpleAttributeWithExclusions( - S, D, AL); - break; - case ParsedAttr::AT_MipsShortCall: - handleSimpleAttributeWithExclusions( - S, D, AL); - break; case ParsedAttr::AT_AMDGPUFlatWorkGroupSize: handleAMDGPUFlatWorkGroupSizeAttr(S, D, AL); break; @@ -7888,22 +7798,9 @@ case ParsedAttr::AT_CUDADevice: handleDeviceAttr(S, D, AL); break; - case ParsedAttr::AT_CUDAHost: - handleSimpleAttributeWithExclusions(S, D, AL); - break; case ParsedAttr::AT_HIPManaged: handleManagedAttr(S, D, AL); break; - case ParsedAttr::AT_CUDADeviceBuiltinSurfaceType: - handleSimpleAttributeWithExclusions(S, D, - AL); - break; - case ParsedAttr::AT_CUDADeviceBuiltinTextureType: - handleSimpleAttributeWithExclusions(S, D, - AL); - break; case ParsedAttr::AT_GNUInline: handleGNUInlineAttr(S, D, AL); break; @@ -7937,12 +7834,6 @@ case ParsedAttr::AT_Ownership: handleOwnershipAttr(S, D, AL); break; - case ParsedAttr::AT_Cold: - handleSimpleAttributeWithExclusions(S, D, AL); - break; - case ParsedAttr::AT_Hot: - handleSimpleAttributeWithExclusions(S, D, AL); - break; case ParsedAttr::AT_Naked: handleNakedAttr(S, D, AL); break; @@ -7995,14 +7886,6 @@ case ParsedAttr::AT_NSErrorDomain: handleNSErrorDomain(S, D, AL); break; - case ParsedAttr::AT_CFAuditedTransfer: - handleSimpleAttributeWithExclusions(S, D, AL); - break; - case ParsedAttr::AT_CFUnknownTransfer: - handleSimpleAttributeWithExclusions(S, D, AL); - break; case ParsedAttr::AT_CFConsumed: case ParsedAttr::AT_NSConsumed: case ParsedAttr::AT_OSConsumed: @@ -8058,15 +7941,6 @@ case ParsedAttr::AT_Section: handleSectionAttr(S, D, AL); break; - case ParsedAttr::AT_SpeculativeLoadHardening: - handleSimpleAttributeWithExclusions(S, D, - AL); - break; - case ParsedAttr::AT_NoSpeculativeLoadHardening: - handleSimpleAttributeWithExclusions(S, D, AL); - break; case ParsedAttr::AT_CodeSeg: handleCodeSegAttr(S, D, AL); break; @@ -8095,14 +7969,6 @@ case ParsedAttr::AT_Unused: handleUnusedAttr(S, D, AL); break; - case ParsedAttr::AT_NotTailCalled: - handleSimpleAttributeWithExclusions( - S, D, AL); - break; - case ParsedAttr::AT_DisableTailCalls: - handleSimpleAttributeWithExclusions(S, D, - AL); - break; case ParsedAttr::AT_Visibility: handleVisibilityAttr(S, D, AL, false); break; Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -332,32 +332,6 @@ << CategoryState.NumericAttr->getDiagnosticName(Policy); } } - - // C++20 [dcl.attr.likelihood]p1 The attribute-token likely shall not appear - // in an attribute-specifier-seq that contains the attribute-token unlikely. - const LikelyAttr *Likely = nullptr; - const UnlikelyAttr *Unlikely = nullptr; - for (const auto *I : Attrs) { - if (const auto *Attr = dyn_cast(I)) { - if (Unlikely) { - S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible) - << Attr << Unlikely << Attr->getRange(); - S.Diag(Unlikely->getLocation(), diag::note_conflicting_attribute) - << Unlikely->getRange(); - return; - } - Likely = Attr; - } else if (const auto *Attr = dyn_cast(I)) { - if (Likely) { - S.Diag(Attr->getLocation(), diag::err_attributes_are_not_compatible) - << Attr << Likely << Attr->getRange(); - S.Diag(Likely->getLocation(), diag::note_conflicting_attribute) - << Likely->getRange(); - return; - } - Unlikely = Attr; - } - } } static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A, Index: clang/test/Sema/attr-coldhot.c =================================================================== --- clang/test/Sema/attr-coldhot.c +++ clang/test/Sema/attr-coldhot.c @@ -10,3 +10,9 @@ // expected-note{{conflicting attribute is here}} int baz() __attribute__((__cold__)) __attribute__((__hot__)); // expected-error{{'__hot__' and 'cold' attributes are not compatible}} \ // expected-note{{conflicting attribute is here}} + +__attribute__((cold)) void test1(void); // expected-note{{conflicting attribute is here}} +__attribute__((hot)) void test1(void); // expected-error{{'hot' and 'cold' attributes are not compatible}} + +__attribute__((hot)) void test2(void); // expected-note{{conflicting attribute is here}} +__attribute__((cold)) void test2(void); // expected-error{{'cold' and 'hot' attributes are not compatible}} Index: clang/test/Sema/attr-disable-tail-calls.c =================================================================== --- clang/test/Sema/attr-disable-tail-calls.c +++ clang/test/Sema/attr-disable-tail-calls.c @@ -11,3 +11,9 @@ int g0 __attribute__((disable_tail_calls)); // expected-warning {{'disable_tail_calls' attribute only applies to functions and Objective-C methods}} int foo3(int a) __attribute__((disable_tail_calls("abc"))); // expected-error {{'disable_tail_calls' attribute takes no arguments}} + +__attribute__((naked)) void foo4(void); // expected-note {{conflicting attribute is here}} +__attribute__((disable_tail_calls)) void foo4(void); // expected-error {{'disable_tail_calls' and 'naked' attributes are not compatible}} + +__attribute__((disable_tail_calls)) void foo5(void); // expected-note {{conflicting attribute is here}} +__attribute__((naked)) void foo5(void); // expected-error {{'naked' and 'disable_tail_calls' attributes are not compatible}} Index: clang/test/Sema/internal_linkage.c =================================================================== --- clang/test/Sema/internal_linkage.c +++ clang/test/Sema/internal_linkage.c @@ -6,13 +6,12 @@ int var3 __attribute__((common,internal_linkage)); // expected-error{{'internal_linkage' and 'common' attributes are not compatible}} \ // expected-note{{conflicting attribute is here}} -int var4 __attribute__((common)); // expected-error{{'common' and 'internal_linkage' attributes are not compatible}} \ -// expected-note{{previous definition is here}} -int var4 __attribute__((internal_linkage)); // expected-note{{conflicting attribute is here}} \ -// expected-error{{'internal_linkage' attribute does not appear on the first declaration of 'var4'}} +int var4 __attribute__((common)); // expected-note{{previous definition is here}} expected-note{{conflicting attribute is here}} +int var4 __attribute__((internal_linkage)); // expected-error{{'internal_linkage' and 'common' attributes are not compatible}} \ + // expected-error{{'internal_linkage' attribute does not appear on the first declaration of 'var4'}} -int var5 __attribute__((internal_linkage)); // expected-error{{'internal_linkage' and 'common' attributes are not compatible}} -int var5 __attribute__((common)); // expected-note{{conflicting attribute is here}} +int var5 __attribute__((internal_linkage)); // expected-note{{conflicting attribute is here}} +int var5 __attribute__((common)); // expected-error{{'common' and 'internal_linkage' attributes are not compatible}} __attribute__((internal_linkage)) int f() {} struct __attribute__((internal_linkage)) S { // expected-warning{{'internal_linkage' attribute only applies to variables, functions, and classes}} Index: clang/test/SemaCXX/attr-speculative-load-hardening.cpp =================================================================== --- clang/test/SemaCXX/attr-speculative-load-hardening.cpp +++ clang/test/SemaCXX/attr-speculative-load-hardening.cpp @@ -22,10 +22,9 @@ void f5() __attribute__((speculative_load_hardening, no_speculative_load_hardening)); // expected-error {{attributes are not compatible}} // expected-note@-1 {{conflicting attribute is here}} -void f6() __attribute__((no_speculative_load_hardening)); +void f6() __attribute__((no_speculative_load_hardening)); // expected-note {{conflicting attribute is here}} -void f6() __attribute__((speculative_load_hardening)); // expected-error@-2 {{'no_speculative_load_hardening' and 'speculative_load_hardening' attributes are not compatible}} -// expected-note@-1 {{conflicting attribute is here}} +void f6() __attribute__((speculative_load_hardening)); // expected-error {{'speculative_load_hardening' and 'no_speculative_load_hardening' attributes are not compatible}} int ci [[clang::speculative_load_hardening]]; // expected-error {{'speculative_load_hardening' attribute only applies to functions}} @@ -51,8 +50,8 @@ // expected-note@-1 {{conflicting attribute is here}} [[clang::speculative_load_hardening]] -void cf6(); +void cf6(); // expected-note@-1 {{conflicting attribute is here}} [[clang::no_speculative_load_hardening]] -void cf6(); // expected-error@-4 {{'speculative_load_hardening' and 'no_speculative_load_hardening' attributes are not compatible}} \ -// expected-note@-1 {{conflicting attribute is here}} +void cf6(); // expected-error@-1 {{'no_speculative_load_hardening' and 'speculative_load_hardening' attributes are not compatible}} \ + Index: clang/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangAttrEmitter.cpp +++ clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3627,6 +3627,114 @@ } } +// Generates the mutual exclusion checks. The checks for parsed attributes are +// written into OS and the checks for merging declaration attributes are +// written into MergeOS. +static void GenerateMutualExclusionsChecks(const Record &Attr, + const RecordKeeper &Records, + raw_ostream &OS, + raw_ostream &MergeOS) { + // Find all of the definitions that inherit from MutualExclusions and include + // the given attribute in the list of exclusions to generate the + // diagMutualExclusion() check. + std::vector ExclusionsList = + Records.getAllDerivedDefinitions("MutualExclusions"); + + // We don't do any of this magic for type attributes yet. + if (Attr.isSubClassOf("TypeAttr")) + return; + + // This means the attribute is either a statement attribute or a decl + // attribute, find out which. + bool CurAttrIsStmtAttr = + Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr"); + + std::vector DeclAttrs, StmtAttrs; + + for (const Record *Exclusion : ExclusionsList) { + std::vector MutuallyExclusiveAttrs = + Exclusion->getValueAsListOfDefs("Exclusions"); + auto IsCurAttr = [Attr](const Record *R) { + return R->getName() == Attr.getName(); + }; + if (llvm::any_of(MutuallyExclusiveAttrs, IsCurAttr)) { + // This list of exclusions includes the attribute we're looking for, so + // add the exclusive attributes to the proper list for checking. + for (const Record *AttrToExclude : MutuallyExclusiveAttrs) { + if (IsCurAttr(AttrToExclude)) + continue; + + if (CurAttrIsStmtAttr) + StmtAttrs.push_back((AttrToExclude->getName() + "Attr").str()); + else + DeclAttrs.push_back((AttrToExclude->getName() + "Attr").str()); + } + } + } + + // If we discovered any decl or stmt attributes to test for, generate the + // predicates for them now. + if (!DeclAttrs.empty()) { + // Generate the ParsedAttrInfo subclass logic for declarations. + OS << " bool diagMutualExclusion(Sema &S, const ParsedAttr &AL, " + << "const Decl *D) const {\n"; + for (const std::string &A : DeclAttrs) { + OS << " if (const auto *A = D->getAttr<" << A << ">()) {\n"; + OS << " S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)" + << " << AL << A;\n"; + OS << " S.Diag(A->getLocation(), diag::note_conflicting_attribute);"; + OS << " \nreturn false;\n"; + OS << " }\n"; + } + OS << " return true;\n"; + OS << " }\n\n"; + + // Also generate the declaration attribute merging logic if the current + // attribute is one that can be inheritted on a declaration. It is assumed + // this code will be executed in the context of a function with parameters: + // Sema &S, Decl *D, Attr *A and that returns a bool (false on diagnostic, + // true on success). + if (Attr.isSubClassOf("InheritableAttr")) { + MergeOS << " if (const auto *Second = dyn_cast<" + << (Attr.getName() + "Attr").str() << ">(A)) {\n"; + for (const std::string &A : DeclAttrs) { + MergeOS << " if (const auto *First = D->getAttr<" << A << ">()) {\n"; + MergeOS << " S.Diag(First->getLocation(), " + << "diag::err_attributes_are_not_compatible) << First << " + << "Second;\n"; + MergeOS << " S.Diag(Second->getLocation(), " + << "diag::note_conflicting_attribute);\n"; + MergeOS << " return false;\n"; + MergeOS << " }\n"; + } + MergeOS << " return true;\n"; + MergeOS << " }\n"; + } + } + if (!StmtAttrs.empty()) { + // Generate the ParsedAttrInfo subclass logic for statements. + OS << " bool diagMutualExclusion(Sema &S, const ParsedAttr &AL, " + << "const Stmt *St) const {\n"; + OS << " if (const auto *AS = dyn_cast(St)) {\n"; + OS << " const ArrayRef &Attrs = AS->getAttrs();\n"; + for (const std::string &A : StmtAttrs) { + OS << " auto Iter" << A << " = llvm::find_if(Attrs, [](const Attr " + << "*A) { return isa<" << A << ">(A); });\n"; + OS << " if (Iter" << A << " != Attrs.end()) {\n"; + OS << " S.Diag(AL.getLoc(), " + << "diag::err_attributes_are_not_compatible) << AL << *Iter" << A + << ";\n"; + OS << " S.Diag((*Iter" << A << ")->getLocation(), " + << "diag::note_conflicting_attribute);\n"; + OS << " return false;\n"; + OS << " }\n"; + } + OS << " }\n"; + OS << " return true;\n"; + OS << " }\n\n"; + } +} + static void emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport, raw_ostream &OS) { @@ -3775,6 +3883,7 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Parsed attribute helpers", OS); + OS << "#if !defined(WANT_MERGE_LOGIC)\n"; PragmaClangAttributeSupport &PragmaAttributeSupport = getPragmaAttributeSupport(Records); @@ -3795,6 +3904,12 @@ GenerateCustomAppertainsTo(*Subject, OS); } + // This stream is used to collect all of the declaration attribute merging + // logic for performing mutual exclusion checks. This gets emitted at the + // end of the file in a helper function of its own. + std::string DeclMergeChecks; + raw_string_ostream MergeOS(DeclMergeChecks); + // Generate a ParsedAttrInfo struct for each of the attributes. for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { // TODO: If the attribute's kind appears in the list of duplicates, that is @@ -3848,6 +3963,7 @@ OS << " Spellings = " << I->first << "Spellings;\n"; OS << " }\n"; GenerateAppertainsTo(Attr, OS); + GenerateMutualExclusionsChecks(Attr, Records, OS, MergeOS); GenerateLangOptRequirements(Attr, OS); GenerateTargetRequirements(Attr, Dupes, OS); GenerateSpellingIndexToSemanticSpelling(Attr, OS); @@ -3867,6 +3983,17 @@ // Generate the attribute match rules. emitAttributeMatchRules(PragmaAttributeSupport, OS); + + OS << "#else // WANT_MERGE_LOGIC\n\n"; + + // Write out the declaration merging check logic. + OS << "static bool DiagnoseMutualExclusions(Sema &S, const NamedDecl *D, " + << "const Attr *A) {\n"; + OS << MergeOS.str(); + OS << " return true;\n"; + OS << "}\n\n"; + + OS << "#endif // WANT_MERGE_LOGIC\n"; } // Emits the kind list of parsed attributes