Skip to content

Commit 2a8eb78

Browse files
committed
Support guarded_by attribute and related attributes inside C structs and support late parsing them
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 69bc159 commit 2a8eb78

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
@@ -471,6 +471,10 @@ Attribute Changes in Clang
471471
size_t count;
472472
};
473473
474+
- The ``guarded_by``, ``pt_guarded_by``, ``acquired_after``, ``acquired_before``
475+
attributes now support referencing struct members in C. The arguments are also
476+
now late parsed when ``-fexperimental-late-parse-attributes`` is passed like
477+
for ``counted_by``.
474478

475479
Improvements to Clang's diagnostics
476480
-----------------------------------

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
@@ -3633,7 +3633,7 @@ def NoThreadSafetyAnalysis : InheritableAttr {
36333633
def GuardedBy : InheritableAttr {
36343634
let Spellings = [GNU<"guarded_by">];
36353635
let Args = [ExprArgument<"Arg">];
3636-
let LateParsed = LateAttrParseStandard;
3636+
let LateParsed = LateAttrParseExperimentalExt;
36373637
let TemplateDependent = 1;
36383638
let ParseArgumentsAsUnevaluated = 1;
36393639
let InheritEvenIfAlreadyPresent = 1;
@@ -3644,7 +3644,7 @@ def GuardedBy : InheritableAttr {
36443644
def PtGuardedBy : InheritableAttr {
36453645
let Spellings = [GNU<"pt_guarded_by">];
36463646
let Args = [ExprArgument<"Arg">];
3647-
let LateParsed = LateAttrParseStandard;
3647+
let LateParsed = LateAttrParseExperimentalExt;
36483648
let TemplateDependent = 1;
36493649
let ParseArgumentsAsUnevaluated = 1;
36503650
let InheritEvenIfAlreadyPresent = 1;
@@ -3655,7 +3655,7 @@ def PtGuardedBy : InheritableAttr {
36553655
def AcquiredAfter : InheritableAttr {
36563656
let Spellings = [GNU<"acquired_after">];
36573657
let Args = [VariadicExprArgument<"Args">];
3658-
let LateParsed = LateAttrParseStandard;
3658+
let LateParsed = LateAttrParseExperimentalExt;
36593659
let TemplateDependent = 1;
36603660
let ParseArgumentsAsUnevaluated = 1;
36613661
let InheritEvenIfAlreadyPresent = 1;
@@ -3666,7 +3666,7 @@ def AcquiredAfter : InheritableAttr {
36663666
def AcquiredBefore : InheritableAttr {
36673667
let Spellings = [GNU<"acquired_before">];
36683668
let Args = [VariadicExprArgument<"Args">];
3669-
let LateParsed = LateAttrParseStandard;
3669+
let LateParsed = LateAttrParseExperimentalExt;
36703670
let TemplateDependent = 1;
36713671
let ParseArgumentsAsUnevaluated = 1;
36723672
let InheritEvenIfAlreadyPresent = 1;

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5142,7 +5142,7 @@ class Sema final : public SemaBase {
51425142
enum ExpressionKind {
51435143
EK_Decltype,
51445144
EK_TemplateArgument,
5145-
EK_BoundsAttrArgument,
5145+
EK_AttrArgument,
51465146
EK_Other
51475147
} ExprContext;
51485148

@@ -5249,10 +5249,9 @@ class Sema final : public SemaBase {
52495249
return const_cast<Sema *>(this)->parentEvaluationContext();
52505250
};
52515251

5252-
bool isBoundsAttrContext() const {
5252+
bool isAttrContext() const {
52535253
return ExprEvalContexts.back().ExprContext ==
5254-
ExpressionEvaluationContextRecord::ExpressionKind::
5255-
EK_BoundsAttrArgument;
5254+
ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument;
52565255
}
52575256

52585257
/// 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
@@ -565,7 +565,9 @@ unsigned Parser::ParseAttributeArgsCommon(
565565
EnterExpressionEvaluationContext Unevaluated(
566566
Actions,
567567
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
568-
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
568+
: Sema::ExpressionEvaluationContext::ConstantEvaluated,
569+
nullptr,
570+
Sema::ExpressionEvaluationContextRecord::EK_AttrArgument);
569571

570572
ExprResult ArgExpr(
571573
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
@@ -582,9 +584,12 @@ unsigned Parser::ParseAttributeArgsCommon(
582584
// General case. Parse all available expressions.
583585
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
584586
EnterExpressionEvaluationContext Unevaluated(
585-
Actions, Uneval
586-
? Sema::ExpressionEvaluationContext::Unevaluated
587-
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
587+
Actions,
588+
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
589+
: Sema::ExpressionEvaluationContext::ConstantEvaluated,
590+
nullptr,
591+
Sema::ExpressionEvaluationContextRecord::ExpressionKind::
592+
EK_AttrArgument);
588593

589594
ExprVector ParsedExprs;
590595
ParsedAttributeArgumentsProperties ArgProperties =
@@ -3355,7 +3360,7 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
33553360
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
33563361
EnterExpressionEvaluationContext EC(
33573362
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3358-
ExpressionKind::EK_BoundsAttrArgument);
3363+
ExpressionKind::EK_AttrArgument);
33593364

33603365
ExprResult ArgExpr(
33613366
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));

clang/lib/Sema/SemaExpr.cpp

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

2722-
// BoundsSafety: This specially handles arguments of bounds attributes
2723-
// appertains to a type of C struct field such that the name lookup
2724-
// within a struct finds the member name, which is not the case for other
2725-
// contexts in C.
2726-
if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
2722+
// This specially handles arguments of attributes appertains to a type of C
2723+
// struct field such that the name lookup within a struct finds the member
2724+
// name, which is not the case for other contexts in C.
2725+
if (isAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
27272726
// See if this is reference to a field of struct.
27282727
LookupResult R(*this, NameInfo, LookupMemberName);
27292728
// LookupName handles a name lookup from within anonymous struct.
@@ -3357,7 +3356,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
33573356
case Decl::Field:
33583357
case Decl::IndirectField:
33593358
case Decl::ObjCIvar:
3360-
assert((getLangOpts().CPlusPlus || isBoundsAttrContext()) &&
3359+
assert((getLangOpts().CPlusPlus || isAttrContext()) &&
33613360
"building reference to field in C?");
33623361

33633362
// 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_);
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)));
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)