203 changes: 105 additions & 98 deletions clang/lib/Analysis/ThreadSafety.cpp

Large diffs are not rendered by default.

46 changes: 29 additions & 17 deletions clang/lib/Analysis/ThreadSafetyCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,22 @@ static StringRef ClassifyDiagnostic(QualType VDT) {
/// \param D The declaration to which the attribute is attached.
/// \param DeclExp An expression involving the Decl to which the attribute
/// is attached. E.g. the call to a function.
/// \param Self S-expression to substitute for a \ref CXXThisExpr.
CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
const NamedDecl *D,
const Expr *DeclExp,
VarDecl *SelfDecl) {
til::SExpr *Self) {
// If we are processing a raw attribute expression, with no substitutions.
if (!DeclExp)
if (!DeclExp && !Self)
return translateAttrExpr(AttrExp, nullptr);

CallingContext Ctx(nullptr, D);

// Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
// for formal parameters when we call buildMutexID later.
if (const auto *ME = dyn_cast<MemberExpr>(DeclExp)) {
if (!DeclExp)
/* We'll use Self. */;
else if (const auto *ME = dyn_cast<MemberExpr>(DeclExp)) {
Ctx.SelfArg = ME->getBase();
Ctx.SelfArrow = ME->isArrow();
} else if (const auto *CE = dyn_cast<CXXMemberCallExpr>(DeclExp)) {
Expand All @@ -142,29 +145,24 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
Ctx.SelfArg = nullptr; // Will be set below
Ctx.NumArgs = CE->getNumArgs();
Ctx.FunArgs = CE->getArgs();
} else if (D && isa<CXXDestructorDecl>(D)) {
// There's no such thing as a "destructor call" in the AST.
Ctx.SelfArg = DeclExp;
}

// Hack to handle constructors, where self cannot be recovered from
// the expression.
if (SelfDecl && !Ctx.SelfArg) {
DeclRefExpr SelfDRE(SelfDecl->getASTContext(), SelfDecl, false,
SelfDecl->getType(), VK_LValue,
SelfDecl->getLocation());
Ctx.SelfArg = &SelfDRE;
if (Self) {
assert(!Ctx.SelfArg && "Ambiguous self argument");
Ctx.SelfArg = Self;

// If the attribute has no arguments, then assume the argument is "this".
if (!AttrExp)
return translateAttrExpr(Ctx.SelfArg, nullptr);
return CapabilityExpr(
Self, ClassifyDiagnostic(cast<CXXMethodDecl>(D)->getThisObjectType()),
false);
else // For most attributes.
return translateAttrExpr(AttrExp, &Ctx);
}

// If the attribute has no arguments, then assume the argument is "this".
if (!AttrExp)
return translateAttrExpr(Ctx.SelfArg, nullptr);
return translateAttrExpr(cast<const Expr *>(Ctx.SelfArg), nullptr);
else // For most attributes.
return translateAttrExpr(AttrExp, &Ctx);
}
Expand Down Expand Up @@ -218,6 +216,16 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
return CapabilityExpr(E, Kind, Neg);
}

til::LiteralPtr *SExprBuilder::createVariable(const VarDecl *VD) {
return new (Arena) til::LiteralPtr(VD);
}

std::pair<til::LiteralPtr *, StringRef>
SExprBuilder::createThisPlaceholder(const Expr *Exp) {
return {new (Arena) til::LiteralPtr(nullptr),
ClassifyDiagnostic(Exp->getType())};
}

// Translate a clang statement or expression to a TIL expression.
// Also performs substitution of variables; Ctx provides the context.
// Dispatches on the type of S.
Expand Down Expand Up @@ -327,8 +335,12 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
til::SExpr *SExprBuilder::translateCXXThisExpr(const CXXThisExpr *TE,
CallingContext *Ctx) {
// Substitute for 'this'
if (Ctx && Ctx->SelfArg)
return translate(Ctx->SelfArg, Ctx->Prev);
if (Ctx && Ctx->SelfArg) {
if (const auto *SelfArg = dyn_cast<const Expr *>(Ctx->SelfArg))
return translate(SelfArg, Ctx->Prev);
else
return cast<til::SExpr *>(Ctx->SelfArg);
}
assert(SelfVar && "We have no variable for 'this'!");
return SelfVar;
}
Expand Down
106 changes: 72 additions & 34 deletions clang/test/SemaCXX/warn-thread-safety-analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,22 @@ struct TestScopedLockable {
a = 5;
}

#ifdef __cpp_guaranteed_copy_elision
void const_lock() {
const MutexLock mulock = MutexLock(&mu1);
a = 5;
}
#endif

void temporary() {
MutexLock{&mu1}, a = 5;
}

void lifetime_extension() {
const MutexLock &mulock = MutexLock(&mu1);
a = 5;
}

void foo2() {
ReaderMutexLock mulock1(&mu1);
if (getBool()) {
Expand All @@ -1701,6 +1717,12 @@ struct TestScopedLockable {
// expected-warning {{acquiring mutex 'mu1' that is already held}}
}

void temporary_double_lock() {
MutexLock mulock_a(&mu1); // expected-note{{mutex acquired here}}
MutexLock{&mu1}; // \
// expected-warning {{acquiring mutex 'mu1' that is already held}}
}

void foo4() {
MutexLock mulock1(&mu1), mulock2(&mu2);
a = b+1;
Expand Down Expand Up @@ -4180,6 +4202,20 @@ class LOCKABLE SelfLock2 {
void foo() EXCLUSIVE_LOCKS_REQUIRED(this);
};

class SelfLockDeferred {
public:
SelfLockDeferred() LOCKS_EXCLUDED(mu_);
~SelfLockDeferred() UNLOCK_FUNCTION(mu_);

Mutex mu_;
};

class LOCKABLE SelfLockDeferred2 {
public:
SelfLockDeferred2() LOCKS_EXCLUDED(this);
~SelfLockDeferred2() UNLOCK_FUNCTION();
};


void test() {
SelfLock s;
Expand All @@ -4191,6 +4227,14 @@ void test2() {
s2.foo();
}

void testDeferredTemporary() {
SelfLockDeferred(); // expected-warning {{releasing mutex '<temporary>.mu_' that was not held}}
}

void testDeferredTemporary2() {
SelfLockDeferred2(); // expected-warning {{releasing mutex '<temporary>' that was not held}}
}

} // end namespace SelfConstructorTest


Expand Down Expand Up @@ -5885,47 +5929,41 @@ C c;
void f() { c[A()]->g(); }
} // namespace PR34800

#ifdef __cpp_guaranteed_copy_elision

namespace ReturnScopedLockable {
template<typename Object> class SCOPED_LOCKABLE ReadLockedPtr {
public:
ReadLockedPtr(Object *ptr) SHARED_LOCK_FUNCTION((*this)->mutex);
ReadLockedPtr(ReadLockedPtr &&) SHARED_LOCK_FUNCTION((*this)->mutex);
~ReadLockedPtr() UNLOCK_FUNCTION();

Object *operator->() const { return object; }
class Object {
public:
MutexLock lock() EXCLUSIVE_LOCK_FUNCTION(mutex) {
// TODO: False positive because scoped lock isn't destructed.
return MutexLock(&mutex); // expected-note {{mutex acquired here}}
} // expected-warning {{mutex 'mutex' is still held at the end of function}}

private:
Object *object;
};
int x GUARDED_BY(mutex);
void needsLock() EXCLUSIVE_LOCKS_REQUIRED(mutex);

struct Object {
int f() SHARED_LOCKS_REQUIRED(mutex);
Mutex mutex;
};

ReadLockedPtr<Object> get();
int use() {
auto ptr = get();
return ptr->f();
}
void use_constructor() {
auto ptr = ReadLockedPtr<Object>(nullptr);
ptr->f();
auto ptr2 = ReadLockedPtr<Object>{nullptr};
ptr2->f();
auto ptr3 = (ReadLockedPtr<Object>{nullptr});
ptr3->f();
}
struct Convertible {
Convertible();
operator ReadLockedPtr<Object>();
};
void use_conversion() {
ReadLockedPtr<Object> ptr = Convertible();
ptr->f();
void testInside() {
MutexLock scope = lock();
x = 1;
needsLock();
}

private:
Mutex mutex;
};

void testOutside() {
Object obj;
MutexLock scope = obj.lock();
obj.x = 1;
obj.needsLock();
}

} // namespace ReturnScopedLockable

#endif

namespace PR38640 {
void f() {
// Self-referencing assignment previously caused an infinite loop when thread
Expand Down