Skip to content

Commit af0d712

Browse files
authored
Support guarded_by attribute and related attributes inside C structs and support late parsing them (#94216)
This fixes #20777. 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; }; ``` Patch by Pierre d'Herbemont (@pdherbemont)
1 parent 7665d3d commit af0d712

File tree

10 files changed

+213
-26
lines changed

10 files changed

+213
-26
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: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -764,12 +764,6 @@ 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-
-------------------------------------------------------------------------
769-
770-
To be fixed in a future update.
771-
772-
773767
.. _mutexheader:
774768

775769
mutex.h

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/Parse/Parser.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,6 +3130,20 @@ class Parser : public CodeCompletionHandler {
31303130
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
31313131
ParsedAttr::Form Form);
31323132

3133+
void ParseGuardedByAttribute(IdentifierInfo &AttrName,
3134+
SourceLocation AttrNameLoc,
3135+
ParsedAttributes &Attrs,
3136+
IdentifierInfo *ScopeName,
3137+
SourceLocation ScopeLoc, SourceLocation *EndLoc,
3138+
ParsedAttr::Form Form);
3139+
3140+
void ParseAcquiredAttribute(IdentifierInfo &AttrName,
3141+
SourceLocation AttrNameLoc,
3142+
ParsedAttributes &Attrs,
3143+
IdentifierInfo *ScopeName,
3144+
SourceLocation ScopeLoc, SourceLocation *EndLoc,
3145+
ParsedAttr::Form Form);
3146+
31333147
void ParseTypeofSpecifier(DeclSpec &DS);
31343148
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
31353149
void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,

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: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,16 @@ void Parser::ParseGNUAttributeArgs(
671671
ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
672672
Form);
673673
return;
674+
} else if (AttrKind == ParsedAttr::AT_GuardedBy ||
675+
AttrKind == ParsedAttr::AT_PtGuardedBy) {
676+
ParseGuardedByAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
677+
EndLoc, Form);
678+
return;
679+
} else if (AttrKind == ParsedAttr::AT_AcquiredAfter ||
680+
AttrKind == ParsedAttr::AT_AcquiredBefore) {
681+
ParseAcquiredAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
682+
EndLoc, Form);
683+
return;
674684
} else if (AttrKind == ParsedAttr::AT_CXXAssume) {
675685
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form);
676686
return;
@@ -3330,6 +3340,112 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
33303340
}
33313341
}
33323342

3343+
/// GuardedBy attributes (e.g., guarded_by):
3344+
/// AttrName '(' expression ')'
3345+
void Parser::ParseGuardedByAttribute(
3346+
IdentifierInfo &AttrName, SourceLocation AttrNameLoc,
3347+
ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
3348+
SourceLocation *EndLoc, ParsedAttr::Form Form) {
3349+
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
3350+
3351+
BalancedDelimiterTracker Parens(*this, tok::l_paren);
3352+
Parens.consumeOpen();
3353+
3354+
if (Tok.is(tok::r_paren)) {
3355+
Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
3356+
Parens.consumeClose();
3357+
return;
3358+
}
3359+
3360+
ArgsVector ArgExprs;
3361+
// Don't evaluate argument when the attribute is ignored.
3362+
using ExpressionKind =
3363+
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
3364+
EnterExpressionEvaluationContext EC(
3365+
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3366+
ExpressionKind::EK_AttrArgument);
3367+
3368+
ExprResult ArgExpr(
3369+
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
3370+
3371+
if (ArgExpr.isInvalid()) {
3372+
Parens.skipToEnd();
3373+
return;
3374+
}
3375+
3376+
ArgExprs.push_back(ArgExpr.get());
3377+
3378+
auto RParens = Tok.getLocation();
3379+
auto &AL =
3380+
*Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, RParens), ScopeName,
3381+
ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form);
3382+
3383+
if (EndLoc)
3384+
*EndLoc = RParens;
3385+
3386+
if (!Tok.is(tok::r_paren)) {
3387+
Diag(Tok.getLocation(), diag::err_attribute_wrong_number_arguments)
3388+
<< AL << 1;
3389+
Parens.skipToEnd();
3390+
return;
3391+
}
3392+
3393+
Parens.consumeClose();
3394+
}
3395+
3396+
/// Acquired attributes (e.g., acquired_before, acquired_after):
3397+
/// AttrName '(' expression-list ')'
3398+
void Parser::ParseAcquiredAttribute(
3399+
IdentifierInfo &AttrName, SourceLocation AttrNameLoc,
3400+
ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
3401+
SourceLocation *EndLoc, ParsedAttr::Form Form) {
3402+
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
3403+
3404+
BalancedDelimiterTracker Parens(*this, tok::l_paren);
3405+
Parens.consumeOpen();
3406+
3407+
if (Tok.is(tok::r_paren)) {
3408+
Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
3409+
Parens.consumeClose();
3410+
return;
3411+
}
3412+
3413+
auto ArgStart = Tok.getLocation();
3414+
3415+
ArgsVector ArgExprs;
3416+
3417+
do {
3418+
3419+
// Don't evaluate argument when the attribute is ignored.
3420+
using ExpressionKind =
3421+
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
3422+
EnterExpressionEvaluationContext EC(
3423+
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
3424+
nullptr, ExpressionKind::EK_AttrArgument);
3425+
3426+
ExprResult ArgExpr(
3427+
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
3428+
3429+
if (ArgExpr.isInvalid()) {
3430+
Parens.skipToEnd();
3431+
return;
3432+
}
3433+
3434+
ArgExprs.push_back(ArgExpr.get());
3435+
3436+
} while (TryConsumeToken(tok::comma));
3437+
3438+
auto ArgEnd = Tok.getLocation();
3439+
3440+
Attrs.addNew(&AttrName, SourceRange(ArgStart, ArgEnd), ScopeName, ScopeLoc,
3441+
ArgExprs.data(), ArgExprs.size(), Form);
3442+
3443+
if (EndLoc)
3444+
*EndLoc = ArgEnd;
3445+
3446+
Parens.consumeClose();
3447+
}
3448+
33333449
/// Bounds attributes (e.g., counted_by):
33343450
/// AttrName '(' expression ')'
33353451
void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
@@ -3355,7 +3471,7 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
33553471
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
33563472
EnterExpressionEvaluationContext EC(
33573473
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3358-
ExpressionKind::EK_BoundsAttrArgument);
3474+
ExpressionKind::EK_AttrArgument);
33593475

33603476
ExprResult ArgExpr(
33613477
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));

clang/lib/Sema/SemaExpr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,7 +2723,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
27232723
// appertains to a type of C struct field such that the name lookup
27242724
// within a struct finds the member name, which is not the case for other
27252725
// contexts in C.
2726-
if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
2726+
if (isAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) {
27272727
// See if this is reference to a field of struct.
27282728
LookupResult R(*this, NameInfo, LookupMemberName);
27292729
// LookupName handles a name lookup from within anonymous struct.
@@ -3357,7 +3357,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
33573357
case Decl::Field:
33583358
case Decl::IndirectField:
33593359
case Decl::ObjCIvar:
3360-
assert((getLangOpts().CPlusPlus || isBoundsAttrContext()) &&
3360+
assert((getLangOpts().CPlusPlus || isAttrContext()) &&
33613361
"building reference to field in C?");
33623362

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

clang/test/AST/ast-dump-color.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@ struct Invalid {
8282
//CHECK: {{^}}[[Blue]]| | `-[[RESET]][[GREEN]]ParmVarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:33[[RESET]]> [[Yellow]]col:33[[RESET]] [[Green]]'const Mutex &'[[RESET]]{{$}}
8383
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[GREEN]]CXXConstructorDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:33[[RESET]]> [[Yellow]]col:33[[RESET]] implicit constexpr[[CYAN]] Mutex[[RESET]] [[Green]]'void (Mutex &&)'[[RESET]] inline{{ .*$}}
8484
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[GREEN]]ParmVarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:33[[RESET]]> [[Yellow]]col:33[[RESET]] [[Green]]'Mutex &&'[[RESET]]{{$}}
85-
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]VarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]line:25:3[[RESET]]> [[Yellow]]col:3[[RESET]] referenced[[CYAN]] mu1[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]]
85+
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]VarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]line:25:3[[RESET]]> [[Yellow]]col:3[[RESET]] used[[CYAN]] mu1[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]]
8686
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]CXXConstructExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:3[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]] [[Green]]'void () noexcept'[[RESET]]{{$}}
8787
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]VarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:18:1[[RESET]], [[Yellow]]line:25:8[[RESET]]> [[Yellow]]col:8[[RESET]][[CYAN]] mu2[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]]
8888
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]CXXConstructExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:8[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]] [[Green]]'void () noexcept'[[RESET]]{{$}}
8989
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]VarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:26:1[[RESET]], [[Yellow]]col:5[[RESET]]> [[Yellow]]col:5[[RESET]][[CYAN]] TestExpr[[RESET]] [[Green]]'int'[[RESET]]
9090
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[BLUE]]GuardedByAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:29[[RESET]], [[Yellow]]col:43[[RESET]]>{{$}}
91-
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]DeclRefExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:40[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]] lvalue[[RESET]][[Cyan]][[RESET]] [[GREEN]]Var[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]][[CYAN]] 'mu1'[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]] non_odr_use_unevaluated{{$}}
91+
//CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]DeclRefExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:40[[RESET]]> [[Green]]'class Mutex':'Mutex'[[RESET]][[Cyan]] lvalue[[RESET]][[Cyan]][[RESET]] [[GREEN]]Var[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]][[CYAN]] 'mu1'[[RESET]] [[Green]]'class Mutex':'Mutex'[[RESET]]{{$}}
9292
//CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:28:1[[RESET]], [[Yellow]]line:30:1[[RESET]]> [[Yellow]]line:28:8[[RESET]] struct[[CYAN]] Invalid[[RESET]] definition
9393
//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]col:8[[RESET]]> [[Yellow]]col:8[[RESET]] implicit referenced struct[[CYAN]] Invalid[[RESET]]
9494
//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXConstructorDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:29:3[[RESET]], [[Yellow]]col:42[[RESET]]> [[Yellow]]col:29[[RESET]] invalid[[CYAN]] Invalid[[RESET]] [[Green]]'void (int)'[[RESET]]

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

Lines changed: 66 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,14 @@ 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);
3241
};
3342

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

7584
void unlock_scope(struct Mutex *const *mu) __attribute__((release_capability(**mu)));
7685

86+
// Verify late parsing:
87+
#ifdef LATE_PARSING
88+
struct LateParsing {
89+
int a_value_defined_before GUARDED_BY(a_mutex_defined_late);
90+
int *a_ptr_defined_before PT_GUARDED_BY(a_mutex_defined_late);
91+
struct Mutex *a_mutex_defined_early
92+
ACQUIRED_BEFORE(a_mutex_defined_late);
93+
struct Mutex *a_mutex_defined_late
94+
ACQUIRED_AFTER(a_mutex_defined_very_late);
95+
struct Mutex *a_mutex_defined_very_late;
96+
} late_parsing;
97+
#endif
98+
7799
int main(void) {
78100

79101
Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \
@@ -136,9 +158,51 @@ int main(void) {
136158
// Cleanup happens automatically -> no warning.
137159
}
138160

161+
foo_.a_value = 0; // expected-warning {{writing variable 'a_value' requires holding mutex 'mu_' exclusively}}
162+
*foo_.a_ptr = 1; // expected-warning {{writing the value pointed to by 'a_ptr' requires holding mutex 'bar.other_mu' exclusively}}
163+
164+
165+
mutex_exclusive_lock(foo_.bar.other_mu);
166+
mutex_exclusive_lock(foo_.bar.third_mu); // expected-warning{{mutex 'third_mu' must be acquired before 'other_mu'}}
167+
mutex_exclusive_lock(foo_.mu_); // expected-warning{{mutex 'mu_' must be acquired before 'other_mu'}}
168+
mutex_exclusive_unlock(foo_.mu_);
169+
mutex_exclusive_unlock(foo_.bar.other_mu);
170+
mutex_exclusive_unlock(foo_.bar.third_mu);
171+
172+
#ifdef LATE_PARSING
173+
late_parsing.a_value_defined_before = 1; // expected-warning{{writing variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late' exclusively}}
174+
late_parsing.a_ptr_defined_before = 0;
175+
mutex_exclusive_lock(late_parsing.a_mutex_defined_late);
176+
mutex_exclusive_lock(late_parsing.a_mutex_defined_early); // expected-warning{{mutex 'a_mutex_defined_early' must be acquired before 'a_mutex_defined_late'}}
177+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_early);
178+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_late);
179+
mutex_exclusive_lock(late_parsing.a_mutex_defined_late);
180+
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'}}
181+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_very_late);
182+
mutex_exclusive_unlock(late_parsing.a_mutex_defined_late);
183+
#endif
184+
139185
return 0;
140186
}
141187

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

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,11 +1516,7 @@ class Foo {
15161516
mutable Mutex mu;
15171517
int a GUARDED_BY(mu);
15181518

1519-
static int si GUARDED_BY(mu);
1520-
//FIXME: Bug 32066 - Error should be emitted irrespective of C++ dialect
1521-
#if __cplusplus <= 199711L
1522-
// expected-error@-3 {{invalid use of non-static data member 'mu'}}
1523-
#endif
1519+
static int si GUARDED_BY(mu); // expected-error {{invalid use of non-static data member 'mu'}}
15241520

15251521
static void foo() EXCLUSIVE_LOCKS_REQUIRED(mu);
15261522
//FIXME: Bug 32066 - Error should be emitted irrespective of C++ dialect

0 commit comments

Comments
 (0)