diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -204,6 +204,9 @@ ``-Wimplicit-int``. - ``-Wmisexpect`` warns when the branch weights collected during profiling conflict with those added by ``llvm.expect``. +- ``-Wthread-safety-analysis`` now considers overloaded compound assignment and + increment/decrement operators as writing to their first argument, thus + requiring an exclusive lock if the argument is guarded. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -1988,16 +1988,27 @@ examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end()); } else if (const auto *OE = dyn_cast(Exp)) { - auto OEop = OE->getOperator(); + OverloadedOperatorKind OEop = OE->getOperator(); switch (OEop) { - case OO_Equal: { - const Expr *Target = OE->getArg(0); - const Expr *Source = OE->getArg(1); - checkAccess(Target, AK_Written); - checkAccess(Source, AK_Read); + case OO_Equal: + case OO_PlusEqual: + case OO_MinusEqual: + case OO_StarEqual: + case OO_SlashEqual: + case OO_PercentEqual: + case OO_CaretEqual: + case OO_AmpEqual: + case OO_PipeEqual: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + checkAccess(OE->getArg(1), AK_Read); + LLVM_FALLTHROUGH; + case OO_PlusPlus: + case OO_MinusMinus: + checkAccess(OE->getArg(0), AK_Written); break; - } case OO_Star: + case OO_ArrowStar: case OO_Arrow: case OO_Subscript: if (!(OEop == OO_Star && OE->getNumArgs() > 1)) { diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -82,6 +82,9 @@ T* ptr_; }; +template +U& operator->*(const SmartPtr& ptr, U T::*p) { return ptr->*p; } + // For testing destructor calls and cleanup. class MyString { @@ -4338,6 +4341,21 @@ void operator()() { } + Data& operator+=(int); + Data& operator-=(int); + Data& operator*=(int); + Data& operator/=(int); + Data& operator%=(int); + Data& operator^=(int); + Data& operator&=(int); + Data& operator|=(int); + Data& operator<<=(int); + Data& operator>>=(int); + Data& operator++(); + Data& operator++(int); + Data& operator--(); + Data& operator--(int); + private: int dat; }; @@ -4404,6 +4422,20 @@ // expected-warning {{reading variable 'datap1_' requires holding mutex 'mu_'}} data_ = *datap2_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} \ // expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}} + data_ += 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ -= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ *= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ /= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ %= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ ^= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ &= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ |= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ <<= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_ >>= 1; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + ++data_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_++; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + --data_; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} + data_--; // expected-warning {{writing variable 'data_' requires holding mutex 'mu_' exclusively}} data_[0] = 0; // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}} (*datap2_)[0] = 0; // expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}} @@ -4923,6 +4955,8 @@ SmartPtr sp GUARDED_BY(mu1) PT_GUARDED_BY(mu2); SmartPtr sq GUARDED_BY(mu1) PT_GUARDED_BY(mu2); + static constexpr int Cell::*pa = &Cell::a; + void test1() { mu1.ReaderLock(); mu2.Lock(); @@ -4931,6 +4965,7 @@ if (*sp == 0) doSomething(); *sp = 0; sq->a = 0; + sq->*pa = 0; if (sp[0] == 0) doSomething(); sp[0] = 0; @@ -4946,6 +4981,7 @@ if (*sp == 0) doSomething(); // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}} *sp = 0; // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}} sq->a = 0; // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}} + sq->*pa = 0; // expected-warning {{reading variable 'sq' requires holding mutex 'mu1'}} if (sp[0] == 0) doSomething(); // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}} sp[0] = 0; // expected-warning {{reading variable 'sp' requires holding mutex 'mu1'}} @@ -4962,6 +4998,7 @@ if (*sp == 0) doSomething(); // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}} *sp = 0; // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}} sq->a = 0; // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}} + sq->*pa = 0; // expected-warning {{reading the value pointed to by 'sq' requires holding mutex 'mu2'}} if (sp[0] == 0) doSomething(); // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}} sp[0] = 0; // expected-warning {{reading the value pointed to by 'sp' requires holding mutex 'mu2'}}