From 4be8a26951da9a6e04de327b38dd158f4c8e3280 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 2 Apr 2021 16:33:14 -0400 Subject: [PATCH] Use tablegen to diagnose mutually exclusive attributes Currently, when one or more attributes are mutually exclusive, the developer adding the attribute has to manually emit diagnostics. In practice, this is highly error prone, especially for declaration attributes, because such checking is not trivial. Redeclarations require you to write a "merge" function to diagnose mutually exclusive attributes and most attributes get this wrong. This patch introduces a table-generated way to specify that a group of two or more attributes are mutually exclusive: def : MutualExclusions<[Attr1, Attr2, Attr3]>; This works for both statement and declaration attributes (but not type attributes) and the checking is done either from the common attribute diagnostic checking code or from within mergeDeclAttribute() when merging redeclarations. --- clang/docs/InternalsManual.rst | 7 + clang/include/clang/Basic/Attr.td | 37 +++++ clang/include/clang/Sema/ParsedAttr.h | 14 ++ clang/include/clang/Sema/Sema.h | 8 - clang/lib/Sema/ParsedAttr.cpp | 8 + clang/lib/Sema/SemaAttr.cpp | 4 + clang/lib/Sema/SemaDecl.cpp | 15 +- clang/lib/Sema/SemaDeclAttr.cpp | 152 ++---------------- clang/lib/Sema/SemaStmtAttr.cpp | 26 --- clang/test/Sema/attr-coldhot.c | 6 + clang/test/Sema/attr-disable-tail-calls.c | 6 + clang/test/Sema/internal_linkage.c | 11 +- .../attr-speculative-load-hardening.cpp | 11 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 127 +++++++++++++++ 14 files changed, 237 insertions(+), 195 deletions(-) diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index a547154721e9..c1e45569a816 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -3033,6 +3033,13 @@ If additional functionality is desired for the semantic form of the attribute, 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 diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b832d1df7172..d9c2422536d0 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -554,6 +554,11 @@ class Attr { 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 @@ def CFAuditedTransfer : InheritableAttr { 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 @@ def CFUnknownTransfer : InheritableAttr { 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 @@ def Cold : InheritableAttr { let Spellings = [GCC<"cold">]; let Subjects = SubjectList<[Function]>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } def Common : InheritableAttr { @@ -1094,6 +1103,7 @@ def CUDADeviceBuiltinSurfaceType : InheritableAttr { let Subjects = SubjectList<[CXXRecord]>; let Documentation = [CUDADeviceBuiltinSurfaceTypeDocs]; let MeaningfulToClassTemplateDefinition = 1; + let SimpleHandler = 1; } def CUDADeviceBuiltinTextureType : InheritableAttr { @@ -1103,7 +1113,10 @@ def CUDADeviceBuiltinTextureType : InheritableAttr { 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 @@ def CUDAGlobal : InheritableAttr { 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 @@ def CUDAShared : InheritableAttr { let LangOpts = [CUDA]; let Documentation = [Undocumented]; } +def : MutualExclusions<[CUDAConstant, CUDAShared, HIPManaged]>; def SYCLKernel : InheritableAttr { let Spellings = [Clang<"sycl_kernel">]; @@ -1342,6 +1359,7 @@ def Unlikely : StmtAttr { 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 @@ def Hot : InheritableAttr { // 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 @@ def Mips16 : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"mips16">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [Undocumented]; + let SimpleHandler = 1; } def MipsInterrupt : InheritableAttr, TargetSpecificAttr { @@ -1562,24 +1583,30 @@ def MipsInterrupt : InheritableAttr, TargetSpecificAttr { 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 @@ def DisableTailCalls : InheritableAttr { 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 @@ def NotTailCalled : InheritableAttr { 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 @@ def Pointer : InheritableAttr { let Args = [TypeArgument<"DerefType", /*opt=*/1>]; let Documentation = [LifetimePointerDocs]; } +def : MutualExclusions<[Owner, Pointer]>; // Microsoft-related attributes @@ -3620,6 +3652,7 @@ def InternalLinkage : InheritableAttr { 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 @@ def AlwaysDestroy : InheritableAttr { 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>]; diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index a3d82fcd84f7..9cbee28d612a 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -86,6 +86,18 @@ struct ParsedAttrInfo { 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 @@ public: 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> diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5f514d5f6a1d..e0973171be93 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3304,12 +3304,6 @@ public: 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 @@ public: 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( diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp index 1ac7ed1afc4e..989db3c2a406 100644 --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -163,6 +163,14 @@ bool ParsedAttr::diagnoseAppertainsTo(Sema &S, const Stmt *St) const { 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); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 2c37ccee1616..88c050b01e3f 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1206,6 +1206,10 @@ static bool checkCommonAttributeFeatures(Sema& S, const Ty *Node, // 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; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b117d73a9b60..cf08ef4631a8 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2540,9 +2540,18 @@ static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) { 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 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, 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 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, 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)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 23b488c527b1..138b9f0132b5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -407,24 +407,6 @@ static void handleSimpleAttributeOrDiagnose(Sema &S, Decl *D, 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 @@ static void handleCommonAttr(Sema &S, Decl *D, const ParsedAttr &AL) { 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 handleCmseNSEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 @@ AlwaysInlineAttr *Sema::mergeAlwaysInlineAttr(Decl *D, 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 @@ InternalLinkageAttr *Sema::mergeInternalLinkageAttr(Decl *D, } } - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - return ::new (Context) InternalLinkageAttr(Context, AL); } InternalLinkageAttr * @@ -4357,9 +4318,6 @@ Sema::mergeInternalLinkageAttr(Decl *D, const InternalLinkageAttr &AL) { } } - if (checkAttrMutualExclusion(*this, D, AL)) - return nullptr; - return ::new (Context) InternalLinkageAttr(Context, AL); } @@ -4376,14 +4334,6 @@ MinSizeAttr *Sema::mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI) { 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 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, 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 handleOptimizeNoneAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 handleSharedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 handleGlobalAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 handleDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } 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 @@ static void handleLifetimeCategoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } // 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 @@ static void handleMipsInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { 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 @@ static void handleDestroyAttr(Sema &S, Decl *D, const ParsedAttr &A) { } 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 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; diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index cb90a03aa20e..39c684cbd6da 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -332,32 +332,6 @@ CheckForIncompatibleAttributes(Sema &S, << 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, diff --git a/clang/test/Sema/attr-coldhot.c b/clang/test/Sema/attr-coldhot.c index a4b15822065c..26ac07722994 100644 --- a/clang/test/Sema/attr-coldhot.c +++ b/clang/test/Sema/attr-coldhot.c @@ -10,3 +10,9 @@ int qux() __attribute__((__hot__)) __attribute__((__cold__)); // expected-error{ // 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}} diff --git a/clang/test/Sema/attr-disable-tail-calls.c b/clang/test/Sema/attr-disable-tail-calls.c index 0545e951e60e..110f45b3bf98 100644 --- a/clang/test/Sema/attr-disable-tail-calls.c +++ b/clang/test/Sema/attr-disable-tail-calls.c @@ -11,3 +11,9 @@ void __attribute__((naked,disable_tail_calls)) foo2(int a) { // expected-error { 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}} diff --git a/clang/test/Sema/internal_linkage.c b/clang/test/Sema/internal_linkage.c index cc8039acd275..a6911f4baa7b 100644 --- a/clang/test/Sema/internal_linkage.c +++ b/clang/test/Sema/internal_linkage.c @@ -6,13 +6,12 @@ int var2 __attribute__((internal_linkage,common)); // expected-error{{'common' a 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}} diff --git a/clang/test/SemaCXX/attr-speculative-load-hardening.cpp b/clang/test/SemaCXX/attr-speculative-load-hardening.cpp index ff31e4be1799..3f24069f1e84 100644 --- a/clang/test/SemaCXX/attr-speculative-load-hardening.cpp +++ b/clang/test/SemaCXX/attr-speculative-load-hardening.cpp @@ -22,10 +22,9 @@ void f4() __attribute__((no_speculative_load_hardening, speculative_load_hardeni 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 @@ struct CA { // 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}} \ + diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index e74df36899d4..bddda1fe47f7 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3627,6 +3627,114 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { } } +// 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 @@ static bool IsKnownToGCC(const Record &Attr) { 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 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { 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 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { 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 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // 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