aaron.ballman updated this revision to Diff 335021. aaron.ballman added a comment.
Correcting lint warnings, adding documentation for the new feature. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D99809/new/ https://reviews.llvm.org/D99809 Files: clang/docs/InternalsManual.rst clang/include/clang/Basic/Attr.td clang/include/clang/Sema/ParsedAttr.h clang/include/clang/Sema/Sema.h clang/lib/Sema/ParsedAttr.cpp clang/lib/Sema/SemaAttr.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/lib/Sema/SemaStmtAttr.cpp clang/test/Sema/attr-coldhot.c clang/test/Sema/attr-disable-tail-calls.c clang/test/Sema/internal_linkage.c clang/test/SemaCXX/attr-speculative-load-hardening.cpp clang/utils/TableGen/ClangAttrEmitter.cpp
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<Record *> 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<std::string> DeclAttrs, StmtAttrs; + + for (const Record *Exclusion : ExclusionsList) { + std::vector<Record *> 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<AttributedStmt>(St)) {\n"; + OS << " const ArrayRef<const Attr *> &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 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/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/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/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/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<LikelyAttr>(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<UnlikelyAttr>(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/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -407,24 +407,6 @@ handleSimpleAttribute<AttrType>(S, D, CI); } -template <typename AttrType> -static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, - const ParsedAttr &AL) { - handleSimpleAttribute<AttrType>(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 <typename AttrType, typename IncompatibleAttrType, - typename... IncompatibleAttrTypes> -static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, - const ParsedAttr &AL) { - if (checkAttrMutualExclusion<IncompatibleAttrType>(S, D, AL)) - return; - handleSimpleAttributeWithExclusions<AttrType, IncompatibleAttrTypes...>(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<DisableTailCallsAttr>(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<InternalLinkageAttr>(*this, D, AL)) - return nullptr; - - return ::new (Context) CommonAttr(Context, AL); -} - -CommonAttr *Sema::mergeCommonAttr(Decl *D, const CommonAttr &AL) { - if (checkAttrMutualExclusion<InternalLinkageAttr>(*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<VarDecl>(D)) { @@ -4334,9 +4298,6 @@ } } - if (checkAttrMutualExclusion<CommonAttr>(*this, D, AL)) - return nullptr; - return ::new (Context) InternalLinkageAttr(Context, AL); } InternalLinkageAttr * @@ -4357,9 +4318,6 @@ } } - if (checkAttrMutualExclusion<CommonAttr>(*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<SpeculativeLoadHardeningAttr>(*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<SwiftNameAttr>()) { @@ -4417,18 +4367,7 @@ return ::new (Context) OptimizeNoneAttr(Context, CI); } -SpeculativeLoadHardeningAttr *Sema::mergeSpeculativeLoadHardeningAttr( - Decl *D, const SpeculativeLoadHardeningAttr &AL) { - if (checkAttrMutualExclusion<NoSpeculativeLoadHardeningAttr>(*this, D, AL)) - return nullptr; - - return ::new (Context) SpeculativeLoadHardeningAttr(Context, AL); -} - static void handleAlwaysInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion<NotTailCalledAttr>(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<CUDASharedAttr>(S, D, AL) || - checkAttrMutualExclusion<HIPManagedAttr>(S, D, AL)) - return; const auto *VD = cast<VarDecl>(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<CUDAConstantAttr>(S, D, AL) || - checkAttrMutualExclusion<HIPManagedAttr>(S, D, AL)) - return; const auto *VD = cast<VarDecl>(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<CUDADeviceAttr>(S, D, AL) || - checkAttrMutualExclusion<CUDAHostAttr>(S, D, AL)) { - return; - } const auto *FD = cast<FunctionDecl>(D); if (!FD->getReturnType()->isVoidType() && !FD->getReturnType()->getAs<AutoType>() && @@ -4513,10 +4442,6 @@ } static void handleDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - if (checkAttrMutualExclusion<CUDAGlobalAttr>(S, D, AL)) { - return; - } - if (const auto *VD = dyn_cast<VarDecl>(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<CUDAConstantAttr>(S, D, AL) || - checkAttrMutualExclusion<CUDASharedAttr>(S, D, AL)) { - return; - } - if (const auto *VD = dyn_cast<VarDecl>(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<PointerAttr>(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<Mips16Attr>(S, D, AL)) return; @@ -7446,9 +7371,9 @@ } if (A.getKind() == ParsedAttr::AT_AlwaysDestroy) - handleSimpleAttributeWithExclusions<AlwaysDestroyAttr, NoDestroyAttr>(S, D, A); + handleSimpleAttribute<AlwaysDestroyAttr>(S, D, A); else - handleSimpleAttributeWithExclusions<NoDestroyAttr, AlwaysDestroyAttr>(S, D, A); + handleSimpleAttribute<NoDestroyAttr>(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<Mips16Attr, MicroMipsAttr, - MipsInterruptAttr>(S, D, AL); - break; - case ParsedAttr::AT_MicroMips: - handleSimpleAttributeWithExclusions<MicroMipsAttr, Mips16Attr>(S, D, AL); - break; - case ParsedAttr::AT_MipsLongCall: - handleSimpleAttributeWithExclusions<MipsLongCallAttr, MipsShortCallAttr>( - S, D, AL); - break; - case ParsedAttr::AT_MipsShortCall: - handleSimpleAttributeWithExclusions<MipsShortCallAttr, MipsLongCallAttr>( - 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<CUDAHostAttr, CUDAGlobalAttr>(S, D, AL); - break; case ParsedAttr::AT_HIPManaged: handleManagedAttr(S, D, AL); break; - case ParsedAttr::AT_CUDADeviceBuiltinSurfaceType: - handleSimpleAttributeWithExclusions<CUDADeviceBuiltinSurfaceTypeAttr, - CUDADeviceBuiltinTextureTypeAttr>(S, D, - AL); - break; - case ParsedAttr::AT_CUDADeviceBuiltinTextureType: - handleSimpleAttributeWithExclusions<CUDADeviceBuiltinTextureTypeAttr, - CUDADeviceBuiltinSurfaceTypeAttr>(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<ColdAttr, HotAttr>(S, D, AL); - break; - case ParsedAttr::AT_Hot: - handleSimpleAttributeWithExclusions<HotAttr, ColdAttr>(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<CFAuditedTransferAttr, - CFUnknownTransferAttr>(S, D, AL); - break; - case ParsedAttr::AT_CFUnknownTransfer: - handleSimpleAttributeWithExclusions<CFUnknownTransferAttr, - CFAuditedTransferAttr>(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<SpeculativeLoadHardeningAttr, - NoSpeculativeLoadHardeningAttr>(S, D, - AL); - break; - case ParsedAttr::AT_NoSpeculativeLoadHardening: - handleSimpleAttributeWithExclusions<NoSpeculativeLoadHardeningAttr, - SpeculativeLoadHardeningAttr>(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<NotTailCalledAttr, AlwaysInlineAttr>( - S, D, AL); - break; - case ParsedAttr::AT_DisableTailCalls: - handleSimpleAttributeWithExclusions<DisableTailCallsAttr, NakedAttr>(S, D, - AL); - break; case ParsedAttr::AT_Visibility: handleVisibilityAttr(S, D, AL, false); break; 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<InternalLinkageAttr>(Attr)) NewAttr = S.mergeInternalLinkageAttr(D, *InternalLinkageA); - else if (const auto *CommonA = dyn_cast<CommonAttr>(Attr)) - NewAttr = S.mergeCommonAttr(D, *CommonA); else if (isa<AlignedAttr>(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<UuidAttr>(Attr)) NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl()); - else if (const auto *SLHA = dyn_cast<SpeculativeLoadHardeningAttr>(Attr)) - NewAttr = S.mergeSpeculativeLoadHardeningAttr(D, *SLHA); - else if (const auto *SLHA = dyn_cast<NoSpeculativeLoadHardeningAttr>(Attr)) - NewAttr = S.mergeNoSpeculativeLoadHardeningAttr(D, *SLHA); else if (const auto *IMA = dyn_cast<WebAssemblyImportModuleAttr>(Attr)) NewAttr = S.mergeImportModuleAttr(D, *IMA); else if (const auto *INA = dyn_cast<WebAssemblyImportNameAttr>(Attr)) 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/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/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/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<std::pair<attr::SubjectMatchRule, bool>> 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> Documentation; } +/// Used to define a set of mutually exclusive attributes. +class MutualExclusions<list<Attr> Ex> { + list<Attr> 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<TargetMips32> { @@ -1562,24 +1583,30 @@ let ParseKind = "Interrupt"; let Documentation = [MipsInterruptDocs]; } +def : MutualExclusions<[Mips16, MipsInterrupt]>; def MicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> { let Spellings = [GCC<"micromips">]; let Subjects = SubjectList<[Function], ErrorDiag>; let Documentation = [MicroMipsDocs]; + let SimpleHandler = 1; } +def : MutualExclusions<[Mips16, MicroMips]>; def MipsLongCall : InheritableAttr, TargetSpecificAttr<TargetAnyMips> { let Spellings = [GCC<"long_call">, GCC<"far">]; let Subjects = SubjectList<[Function]>; let Documentation = [MipsLongCallStyleDocs]; + let SimpleHandler = 1; } def MipsShortCall : InheritableAttr, TargetSpecificAttr<TargetAnyMips> { 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<TargetM68k> { // 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/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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits