Skip to content

Commit c8ba556

Browse files
authored
Support guarded_by attribute and related attributes inside C structs and support late parsing them (llvm#95455)
This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged. Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration. This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g. ``` struct Example { int a_value_defined_before __attribute__ ((guarded_by(a_mutex))); struct Mutex *a_mutex; }; ```
1 parent 7221516 commit c8ba556

File tree

8 files changed

+134
-24
lines changed

8 files changed

+134
-24
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,10 @@ Attribute Changes in Clang
549549
size_t count;
550550
};
551551
552+
- The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before``
553+
attributes now support referencing struct members in C. The arguments are also
554+
now late parsed when ``-fexperimental-late-parse-attributes`` is passed like
555+
for ``counted_by``.
552556

553557
- Introduced new function type attributes ``[[clang::nonblocking]]``, ``[[clang::nonallocating]]``,
554558
``[[clang::blocking]]``, and ``[[clang::allocating]]``, with GNU-style variants as well.

clang/docs/ThreadSafetyAnalysis.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -764,10 +764,11 @@ doesn't know that munl.mu == mutex. The SCOPED_CAPABILITY attribute handles
764764
aliasing for MutexLocker, but does so only for that particular pattern.
765765

766766

767-
ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) are currently unimplemented.
768-
-------------------------------------------------------------------------
767+
ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) support is still experimental.
768+
---------------------------------------------------------------------------
769769

770-
To be fixed in a future update.
770+
ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) are currently being developed under
771+
the ``-Wthread-safety-beta`` flag.
771772

772773

773774
.. _mutexheader:

clang/include/clang/Basic/Attr.td

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3660,7 +3660,7 @@ def NoThreadSafetyAnalysis : InheritableAttr {
36603660
def GuardedBy : InheritableAttr {
36613661
let Spellings = [GNU<"guarded_by">];
36623662
let Args = [ExprArgument<"Arg">];
3663-
let LateParsed = LateAttrParseStandard;
3663+
let LateParsed = LateAttrParseExperimentalExt;
36643664
let TemplateDependent = 1;
36653665
let ParseArgumentsAsUnevaluated = 1;
36663666
let InheritEvenIfAlreadyPresent = 1;
@@ -3671,7 +3671,7 @@ def GuardedBy : InheritableAttr {
36713671
def PtGuardedBy : InheritableAttr {
36723672
let Spellings = [GNU<"pt_guarded_by">];
36733673
let Args = [ExprArgument<"Arg">];
3674-
let LateParsed = LateAttrParseStandard;
3674+
let LateParsed = LateAttrParseExperimentalExt;
36753675
let TemplateDependent = 1;
36763676
let ParseArgumentsAsUnevaluated = 1;
36773677
let InheritEvenIfAlreadyPresent = 1;
@@ -3682,7 +3682,7 @@ def PtGuardedBy : InheritableAttr {
36823682
def AcquiredAfter : InheritableAttr {
36833683
let Spellings = [GNU<"acquired_after">];
36843684
let Args = [VariadicExprArgument<"Args">];
3685-
let LateParsed = LateAttrParseStandard;
3685+
let LateParsed = LateAttrParseExperimentalExt;
36863686
let TemplateDependent = 1;
36873687
let ParseArgumentsAsUnevaluated = 1;
36883688
let InheritEvenIfAlreadyPresent = 1;
@@ -3693,7 +3693,7 @@ def AcquiredAfter : InheritableAttr {
36933693
def AcquiredBefore : InheritableAttr {
36943694
let Spellings = [GNU<"acquired_before">];
36953695
let Args = [VariadicExprArgument<"Args">];
3696-
let LateParsed = LateAttrParseStandard;
3696+
let LateParsed = LateAttrParseExperimentalExt;
36973697
let TemplateDependent = 1;
36983698
let ParseArgumentsAsUnevaluated = 1;
36993699
let InheritEvenIfAlreadyPresent = 1;

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6341,7 +6341,7 @@ class Sema final : public SemaBase {
63416341
enum ExpressionKind {
63426342
EK_Decltype,
63436343
EK_TemplateArgument,
6344-
EK_BoundsAttrArgument,
6344+
EK_AttrArgument,
63456345
EK_Other
63466346
} ExprContext;
63476347

@@ -6454,10 +6454,9 @@ class Sema final : public SemaBase {
64546454
return const_cast<Sema *>(this)->parentEvaluationContext();
64556455
};
64566456

6457-
bool isBoundsAttrContext() const {
6457+
bool isAttrContext() const {
64586458
return ExprEvalContexts.back().ExprContext ==
6459-
ExpressionEvaluationContextRecord::ExpressionKind::
6460-
EK_BoundsAttrArgument;
6459+
ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument;
64616460
}
64626461

64636462
/// Increment when we find a reference; decrement when we find an ignored

clang/lib/Parse/ParseDecl.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,9 @@ unsigned Parser::ParseAttributeArgsCommon(
593593
EnterExpressionEvaluationContext Unevaluated(
594594
Actions,
595595
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
596-
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
596+
: Sema::ExpressionEvaluationContext::ConstantEvaluated,
597+
nullptr,
598+
Sema::ExpressionEvaluationContextRecord::EK_AttrArgument);
597599

598600
ExprResult ArgExpr(
599601
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
@@ -610,9 +612,12 @@ unsigned Parser::ParseAttributeArgsCommon(
610612
// General case. Parse all available expressions.
611613
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
612614
EnterExpressionEvaluationContext Unevaluated(
613-
Actions, Uneval
614-
? Sema::ExpressionEvaluationContext::Unevaluated
615-
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
615+
Actions,
616+
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
617+
: Sema::ExpressionEvaluationContext::ConstantEvaluated,
618+
nullptr,
619+
Sema::ExpressionEvaluationContextRecord::ExpressionKind::
620+
EK_AttrArgument);
616621

617622
ExprVector ParsedExprs;
618623
ParsedAttributeArgumentsProperties ArgProperties =
@@ -3383,7 +3388,7 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
33833388
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
33843389
EnterExpressionEvaluationContext EC(
33853390
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3386-
ExpressionKind::EK_BoundsAttrArgument);
3391+
ExpressionKind::EK_AttrArgument);
33873392

33883393
ExprResult ArgExpr(
33893394
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));

clang/lib/Sema/SemaExpr.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,11 +2666,10 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
26662666
return ExprError();
26672667
}
26682668

2669-
// BoundsSafety: This specially handles arguments of bounds attributes
2670-
// appertains to a type of C struct field such that the name lookup
2671-
// within a struct finds the member name, which is not the case for other
2672-
// contexts in C.
2673-
if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
2669+
// This specially handles arguments of attributes appertains to a type of C
2670+
// struct field such that the name lookup within a struct finds the member
2671+
// name, which is not the case for other contexts in C.
2672+
if (isAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
26742673
// See if this is reference to a field of struct.
26752674
LookupResult R(*this, NameInfo, LookupMemberName);
26762675
// LookupName handles a name lookup from within anonymous struct.
@@ -3279,7 +3278,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
32793278
case Decl::Field:
32803279
case Decl::IndirectField:
32813280
case Decl::ObjCIvar:
3282-
assert((getLangOpts().CPlusPlus || isBoundsAttrContext()) &&
3281+
assert((getLangOpts().CPlusPlus || isAttrContext()) &&
32833282
"building reference to field in C?");
32843283

32853284
// These can't have reference type in well-formed programs, but

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

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -Wthread-safety-beta %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety -Wthread-safety-beta -fexperimental-late-parse-attributes -DLATE_PARSING %s
23

34
#define LOCKABLE __attribute__ ((lockable))
45
#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
5-
#define GUARDED_BY(x) __attribute__ ((guarded_by(x)))
6+
#define GUARDED_BY(...) __attribute__ ((guarded_by(__VA_ARGS__)))
67
#define GUARDED_VAR __attribute__ ((guarded_var))
7-
#define PT_GUARDED_BY(x) __attribute__ ((pt_guarded_by(x)))
8+
#define PT_GUARDED_BY(...) __attribute__ ((pt_guarded_by(__VA_ARGS__)))
89
#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var))
910
#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__)))
1011
#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__)))
@@ -29,6 +30,22 @@ struct LOCKABLE Mutex {};
2930

3031
struct Foo {
3132
struct Mutex *mu_;
33+
int a_value GUARDED_BY(mu_);
34+
35+
struct Bar {
36+
struct Mutex *other_mu ACQUIRED_AFTER(mu_); // Note: referencing the parent structure is convenient here, but this should probably be disallowed if the child structure is re-used outside of the parent.
37+
struct Mutex *third_mu ACQUIRED_BEFORE(other_mu);
38+
} bar;
39+
40+
int* a_ptr PT_GUARDED_BY(bar.other_mu);
41+
};
42+
43+
struct LOCKABLE Lock {};
44+
struct A {
45+
struct Lock lock;
46+
union {
47+
int b __attribute__((guarded_by(lock))); // Note: referencing the parent structure is convenient here, but this should probably be disallowed if the child is re-used outside of the parent.
48+
};
3249
};
3350

3451
// Declare mutex lock/unlock functions.
@@ -74,6 +91,19 @@ int get_value(int *p) SHARED_LOCKS_REQUIRED(foo_.mu_){
7491

7592
void unlock_scope(struct Mutex *const *mu) __attribute__((release_capability(**mu)));
7693

94+
// Verify late parsing:
95+
#ifdef LATE_PARSING
96+
struct LateParsing {
97+
int a_value_defined_before GUARDED_BY(a_mutex_defined_late);
98+
int *a_ptr_defined_before PT_GUARDED_BY(a_mutex_defined_late);
99+
struct Mutex *a_mutex_defined_early
100+
ACQUIRED_BEFORE(a_mutex_defined_late);
101+
struct Mutex *a_mutex_defined_late
102+
ACQUIRED_AFTER(a_mutex_defined_very_late);
103+
struct Mutex *a_mutex_defined_very_late;
104+
} late_parsing;
105+
#endif
106+
77107
int main(void) {
78108

79109
Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \
@@ -136,9 +166,51 @@ int main(void) {
136166
// Cleanup happens automatically -> no warning.
137167
}
138168

169+
foo_.a_value = 0; // expected-warning {{writing variable 'a_value' requires holding mutex 'mu_' exclusively}}
170+
*foo_.a_ptr = 1; // expected-warning {{writing the value pointed to by 'a_ptr' requires holding mutex 'bar.other_mu' exclusively}}
171+
172+
173+
mutex_exclusive_lock(foo_.bar.other_mu);
174+
mutex_exclusive_lock(foo_.bar.third_mu); // expected-warning{{mutex 'third_mu' must be acquired before 'other_mu'}}
175+
mutex_exclusive_lock(foo_.mu_); // expected-warning{{mutex 'mu_' must be acquired before 'other_mu'}}
176+
mutex_exclusive_unlock(foo_.mu_);
177+
mutex_exclusive_unlock(foo_.bar.other_mu);
178+
mutex_exclusive_unlock(foo_.bar.third_mu);
179+
180+
#ifdef LATE_PARSING
181+
late_parsing.a_value_defined_before = 1; // expected-warning{{writing variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late' exclusively}}
182+
late_parsing.a_ptr_defined_before = 0;
183+
mutex_exclusive_lock(late_parsing.a_mutex_defined_late);
184+
mutex_exclusive_lock(late_parsing.a_mutex_defined_early); // expected-warning{{mutex 'a_mutex_defined_early' must be acquired before 'a_mutex_defined_late'}}
185+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_early);
186+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_late);
187+
mutex_exclusive_lock(late_parsing.a_mutex_defined_late);
188+
mutex_exclusive_lock(late_parsing.a_mutex_defined_very_late); // expected-warning{{mutex 'a_mutex_defined_very_late' must be acquired before 'a_mutex_defined_late'}}
189+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_very_late);
190+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_late);
191+
#endif
192+
139193
return 0;
140194
}
141195

142196
// We had a problem where we'd skip all attributes that follow a late-parsed
143197
// attribute in a single __attribute__.
144198
void run(void) __attribute__((guarded_by(mu1), guarded_by(mu1))); // expected-warning 2{{only applies to non-static data members and global variables}}
199+
200+
int value_with_wrong_number_of_args GUARDED_BY(mu1, mu2); // expected-error{{'guarded_by' attribute takes one argument}}
201+
202+
int *ptr_with_wrong_number_of_args PT_GUARDED_BY(mu1, mu2); // expected-error{{'pt_guarded_by' attribute takes one argument}}
203+
204+
int value_with_no_open_brace __attribute__((guarded_by)); // expected-error{{'guarded_by' attribute takes one argument}}
205+
int *ptr_with_no_open_brace __attribute__((pt_guarded_by)); // expected-error{{'pt_guarded_by' attribute takes one argument}}
206+
207+
int value_with_no_open_brace_on_acquire_after __attribute__((acquired_after)); // expected-error{{'acquired_after' attribute takes at least 1 argument}}
208+
int value_with_no_open_brace_on_acquire_before __attribute__((acquired_before)); // expected-error{{'acquired_before' attribute takes at least 1 argument}}
209+
210+
int value_with_bad_expr GUARDED_BY(bad_expr); // expected-error{{use of undeclared identifier 'bad_expr'}}
211+
int *ptr_with_bad_expr PT_GUARDED_BY(bad_expr); // expected-error{{use of undeclared identifier 'bad_expr'}}
212+
213+
int value_with_bad_expr_on_acquire_after __attribute__((acquired_after(other_bad_expr))); // expected-error{{use of undeclared identifier 'other_bad_expr'}}
214+
int value_with_bad_expr_on_acquire_before __attribute__((acquired_before(other_bad_expr))); // expected-error{{use of undeclared identifier 'other_bad_expr'}}
215+
216+
int a_final_expression = 0;

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,36 @@ class MutexWrapper {
141141
void MyLock() EXCLUSIVE_LOCK_FUNCTION(mu);
142142
};
143143

144+
struct TestingMoreComplexAttributes {
145+
Mutex lock;
146+
struct { Mutex lock; } strct;
147+
union {
148+
bool a __attribute__((guarded_by(lock)));
149+
bool b __attribute__((guarded_by(strct.lock)));
150+
bool *ptr_a __attribute__((pt_guarded_by(lock)));
151+
bool *ptr_b __attribute__((pt_guarded_by(strct.lock)));
152+
Mutex lock1 __attribute__((acquired_before(lock))) __attribute__((acquired_before(strct.lock)));
153+
Mutex lock2 __attribute__((acquired_after(lock))) __attribute__((acquired_after(strct.lock)));
154+
};
155+
} more_complex_atttributes;
156+
157+
void more_complex_attributes() {
158+
more_complex_atttributes.a = true; // expected-warning{{writing variable 'a' requires holding mutex 'lock' exclusively}}
159+
more_complex_atttributes.b = true; // expected-warning{{writing variable 'b' requires holding mutex 'strct.lock' exclusively}}
160+
*more_complex_atttributes.ptr_a = true; // expected-warning{{writing the value pointed to by 'ptr_a' requires holding mutex 'lock' exclusively}}
161+
*more_complex_atttributes.ptr_b = true; // expected-warning{{writing the value pointed to by 'ptr_b' requires holding mutex 'strct.lock' exclusively}}
162+
163+
more_complex_atttributes.lock.Lock();
164+
more_complex_atttributes.lock1.Lock(); // expected-warning{{mutex 'lock1' must be acquired before 'lock'}}
165+
more_complex_atttributes.lock1.Unlock();
166+
more_complex_atttributes.lock.Unlock();
167+
168+
more_complex_atttributes.lock2.Lock();
169+
more_complex_atttributes.lock.Lock(); // expected-warning{{mutex 'lock' must be acquired before 'lock2'}}
170+
more_complex_atttributes.lock.Unlock();
171+
more_complex_atttributes.lock2.Unlock();
172+
}
173+
144174
MutexWrapper sls_mw;
145175

146176
void sls_fun_0() {

0 commit comments

Comments
 (0)