Skip to content

Commit a999ab4

Browse files
authored
Thread safety analysis: Fix substitution for operator calls (llvm#116487)
For operator calls that go to methods we need to substitute the first parameter for "this" and the following parameters into the function parameters, instead of substituting all of them into the parameters. This revealed an issue about lambdas. An existing test accidentally worked because the substitution bug was covered by a speciality of lambdas: a CXXThisExpr in a lambda CXXMethodDecl does not refer to the implicit this argument of the method, but to a captured "this" from the context the lambda was created in. This can happen for operator calls, where it worked due to the substitution bug (we treated the implicit this argument incorrectly as parameter), and for regular calls (i.e. obj.operator()(args) instead of obj(args)), where it didn't work. The correct fix seems to be to clear the self-argument on a lambda call. Lambdas can only capture "this" inside methods, and calls to the lambda in that scope cannot substitute anything for "this".
1 parent c3276a9 commit a999ab4

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

clang/lib/Analysis/ThreadSafetyCommon.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,30 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
135135
Ctx.NumArgs = CE->getNumArgs();
136136
Ctx.FunArgs = CE->getArgs();
137137
} else if (const auto *CE = dyn_cast<CallExpr>(DeclExp)) {
138-
Ctx.NumArgs = CE->getNumArgs();
139-
Ctx.FunArgs = CE->getArgs();
138+
// Calls to operators that are members need to be treated like member calls.
139+
if (isa<CXXOperatorCallExpr>(CE) && isa<CXXMethodDecl>(D)) {
140+
Ctx.SelfArg = CE->getArg(0);
141+
Ctx.SelfArrow = false;
142+
Ctx.NumArgs = CE->getNumArgs() - 1;
143+
Ctx.FunArgs = CE->getArgs() + 1;
144+
} else {
145+
Ctx.NumArgs = CE->getNumArgs();
146+
Ctx.FunArgs = CE->getArgs();
147+
}
140148
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(DeclExp)) {
141149
Ctx.SelfArg = nullptr; // Will be set below
142150
Ctx.NumArgs = CE->getNumArgs();
143151
Ctx.FunArgs = CE->getArgs();
144152
}
145153

154+
// Usually we want to substitute the self-argument for "this", but lambdas
155+
// are an exception: "this" on or in a lambda call operator doesn't refer
156+
// to the lambda, but to captured "this" in the context it was created in.
157+
// This can happen for operator calls and member calls, so fix it up here.
158+
if (const auto *CMD = dyn_cast<CXXMethodDecl>(D))
159+
if (CMD->getParent()->isLambda())
160+
Ctx.SelfArg = nullptr;
161+
146162
if (Self) {
147163
assert(!Ctx.SelfArg && "Ambiguous self argument");
148164
assert(isa<FunctionDecl>(D) && "Self argument requires function");

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,8 +1593,12 @@ namespace substitution_test {
15931593
void unlockData() UNLOCK_FUNCTION(mu);
15941594

15951595
void doSomething() EXCLUSIVE_LOCKS_REQUIRED(mu) { }
1596+
void operator()() EXCLUSIVE_LOCKS_REQUIRED(mu) { }
1597+
1598+
MyData operator+(const MyData& other) const SHARED_LOCKS_REQUIRED(mu, other.mu);
15961599
};
15971600

1601+
MyData operator-(const MyData& a, const MyData& b) SHARED_LOCKS_REQUIRED(a.mu, b.mu);
15981602

15991603
class DataLocker {
16001604
public:
@@ -1607,6 +1611,27 @@ namespace substitution_test {
16071611
public:
16081612
void foo(MyData* d) EXCLUSIVE_LOCKS_REQUIRED(d->mu) { }
16091613

1614+
void subst(MyData& d) {
1615+
d.doSomething(); // expected-warning {{calling function 'doSomething' requires holding mutex 'd.mu' exclusively}}
1616+
d(); // expected-warning {{calling function 'operator()' requires holding mutex 'd.mu' exclusively}}
1617+
d.operator()(); // expected-warning {{calling function 'operator()' requires holding mutex 'd.mu' exclusively}}
1618+
1619+
d.lockData();
1620+
d.doSomething();
1621+
d();
1622+
d.operator()();
1623+
d.unlockData();
1624+
}
1625+
1626+
void binop(MyData& a, MyData& b) EXCLUSIVE_LOCKS_REQUIRED(a.mu) {
1627+
a + b; // expected-warning {{calling function 'operator+' requires holding mutex 'b.mu'}}
1628+
// expected-note@-1 {{found near match 'a.mu'}}
1629+
b + a; // expected-warning {{calling function 'operator+' requires holding mutex 'b.mu'}}
1630+
// expected-note@-1 {{found near match 'a.mu'}}
1631+
a - b; // expected-warning {{calling function 'operator-' requires holding mutex 'b.mu'}}
1632+
// expected-note@-1 {{found near match 'a.mu'}}
1633+
}
1634+
16101635
void bar1(MyData* d) {
16111636
d->lockData();
16121637
foo(d);
@@ -5172,9 +5197,13 @@ class Foo {
51725197
};
51735198

51745199
func1(); // expected-warning {{calling function 'operator()' requires holding mutex 'mu_' exclusively}}
5200+
func1.operator()(); // expected-warning {{calling function 'operator()' requires holding mutex 'mu_' exclusively}}
51755201
func2();
5202+
func2.operator()();
51765203
func3();
51775204
mu_.Unlock();
5205+
func3.operator()();
5206+
mu_.Unlock();
51785207
}
51795208
};
51805209

0 commit comments

Comments
 (0)