Skip to content

Commit 6422bcf

Browse files
AaronBallmanmemfrob
authored andcommitted
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.
1 parent bdafe1e commit 6422bcf

File tree

14 files changed

+237
-195
lines changed

14 files changed

+237
-195
lines changed

clang/docs/InternalsManual.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3033,6 +3033,13 @@ If additional functionality is desired for the semantic form of the attribute,
30333033
the ``AdditionalMembers`` field specifies code to be copied verbatim into the
30343034
semantic attribute class object, with ``public`` access.
30353035

3036+
If two or more attributes cannot be used in combination on the same declaration
3037+
or statement, a ``MutualExclusions`` definition can be supplied to automatically
3038+
generate diagnostic code. This will disallow the attribute combinations
3039+
regardless of spellings used. Additionally, it will diagnose combinations within
3040+
the same attribute list, different attribute list, and redeclarations, as
3041+
appropriate.
3042+
30363043
Boilerplate
30373044
^^^^^^^^^^^
30383045
All semantic processing of declaration attributes happens in `lib/Sema/SemaDeclAttr.cpp

clang/include/clang/Basic/Attr.td

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,11 @@ class Attr {
554554
list<Documentation> Documentation;
555555
}
556556

557+
/// Used to define a set of mutually exclusive attributes.
558+
class MutualExclusions<list<Attr> Ex> {
559+
list<Attr> Exclusions = Ex;
560+
}
561+
557562
/// A type attribute is not processed on a declaration or a statement.
558563
class TypeAttr : Attr;
559564

@@ -918,6 +923,7 @@ def CFAuditedTransfer : InheritableAttr {
918923
let Spellings = [Clang<"cf_audited_transfer">];
919924
let Subjects = SubjectList<[Function], ErrorDiag>;
920925
let Documentation = [Undocumented];
926+
let SimpleHandler = 1;
921927
}
922928

923929
// cf_unknown_transfer is an explicit opt-out of cf_audited_transfer.
@@ -927,7 +933,9 @@ def CFUnknownTransfer : InheritableAttr {
927933
let Spellings = [Clang<"cf_unknown_transfer">];
928934
let Subjects = SubjectList<[Function], ErrorDiag>;
929935
let Documentation = [Undocumented];
936+
let SimpleHandler = 1;
930937
}
938+
def : MutualExclusions<[CFAuditedTransfer, CFUnknownTransfer]>;
931939

932940
def CFReturnsRetained : InheritableAttr {
933941
let Spellings = [Clang<"cf_returns_retained">];
@@ -1009,6 +1017,7 @@ def Cold : InheritableAttr {
10091017
let Spellings = [GCC<"cold">];
10101018
let Subjects = SubjectList<[Function]>;
10111019
let Documentation = [Undocumented];
1020+
let SimpleHandler = 1;
10121021
}
10131022

10141023
def Common : InheritableAttr {
@@ -1094,6 +1103,7 @@ def CUDADeviceBuiltinSurfaceType : InheritableAttr {
10941103
let Subjects = SubjectList<[CXXRecord]>;
10951104
let Documentation = [CUDADeviceBuiltinSurfaceTypeDocs];
10961105
let MeaningfulToClassTemplateDefinition = 1;
1106+
let SimpleHandler = 1;
10971107
}
10981108

10991109
def CUDADeviceBuiltinTextureType : InheritableAttr {
@@ -1103,21 +1113,27 @@ def CUDADeviceBuiltinTextureType : InheritableAttr {
11031113
let Subjects = SubjectList<[CXXRecord]>;
11041114
let Documentation = [CUDADeviceBuiltinTextureTypeDocs];
11051115
let MeaningfulToClassTemplateDefinition = 1;
1116+
let SimpleHandler = 1;
11061117
}
1118+
def : MutualExclusions<[CUDADeviceBuiltinSurfaceType,
1119+
CUDADeviceBuiltinTextureType]>;
11071120

11081121
def CUDAGlobal : InheritableAttr {
11091122
let Spellings = [GNU<"global">, Declspec<"__global__">];
11101123
let Subjects = SubjectList<[Function]>;
11111124
let LangOpts = [CUDA];
11121125
let Documentation = [Undocumented];
11131126
}
1127+
def : MutualExclusions<[CUDADevice, CUDAGlobal]>;
11141128

11151129
def CUDAHost : InheritableAttr {
11161130
let Spellings = [GNU<"host">, Declspec<"__host__">];
11171131
let Subjects = SubjectList<[Function]>;
11181132
let LangOpts = [CUDA];
11191133
let Documentation = [Undocumented];
1134+
let SimpleHandler = 1;
11201135
}
1136+
def : MutualExclusions<[CUDAGlobal, CUDAHost]>;
11211137

11221138
def HIPManaged : InheritableAttr {
11231139
let Spellings = [GNU<"managed">, Declspec<"__managed__">];
@@ -1150,6 +1166,7 @@ def CUDAShared : InheritableAttr {
11501166
let LangOpts = [CUDA];
11511167
let Documentation = [Undocumented];
11521168
}
1169+
def : MutualExclusions<[CUDAConstant, CUDAShared, HIPManaged]>;
11531170

11541171
def SYCLKernel : InheritableAttr {
11551172
let Spellings = [Clang<"sycl_kernel">];
@@ -1342,6 +1359,7 @@ def Unlikely : StmtAttr {
13421359
let Spellings = [CXX11<"", "unlikely", 201803>, C2x<"clang", "unlikely">];
13431360
let Documentation = [LikelihoodDocs];
13441361
}
1362+
def : MutualExclusions<[Likely, Unlikely]>;
13451363

13461364
def NoMerge : DeclOrStmtAttr {
13471365
let Spellings = [Clang<"nomerge">];
@@ -1433,7 +1451,9 @@ def Hot : InheritableAttr {
14331451
// An AST node is created for this attribute, but not actually used beyond
14341452
// semantic checking for mutual exclusion with the Cold attribute.
14351453
let Documentation = [Undocumented];
1454+
let SimpleHandler = 1;
14361455
}
1456+
def : MutualExclusions<[Hot, Cold]>;
14371457

14381458
def IBAction : InheritableAttr {
14391459
let Spellings = [Clang<"ibaction">];
@@ -1544,6 +1564,7 @@ def Mips16 : InheritableAttr, TargetSpecificAttr<TargetMips32> {
15441564
let Spellings = [GCC<"mips16">];
15451565
let Subjects = SubjectList<[Function], ErrorDiag>;
15461566
let Documentation = [Undocumented];
1567+
let SimpleHandler = 1;
15471568
}
15481569

15491570
def MipsInterrupt : InheritableAttr, TargetSpecificAttr<TargetMips32> {
@@ -1562,24 +1583,30 @@ def MipsInterrupt : InheritableAttr, TargetSpecificAttr<TargetMips32> {
15621583
let ParseKind = "Interrupt";
15631584
let Documentation = [MipsInterruptDocs];
15641585
}
1586+
def : MutualExclusions<[Mips16, MipsInterrupt]>;
15651587

15661588
def MicroMips : InheritableAttr, TargetSpecificAttr<TargetMips32> {
15671589
let Spellings = [GCC<"micromips">];
15681590
let Subjects = SubjectList<[Function], ErrorDiag>;
15691591
let Documentation = [MicroMipsDocs];
1592+
let SimpleHandler = 1;
15701593
}
1594+
def : MutualExclusions<[Mips16, MicroMips]>;
15711595

15721596
def MipsLongCall : InheritableAttr, TargetSpecificAttr<TargetAnyMips> {
15731597
let Spellings = [GCC<"long_call">, GCC<"far">];
15741598
let Subjects = SubjectList<[Function]>;
15751599
let Documentation = [MipsLongCallStyleDocs];
1600+
let SimpleHandler = 1;
15761601
}
15771602

15781603
def MipsShortCall : InheritableAttr, TargetSpecificAttr<TargetAnyMips> {
15791604
let Spellings = [GCC<"short_call">, GCC<"near">];
15801605
let Subjects = SubjectList<[Function]>;
15811606
let Documentation = [MipsShortCallStyleDocs];
1607+
let SimpleHandler = 1;
15821608
}
1609+
def : MutualExclusions<[MipsLongCall, MipsShortCall]>;
15831610

15841611
def M68kInterrupt : InheritableAttr, TargetSpecificAttr<TargetM68k> {
15851612
// NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's
@@ -1656,7 +1683,9 @@ def DisableTailCalls : InheritableAttr {
16561683
let Spellings = [Clang<"disable_tail_calls">];
16571684
let Subjects = SubjectList<[Function, ObjCMethod]>;
16581685
let Documentation = [DisableTailCallsDocs];
1686+
let SimpleHandler = 1;
16591687
}
1688+
def : MutualExclusions<[Naked, DisableTailCalls]>;
16601689

16611690
def NoAlias : InheritableAttr {
16621691
let Spellings = [Declspec<"noalias">];
@@ -1930,7 +1959,9 @@ def NotTailCalled : InheritableAttr {
19301959
let Spellings = [Clang<"not_tail_called">];
19311960
let Subjects = SubjectList<[Function]>;
19321961
let Documentation = [NotTailCalledDocs];
1962+
let SimpleHandler = 1;
19331963
}
1964+
def : MutualExclusions<[AlwaysInline, NotTailCalled]>;
19341965

19351966
def NoStackProtector : InheritableAttr {
19361967
let Spellings = [Clang<"no_stack_protector">];
@@ -3248,6 +3279,7 @@ def Pointer : InheritableAttr {
32483279
let Args = [TypeArgument<"DerefType", /*opt=*/1>];
32493280
let Documentation = [LifetimePointerDocs];
32503281
}
3282+
def : MutualExclusions<[Owner, Pointer]>;
32513283

32523284
// Microsoft-related attributes
32533285

@@ -3620,6 +3652,7 @@ def InternalLinkage : InheritableAttr {
36203652
let Subjects = SubjectList<[Var, Function, CXXRecord]>;
36213653
let Documentation = [InternalLinkageDocs];
36223654
}
3655+
def : MutualExclusions<[Common, InternalLinkage]>;
36233656

36243657
def ExcludeFromExplicitInstantiation : InheritableAttr {
36253658
let Spellings = [Clang<"exclude_from_explicit_instantiation">];
@@ -3647,18 +3680,22 @@ def AlwaysDestroy : InheritableAttr {
36473680
let Subjects = SubjectList<[Var]>;
36483681
let Documentation = [AlwaysDestroyDocs];
36493682
}
3683+
def : MutualExclusions<[NoDestroy, AlwaysDestroy]>;
36503684

36513685
def SpeculativeLoadHardening : InheritableAttr {
36523686
let Spellings = [Clang<"speculative_load_hardening">];
36533687
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
36543688
let Documentation = [SpeculativeLoadHardeningDocs];
3689+
let SimpleHandler = 1;
36553690
}
36563691

36573692
def NoSpeculativeLoadHardening : InheritableAttr {
36583693
let Spellings = [Clang<"no_speculative_load_hardening">];
36593694
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
36603695
let Documentation = [NoSpeculativeLoadHardeningDocs];
3696+
let SimpleHandler = 1;
36613697
}
3698+
def : MutualExclusions<[SpeculativeLoadHardening, NoSpeculativeLoadHardening]>;
36623699

36633700
def Uninitialized : InheritableAttr {
36643701
let Spellings = [Clang<"uninitialized", 0>];

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ struct ParsedAttrInfo {
8686
const Stmt *St) const {
8787
return true;
8888
}
89+
/// Check if the given attribute is mutually exclusive with other attributes
90+
/// already applied to the given declaration.
91+
virtual bool diagMutualExclusion(Sema &S, const ParsedAttr &A,
92+
const Decl *D) const {
93+
return true;
94+
}
95+
/// Check if the given attribute is mutually exclusive with other attributes
96+
/// already applied to the given statement.
97+
virtual bool diagMutualExclusion(Sema &S, const ParsedAttr &A,
98+
const Stmt *St) const {
99+
return true;
100+
}
89101
/// Check if this attribute is allowed by the language we are compiling, and
90102
/// issue a diagnostic if not.
91103
virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const {
@@ -599,6 +611,8 @@ class ParsedAttr final
599611
bool hasVariadicArg() const;
600612
bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const;
601613
bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const;
614+
bool diagnoseMutualExclusion(class Sema &S, const Decl *D) const;
615+
bool diagnoseMutualExclusion(class Sema &S, const Stmt *St) const;
602616
bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const;
603617
void getMatchRules(const LangOptions &LangOpts,
604618
SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>>

clang/include/clang/Sema/Sema.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,21 +3304,13 @@ class Sema final {
33043304
const AttributeCommonInfo &CI,
33053305
const IdentifierInfo *Ident);
33063306
MinSizeAttr *mergeMinSizeAttr(Decl *D, const AttributeCommonInfo &CI);
3307-
NoSpeculativeLoadHardeningAttr *
3308-
mergeNoSpeculativeLoadHardeningAttr(Decl *D,
3309-
const NoSpeculativeLoadHardeningAttr &AL);
3310-
SpeculativeLoadHardeningAttr *
3311-
mergeSpeculativeLoadHardeningAttr(Decl *D,
3312-
const SpeculativeLoadHardeningAttr &AL);
33133307
SwiftNameAttr *mergeSwiftNameAttr(Decl *D, const SwiftNameAttr &SNA,
33143308
StringRef Name);
33153309
OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D,
33163310
const AttributeCommonInfo &CI);
33173311
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, const ParsedAttr &AL);
33183312
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D,
33193313
const InternalLinkageAttr &AL);
3320-
CommonAttr *mergeCommonAttr(Decl *D, const ParsedAttr &AL);
3321-
CommonAttr *mergeCommonAttr(Decl *D, const CommonAttr &AL);
33223314
WebAssemblyImportNameAttr *mergeImportNameAttr(
33233315
Decl *D, const WebAssemblyImportNameAttr &AL);
33243316
WebAssemblyImportModuleAttr *mergeImportModuleAttr(

clang/lib/Sema/ParsedAttr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ bool ParsedAttr::diagnoseAppertainsTo(Sema &S, const Stmt *St) const {
163163
return getInfo().diagAppertainsToStmt(S, *this, St);
164164
}
165165

166+
bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Decl *D) const {
167+
return getInfo().diagMutualExclusion(S, *this, D);
168+
}
169+
170+
bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Stmt *St) const {
171+
return getInfo().diagMutualExclusion(S, *this, St);
172+
}
173+
166174
bool ParsedAttr::appliesToDecl(const Decl *D,
167175
attr::SubjectMatchRule MatchRule) const {
168176
return checkAttributeMatchRuleAppliesTo(D, MatchRule);

clang/lib/Sema/SemaAttr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,10 @@ static bool checkCommonAttributeFeatures(Sema& S, const Ty *Node,
12061206
// Check whether the attribute appertains to the given subject.
12071207
if (!A.diagnoseAppertainsTo(S, Node))
12081208
return true;
1209+
// Check whether the attribute is mutually exclusive with other attributes
1210+
// that have already been applied to the declaration.
1211+
if (!A.diagnoseMutualExclusion(S, Node))
1212+
return true;
12091213
// Check whether the attribute exists in the target architecture.
12101214
if (S.CheckAttrTarget(A))
12111215
return true;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,9 +2540,18 @@ static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) {
25402540
return AnyAdded;
25412541
}
25422542

2543+
#define WANT_MERGE_LOGIC
2544+
#include "clang/Sema/AttrParsedAttrImpl.inc"
2545+
#undef WANT_MERGE_LOGIC
2546+
25432547
static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
25442548
const InheritableAttr *Attr,
25452549
Sema::AvailabilityMergeKind AMK) {
2550+
// Diagnose any mutual exclusions between the attribute that we want to add
2551+
// and attributes that already exist on the declaration.
2552+
if (!DiagnoseMutualExclusions(S, D, Attr))
2553+
return false;
2554+
25462555
// This function copies an attribute Attr from a previous declaration to the
25472556
// new declaration D if the new declaration doesn't itself have that attribute
25482557
// yet or if that attribute allows duplicates.
@@ -2592,8 +2601,6 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
25922601
NewAttr = S.mergeOptimizeNoneAttr(D, *OA);
25932602
else if (const auto *InternalLinkageA = dyn_cast<InternalLinkageAttr>(Attr))
25942603
NewAttr = S.mergeInternalLinkageAttr(D, *InternalLinkageA);
2595-
else if (const auto *CommonA = dyn_cast<CommonAttr>(Attr))
2596-
NewAttr = S.mergeCommonAttr(D, *CommonA);
25972604
else if (isa<AlignedAttr>(Attr))
25982605
// AlignedAttrs are handled separately, because we need to handle all
25992606
// such attributes on a declaration at the same time.
@@ -2604,10 +2611,6 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26042611
NewAttr = nullptr;
26052612
else if (const auto *UA = dyn_cast<UuidAttr>(Attr))
26062613
NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl());
2607-
else if (const auto *SLHA = dyn_cast<SpeculativeLoadHardeningAttr>(Attr))
2608-
NewAttr = S.mergeSpeculativeLoadHardeningAttr(D, *SLHA);
2609-
else if (const auto *SLHA = dyn_cast<NoSpeculativeLoadHardeningAttr>(Attr))
2610-
NewAttr = S.mergeNoSpeculativeLoadHardeningAttr(D, *SLHA);
26112614
else if (const auto *IMA = dyn_cast<WebAssemblyImportModuleAttr>(Attr))
26122615
NewAttr = S.mergeImportModuleAttr(D, *IMA);
26132616
else if (const auto *INA = dyn_cast<WebAssemblyImportNameAttr>(Attr))

0 commit comments

Comments
 (0)