Skip to content

Commit 354359e

Browse files
committed
[clang][TSA] Make RequiresCapability a DeclOrType attribute
1 parent 31abb20 commit 354359e

File tree

8 files changed

+79
-29
lines changed

8 files changed

+79
-29
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3851,7 +3851,7 @@ def ReleaseCapability : InheritableAttr {
38513851
let Documentation = [ReleaseCapabilityDocs];
38523852
}
38533853

3854-
def RequiresCapability : InheritableAttr {
3854+
def RequiresCapability : DeclOrTypeAttr {
38553855
let Spellings = [Clang<"requires_capability", 0>,
38563856
Clang<"exclusive_locks_required", 0>,
38573857
Clang<"requires_shared_capability", 0>,
@@ -3861,16 +3861,16 @@ def RequiresCapability : InheritableAttr {
38613861
let TemplateDependent = 1;
38623862
let ParseArgumentsAsUnevaluated = 1;
38633863
let InheritEvenIfAlreadyPresent = 1;
3864-
let Subjects = SubjectList<[Function, ParmVar]>;
3864+
let Subjects = SubjectList<[Function, FunctionPointer, ParmVar]>;
38653865
let Accessors = [Accessor<"isShared", [Clang<"requires_shared_capability", 0>,
38663866
Clang<"shared_locks_required", 0>]>];
3867-
let Documentation = [Undocumented];
3867+
let Documentation = [ThreadSafetyDocs];
38683868
}
38693869

38703870
def NoThreadSafetyAnalysis : InheritableAttr {
38713871
let Spellings = [Clang<"no_thread_safety_analysis">];
38723872
let Subjects = SubjectList<[Function]>;
3873-
let Documentation = [Undocumented];
3873+
let Documentation = [ThreadSafetyDocs];
38743874
let SimpleHandler = 1;
38753875
}
38763876

@@ -3882,7 +3882,7 @@ def GuardedBy : InheritableAttr {
38823882
let ParseArgumentsAsUnevaluated = 1;
38833883
let InheritEvenIfAlreadyPresent = 1;
38843884
let Subjects = SubjectList<[Field, SharedVar]>;
3885-
let Documentation = [Undocumented];
3885+
let Documentation = [ThreadSafetyDocs];
38863886
}
38873887

38883888
def PtGuardedBy : InheritableAttr {
@@ -3893,7 +3893,7 @@ def PtGuardedBy : InheritableAttr {
38933893
let ParseArgumentsAsUnevaluated = 1;
38943894
let InheritEvenIfAlreadyPresent = 1;
38953895
let Subjects = SubjectList<[Field, SharedVar]>;
3896-
let Documentation = [Undocumented];
3896+
let Documentation = [ThreadSafetyDocs];
38973897
}
38983898

38993899
def AcquiredAfter : InheritableAttr {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9037,3 +9037,10 @@ Declares that a function potentially allocates heap memory, and prevents any pot
90379037
of ``nonallocating`` by the compiler.
90389038
}];
90399039
}
9040+
9041+
def ThreadSafetyDocs : Documentation {
9042+
let Category = DocCatFunction;
9043+
let Content = [{
9044+
Part of Thread Safety Analysis (TSA) in Clang, as documented at https://clang.llvm.org/docs/ThreadSafetyAnalysis.html.
9045+
}];
9046+
}

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13895,6 +13895,12 @@ class Sema final : public SemaBase {
1389513895
LocalInstantiationScope &Scope,
1389613896
const MultiLevelTemplateArgumentList &TemplateArgs);
1389713897

13898+
public:
13899+
void checkAttrArgsAreCapabilityObjs(Decl *D, const ParsedAttr &AL,
13900+
SmallVectorImpl<Expr *> &Args,
13901+
unsigned Sidx = 0,
13902+
bool ParamIdxOk = false);
13903+
1389813904
int ParsingClassDepth = 0;
1389913905

1390013906
class SavePendingParsedClassStateRAII {

clang/lib/AST/TypePrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2073,6 +2073,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
20732073
case attr::ArmMveStrictPolymorphism:
20742074
OS << "__clang_arm_mve_strict_polymorphism";
20752075
break;
2076+
case attr::RequiresCapability:
2077+
OS << "requires_capability(...)";
2078+
break;
20762079
}
20772080
OS << "))";
20782081
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,10 @@ static bool isCapabilityExpr(Sema &S, const Expr *Ex) {
337337
/// \param Sidx The attribute argument index to start checking with.
338338
/// \param ParamIdxOk Whether an argument can be indexing into a function
339339
/// parameter list.
340-
static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
341-
const ParsedAttr &AL,
342-
SmallVectorImpl<Expr *> &Args,
343-
unsigned Sidx = 0,
344-
bool ParamIdxOk = false) {
345-
if (Sidx == AL.getNumArgs()) {
340+
void Sema::checkAttrArgsAreCapabilityObjs(Decl *D, const ParsedAttr &AL,
341+
SmallVectorImpl<Expr *> &Args,
342+
unsigned Sidx, bool ParamIdxOk) {
343+
if (D && Sidx == AL.getNumArgs()) {
346344
// If we don't have any capability arguments, the attribute implicitly
347345
// refers to 'this'. So we need to make sure that 'this' exists, i.e. we're
348346
// a non-static method, and that the class is a (scoped) capability.
@@ -352,11 +350,10 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
352350
// FIXME -- need to check this again on template instantiation
353351
if (!checkRecordDeclForAttr<CapabilityAttr>(RD) &&
354352
!checkRecordDeclForAttr<ScopedLockableAttr>(RD))
355-
S.Diag(AL.getLoc(),
356-
diag::warn_thread_attribute_not_on_capability_member)
353+
Diag(AL.getLoc(), diag::warn_thread_attribute_not_on_capability_member)
357354
<< AL << MD->getParent();
358355
} else {
359-
S.Diag(AL.getLoc(), diag::warn_thread_attribute_not_on_non_static_member)
356+
Diag(AL.getLoc(), diag::warn_thread_attribute_not_on_non_static_member)
360357
<< AL;
361358
}
362359
}
@@ -381,7 +378,7 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
381378

382379
// We allow constant strings to be used as a placeholder for expressions
383380
// that are not valid C++ syntax, but warn that they are ignored.
384-
S.Diag(AL.getLoc(), diag::warn_thread_attribute_ignored) << AL;
381+
Diag(AL.getLoc(), diag::warn_thread_attribute_ignored) << AL;
385382
Args.push_back(ArgExp);
386383
continue;
387384
}
@@ -400,7 +397,7 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
400397
const RecordType *RT = getRecordType(ArgTy);
401398

402399
// Now check if we index into a record type function param.
403-
if(!RT && ParamIdxOk) {
400+
if (D && !RT && ParamIdxOk) {
404401
const auto *FD = dyn_cast<FunctionDecl>(D);
405402
const auto *IL = dyn_cast<IntegerLiteral>(ArgExp);
406403
if(FD && IL) {
@@ -409,8 +406,8 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
409406
uint64_t ParamIdxFromOne = ArgValue.getZExtValue();
410407
uint64_t ParamIdxFromZero = ParamIdxFromOne - 1;
411408
if (!ArgValue.isStrictlyPositive() || ParamIdxFromOne > NumParams) {
412-
S.Diag(AL.getLoc(),
413-
diag::err_attribute_argument_out_of_bounds_extra_info)
409+
Diag(AL.getLoc(),
410+
diag::err_attribute_argument_out_of_bounds_extra_info)
414411
<< AL << Idx + 1 << NumParams;
415412
continue;
416413
}
@@ -422,8 +419,8 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
422419
// expression have capabilities. This allows for writing C code where the
423420
// capability may be on the type, and the expression is a capability
424421
// boolean logic expression. Eg) requires_capability(A || B && !C)
425-
if (!typeHasCapability(S, ArgTy) && !isCapabilityExpr(S, ArgExp))
426-
S.Diag(AL.getLoc(), diag::warn_thread_attribute_argument_not_lockable)
422+
if (!typeHasCapability(*this, ArgTy) && !isCapabilityExpr(*this, ArgExp))
423+
Diag(AL.getLoc(), diag::warn_thread_attribute_argument_not_lockable)
427424
<< AL << ArgTy;
428425

429426
Args.push_back(ArgExp);
@@ -458,7 +455,7 @@ static bool checkGuardedByAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
458455
Expr *&Arg) {
459456
SmallVector<Expr *, 1> Args;
460457
// check that all arguments are lockable objects
461-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args);
458+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args);
462459
unsigned Size = Args.size();
463460
if (Size != 1)
464461
return false;
@@ -500,7 +497,7 @@ static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
500497
}
501498

502499
// Check that all arguments are lockable objects.
503-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args);
500+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args);
504501
if (Args.empty())
505502
return false;
506503

@@ -531,7 +528,7 @@ static bool checkLockFunAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
531528
SmallVectorImpl<Expr *> &Args) {
532529
// zero or more arguments ok
533530
// check that all arguments are lockable objects
534-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args, 0, /*ParamIdxOk=*/true);
531+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args, 0, /*ParamIdxOk=*/true);
535532

536533
return true;
537534
}
@@ -633,7 +630,7 @@ static bool checkTryLockFunAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
633630
}
634631

635632
// check that all arguments are lockable objects
636-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args, 1);
633+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args, 1);
637634

638635
return true;
639636
}
@@ -661,7 +658,7 @@ static void handleExclusiveTrylockFunctionAttr(Sema &S, Decl *D,
661658
static void handleLockReturnedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
662659
// check that the argument is lockable object
663660
SmallVector<Expr*, 1> Args;
664-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args);
661+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args);
665662
unsigned Size = Args.size();
666663
if (Size == 0)
667664
return;
@@ -679,7 +676,7 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
679676

680677
// check that all arguments are lockable objects
681678
SmallVector<Expr*, 1> Args;
682-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args);
679+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args);
683680
unsigned Size = Args.size();
684681
if (Size == 0)
685682
return;
@@ -6020,7 +6017,7 @@ static void handleReleaseCapabilityAttr(Sema &S, Decl *D,
60206017
return;
60216018
// Check that all arguments are lockable objects.
60226019
SmallVector<Expr *, 1> Args;
6023-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args, 0, true);
6020+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args, 0, true);
60246021

60256022
D->addAttr(::new (S.Context) ReleaseCapabilityAttr(S.Context, AL, Args.data(),
60266023
Args.size()));
@@ -6037,7 +6034,7 @@ static void handleRequiresCapabilityAttr(Sema &S, Decl *D,
60376034

60386035
// check that all arguments are lockable objects
60396036
SmallVector<Expr*, 1> Args;
6040-
checkAttrArgsAreCapabilityObjs(S, D, AL, Args);
6037+
S.checkAttrArgsAreCapabilityObjs(D, AL, Args);
60416038
if (Args.empty())
60426039
return;
60436040

clang/lib/Sema/SemaType.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8679,6 +8679,25 @@ static void HandleAnnotateTypeAttr(TypeProcessingState &State,
86798679
CurType = State.getAttributedType(AnnotateTypeAttr, CurType, CurType);
86808680
}
86818681

8682+
static void HandleRequiresCapabilityAttr(TypeProcessingState &State,
8683+
QualType &CurType,
8684+
const ParsedAttr &PA) {
8685+
Sema &S = State.getSema();
8686+
8687+
if (PA.getNumArgs() < 1) {
8688+
// Already diganosed elsewhere, just ignore.
8689+
return;
8690+
}
8691+
8692+
llvm::SmallVector<Expr *, 4> Args;
8693+
Args.reserve(PA.getNumArgs() - 1);
8694+
State.getSema().checkAttrArgsAreCapabilityObjs(/*Decl=*/nullptr, PA, Args);
8695+
8696+
auto *RCAttr =
8697+
RequiresCapabilityAttr::Create(S.Context, Args.data(), Args.size(), PA);
8698+
CurType = State.getAttributedType(RCAttr, CurType, CurType);
8699+
}
8700+
86828701
static void HandleLifetimeBoundAttr(TypeProcessingState &State,
86838702
QualType &CurType,
86848703
ParsedAttr &Attr) {
@@ -9026,6 +9045,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
90269045
attr.setUsedAsTypeAttr();
90279046
break;
90289047
}
9048+
9049+
case ParsedAttr::AT_RequiresCapability: {
9050+
HandleRequiresCapabilityAttr(state, type, attr);
9051+
attr.setUsedAsTypeAttr();
9052+
break;
9053+
}
90299054
}
90309055

90319056
// Handle attributes that are defined in a macro. We do not want this to be

clang/test/Sema/warn-thread-safety-analysis.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ int main(void) {
189189
mutex_exclusive_unlock(late_parsing.a_mutex_defined_very_late);
190190
mutex_exclusive_unlock(late_parsing.a_mutex_defined_late);
191191
#endif
192+
/// Function pointers
193+
{
194+
int __attribute__((requires_capability(&mu1))) (*function_ptr)(int) = Foo_fun1;
195+
196+
function_ptr(5); // expected-warning {{calling function 'function_ptr' requires holding mutex 'mu1'}}
197+
198+
mutex_exclusive_lock(&mu1);
199+
int five = function_ptr(5);
200+
mutex_exclusive_unlock(&mu1);
201+
}
192202

193203
return 0;
194204
}

clang/test/SemaCXX/warn-thread-safety-parsing.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,8 @@ void elr_function_args() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2);
11091109

11101110
int elr_testfn(int y) EXCLUSIVE_LOCKS_REQUIRED(mu1);
11111111

1112+
int EXCLUSIVE_LOCKS_REQUIRED(mu1) (*function_ptr)(int);
1113+
11121114
int elr_testfn(int y) {
11131115
int x EXCLUSIVE_LOCKS_REQUIRED(mu1) = y; // \
11141116
// expected-warning {{'exclusive_locks_required' attribute only applies to functions}}

0 commit comments

Comments
 (0)