Skip to content

Commit 7d01e8a

Browse files
committed
Support [[guarded_by(mutex)]] attribute inside C struct
Today, it's only supported inside C++ classes or top level C/C++ declaration. I mostly copied and adapted over the code from the [[counted_by(value)]] lookup. I had to change ast-dump-color.cpp because of the way the expression marking now marks the mutex as "used" instead of "referenced". If I change the expression parsing to use `ExpressionEvaluationContext::Unevaluated` then I get the mutex as "referenced" but I lose the C++ check for "invalid use of non-static data member".
1 parent 50d837e commit 7d01e8a

File tree

8 files changed

+84
-16
lines changed

8 files changed

+84
-16
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 2 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;

clang/include/clang/Parse/Parser.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3128,6 +3128,13 @@ class Parser : public CodeCompletionHandler {
31283128
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
31293129
ParsedAttr::Form Form);
31303130

3131+
void ParseGuardedByAttribute(IdentifierInfo &AttrName,
3132+
SourceLocation AttrNameLoc,
3133+
ParsedAttributes &Attrs,
3134+
IdentifierInfo *ScopeName,
3135+
SourceLocation ScopeLoc, SourceLocation *EndLoc,
3136+
ParsedAttr::Form Form);
3137+
31313138
void ParseTypeofSpecifier(DeclSpec &DS);
31323139
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
31333140
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
@@ -5078,7 +5078,7 @@ class Sema final : public SemaBase {
50785078
enum ExpressionKind {
50795079
EK_Decltype,
50805080
EK_TemplateArgument,
5081-
EK_BoundsAttrArgument,
5081+
EK_AttrArgument,
50825082
EK_Other
50835083
} ExprContext;
50845084

@@ -5185,10 +5185,9 @@ class Sema final : public SemaBase {
51855185
return const_cast<Sema *>(this)->parentEvaluationContext();
51865186
};
51875187

5188-
bool isBoundsAttrContext() const {
5188+
bool isAttrContext() const {
51895189
return ExprEvalContexts.back().ExprContext ==
5190-
ExpressionEvaluationContextRecord::ExpressionKind::
5191-
EK_BoundsAttrArgument;
5190+
ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument;
51925191
}
51935192

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

clang/lib/Parse/ParseDecl.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,11 @@ 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;
674679
} else if (AttrKind == ParsedAttr::AT_CXXAssume) {
675680
ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form);
676681
return;
@@ -3330,6 +3335,59 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
33303335
}
33313336
}
33323337

3338+
/// GuardedBy attributes (e.g., guarded_by):
3339+
/// AttrName '(' expression ')'
3340+
void Parser::ParseGuardedByAttribute(
3341+
IdentifierInfo &AttrName, SourceLocation AttrNameLoc,
3342+
ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
3343+
SourceLocation *EndLoc, ParsedAttr::Form Form) {
3344+
assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
3345+
3346+
BalancedDelimiterTracker Parens(*this, tok::l_paren);
3347+
Parens.consumeOpen();
3348+
3349+
if (Tok.is(tok::r_paren)) {
3350+
Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
3351+
Parens.consumeClose();
3352+
return;
3353+
}
3354+
3355+
ArgsVector ArgExprs;
3356+
// Don't evaluate argument when the attribute is ignored.
3357+
using ExpressionKind =
3358+
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
3359+
EnterExpressionEvaluationContext EC(
3360+
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3361+
ExpressionKind::EK_AttrArgument);
3362+
3363+
ExprResult ArgExpr(
3364+
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
3365+
3366+
if (ArgExpr.isInvalid()) {
3367+
Parens.skipToEnd();
3368+
return;
3369+
}
3370+
3371+
ArgExprs.push_back(ArgExpr.get());
3372+
3373+
auto RParens = Tok.getLocation();
3374+
auto &AL =
3375+
*Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, RParens), ScopeName,
3376+
ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form);
3377+
3378+
if (EndLoc)
3379+
*EndLoc = Tok.getLocation();
3380+
3381+
if (!Tok.is(tok::r_paren)) {
3382+
Diag(Tok.getLocation(), diag::err_attribute_wrong_number_arguments)
3383+
<< AL << 1;
3384+
Parens.skipToEnd();
3385+
return;
3386+
}
3387+
3388+
Parens.consumeClose();
3389+
}
3390+
33333391
/// Bounds attributes (e.g., counted_by):
33343392
/// AttrName '(' expression ')'
33353393
void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
@@ -3355,7 +3413,7 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
33553413
Sema::ExpressionEvaluationContextRecord::ExpressionKind;
33563414
EnterExpressionEvaluationContext EC(
33573415
Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
3358-
ExpressionKind::EK_BoundsAttrArgument);
3416+
ExpressionKind::EK_AttrArgument);
33593417

33603418
ExprResult ArgExpr(
33613419
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ struct LOCKABLE Mutex {};
2929

3030
struct Foo {
3131
struct Mutex *mu_;
32+
struct Bar {
33+
struct Mutex *other_mu;
34+
} bar;
35+
int a_value GUARDED_BY(mu_);
36+
int* a_ptr PT_GUARDED_BY(bar.other_mu);
3237
};
3338

3439
// Declare mutex lock/unlock functions.
@@ -136,6 +141,9 @@ int main(void) {
136141
// Cleanup happens automatically -> no warning.
137142
}
138143

144+
foo_.a_value = 0; // expected-warning {{writing variable 'a_value' requires holding mutex 'mu_' exclusively}}
145+
*foo_.a_ptr = 1; // expected-warning {{writing the value pointed to by 'a_ptr' requires holding mutex 'bar.other_mu' exclusively}}
146+
139147
return 0;
140148
}
141149

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)