diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h index 6c97905a2d7f9..e5cd1948c9314 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h @@ -35,7 +35,7 @@ #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" -#include +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -90,9 +90,10 @@ inline bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) { } inline std::string toString(const til::SExpr *E) { - std::stringstream ss; + std::string s; + llvm::raw_string_ostream ss(s); til::StdPrinter::print(E, ss); - return ss.str(); + return s; } } // namespace sx diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h index 14c5b679428a3..890ba19465f7f 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h @@ -148,129 +148,63 @@ StringRef getBinaryOpcodeString(TIL_BinaryOpcode Op); /// All variables and expressions must have a value type. /// Pointer types are further subdivided into the various heap-allocated /// types, such as functions, records, etc. -/// Structured types that are passed by value (e.g. complex numbers) -/// require special handling; they use BT_ValueRef, and size ST_0. struct ValueType { enum BaseType : unsigned char { - BT_Void = 0, BT_Bool, + BT_AsciiChar, + BT_WideChar, + BT_UTF16Char, + BT_UTF32Char, BT_Int, - BT_Float, - BT_String, // String literals + BT_String, // String literals BT_Pointer, - BT_ValueRef }; - enum SizeType : unsigned char { - ST_0 = 0, - ST_1, - ST_8, - ST_16, - ST_32, - ST_64, - ST_128 - }; - - ValueType(BaseType B, SizeType Sz, bool S, unsigned char VS) - : Base(B), Size(Sz), Signed(S), VectSize(VS) {} - - inline static SizeType getSizeType(unsigned nbytes); + ValueType(BaseType B) : Base(B) {} template inline static ValueType getValueType(); BaseType Base; - SizeType Size; - bool Signed; - - // 0 for scalar, otherwise num elements in vector - unsigned char VectSize; }; -inline ValueType::SizeType ValueType::getSizeType(unsigned nbytes) { - switch (nbytes) { - case 1: return ST_8; - case 2: return ST_16; - case 4: return ST_32; - case 8: return ST_64; - case 16: return ST_128; - default: return ST_0; - } -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Void, ST_0, false, 0); +inline bool operator==(const ValueType &a, const ValueType &b) { + return a.Base == b.Base; } template<> inline ValueType ValueType::getValueType() { - return ValueType(BT_Bool, ST_1, false, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_8, true, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_8, false, 0); + return ValueType(BT_Bool); } -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_16, true, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_16, false, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_32, true, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_32, false, 0); -} - -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_64, true, 0); +template <> inline ValueType ValueType::getValueType() { + return ValueType(BT_AsciiChar); } -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Int, ST_64, false, 0); +template <> inline ValueType ValueType::getValueType() { + return ValueType(BT_WideChar); } -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Float, ST_32, true, 0); +template <> inline ValueType ValueType::getValueType() { + return ValueType(BT_UTF16Char); } -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Float, ST_64, true, 0); +template <> inline ValueType ValueType::getValueType() { + return ValueType(BT_UTF32Char); } -template<> -inline ValueType ValueType::getValueType() { - return ValueType(BT_Float, ST_128, true, 0); +template <> inline ValueType ValueType::getValueType() { + return ValueType(BT_Int); } template<> inline ValueType ValueType::getValueType() { - return ValueType(BT_String, getSizeType(sizeof(StringRef)), false, 0); + return ValueType(BT_String); } template<> inline ValueType ValueType::getValueType() { - return ValueType(BT_Pointer, getSizeType(sizeof(void*)), false, 0); + return ValueType(BT_Pointer); } /// Base class for AST nodes in the typed intermediate language. @@ -532,37 +466,29 @@ template class LiteralT; // Base class for literal values. class Literal : public SExpr { -public: - Literal(const Expr *C) - : SExpr(COP_Literal), ValType(ValueType::getValueType()), Cexpr(C) {} +protected: Literal(ValueType VT) : SExpr(COP_Literal), ValType(VT) {} - Literal(const Literal &) = default; +public: static bool classof(const SExpr *E) { return E->opcode() == COP_Literal; } - // The clang expression for this literal. - const Expr *clangExpr() const { return Cexpr; } - ValueType valueType() const { return ValType; } template const LiteralT& as() const { + assert(ValType == ValueType::getValueType()); return *static_cast*>(this); } template LiteralT& as() { + assert(ValType == ValueType::getValueType()); return *static_cast*>(this); } template typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx); - template - typename C::CType compare(const Literal* E, C& Cmp) const { - // TODO: defer actual comparison to LiteralT - return Cmp.trueResult(); - } + template typename C::CType compare(const Literal *E, C &Cmp) const; private: const ValueType ValType; - const Expr *Cexpr = nullptr; }; // Derived class for literal values, which stores the actual value. @@ -585,58 +511,55 @@ class LiteralT : public Literal { template typename V::R_SExpr Literal::traverse(V &Vs, typename V::R_Ctx Ctx) { - if (Cexpr) - return Vs.reduceLiteral(*this); - switch (ValType.Base) { - case ValueType::BT_Void: - break; case ValueType::BT_Bool: return Vs.reduceLiteralT(as()); - case ValueType::BT_Int: { - switch (ValType.Size) { - case ValueType::ST_8: - if (ValType.Signed) - return Vs.reduceLiteralT(as()); - else - return Vs.reduceLiteralT(as()); - case ValueType::ST_16: - if (ValType.Signed) - return Vs.reduceLiteralT(as()); - else - return Vs.reduceLiteralT(as()); - case ValueType::ST_32: - if (ValType.Signed) - return Vs.reduceLiteralT(as()); - else - return Vs.reduceLiteralT(as()); - case ValueType::ST_64: - if (ValType.Signed) - return Vs.reduceLiteralT(as()); - else - return Vs.reduceLiteralT(as()); - default: - break; - } - } - case ValueType::BT_Float: { - switch (ValType.Size) { - case ValueType::ST_32: - return Vs.reduceLiteralT(as()); - case ValueType::ST_64: - return Vs.reduceLiteralT(as()); - default: - break; - } - } + case ValueType::BT_AsciiChar: + return Vs.reduceLiteralT(as()); + case ValueType::BT_WideChar: + return Vs.reduceLiteralT(as()); + case ValueType::BT_UTF16Char: + return Vs.reduceLiteralT(as()); + case ValueType::BT_UTF32Char: + return Vs.reduceLiteralT(as()); + case ValueType::BT_Int: + return Vs.reduceLiteralT(as()); case ValueType::BT_String: return Vs.reduceLiteralT(as()); case ValueType::BT_Pointer: - return Vs.reduceLiteralT(as()); - case ValueType::BT_ValueRef: - break; + return Vs.reduceLiteralT(as()); + } + llvm_unreachable("Invalid BaseType"); +} + +template +typename C::CType Literal::compare(const Literal *E, C &Cmp) const { + typename C::CType Ct = Cmp.compareIntegers(ValType.Base, E->ValType.Base); + if (Cmp.notTrue(Ct)) + return Ct; + switch (ValType.Base) { + case ValueType::BT_Bool: + return Cmp.compareIntegers(as().value(), E->as().value()); + case ValueType::BT_AsciiChar: + return Cmp.compareIntegers(as().value(), E->as().value()); + case ValueType::BT_WideChar: + return Cmp.compareIntegers(as().value(), E->as().value()); + case ValueType::BT_UTF16Char: + return Cmp.compareIntegers(as().value(), + E->as().value()); + case ValueType::BT_UTF32Char: + return Cmp.compareIntegers(as().value(), + E->as().value()); + case ValueType::BT_Int: + return Cmp.compareIntegers(as().value(), + E->as().value()); + case ValueType::BT_String: + return Cmp.compareStrings(as().value(), + E->as().value()); + case ValueType::BT_Pointer: + return Cmp.trueResult(); } - return Vs.reduceLiteral(*this); + llvm_unreachable("Invalid BaseType"); } /// A Literal pointer to an object allocated in memory. diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h b/clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h index acab8bcdc1dab..6b0c240bc4a9b 100644 --- a/clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h +++ b/clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h @@ -192,7 +192,6 @@ class VisitReducer : public Traversal, R_SExpr reduceUndefined(Undefined &Orig) { return true; } R_SExpr reduceWildcard(Wildcard &Orig) { return true; } - R_SExpr reduceLiteral(Literal &Orig) { return true; } template R_SExpr reduceLiteralT(LiteralT &Orig) { return true; } R_SExpr reduceLiteralPtr(Literal &Orig) { return true; } @@ -337,6 +336,9 @@ class EqualsComparator : public Comparator { bool notTrue(CType ct) { return !ct; } bool compareIntegers(unsigned i, unsigned j) { return i == j; } + bool compareIntegers(const llvm::APInt &i, const llvm::APInt &j) { + return i == j; + } bool compareStrings (StringRef s, StringRef r) { return s == r; } bool comparePointers(const void* P, const void* Q) { return P == Q; } @@ -365,6 +367,9 @@ class MatchComparator : public Comparator { bool notTrue(CType ct) { return !ct; } bool compareIntegers(unsigned i, unsigned j) { return i == j; } + bool compareIntegers(const llvm::APInt &i, const llvm::APInt &j) { + return i == j; + } bool compareStrings (StringRef s, StringRef r) { return s == r; } bool comparePointers(const void *P, const void *Q) { return P == Q; } @@ -532,88 +537,46 @@ class PrettyPrinter { SS << "*"; } - template - void printLiteralT(const LiteralT *E, StreamType &SS) { - SS << E->value(); - } - - void printLiteralT(const LiteralT *E, StreamType &SS) { - SS << "'" << E->value() << "'"; - } - void printLiteral(const Literal *E, StreamType &SS) { - if (E->clangExpr()) { - SS << getSourceLiteralString(E->clangExpr()); + ValueType VT = E->valueType(); + switch (VT.Base) { + case ValueType::BT_Bool: + if (E->as().value()) + SS << "true"; + else + SS << "false"; + return; + case ValueType::BT_AsciiChar: + CharacterLiteral::print(E->as().value(), + CharacterLiteralKind::Ascii, SS); + return; + case ValueType::BT_WideChar: + CharacterLiteral::print(E->as().value(), + CharacterLiteralKind::Wide, SS); + return; + case ValueType::BT_UTF16Char: + CharacterLiteral::print(E->as().value(), + CharacterLiteralKind::UTF16, SS); + return; + case ValueType::BT_UTF32Char: + CharacterLiteral::print(E->as().value(), + CharacterLiteralKind::UTF32, SS); + return; + case ValueType::BT_Int: { + SmallVector Str; + E->as().value().toStringSigned(Str); + Str.push_back('\0'); + SS << Str.data(); return; } - else { - ValueType VT = E->valueType(); - switch (VT.Base) { - case ValueType::BT_Void: - SS << "void"; - return; - case ValueType::BT_Bool: - if (E->as().value()) - SS << "true"; - else - SS << "false"; - return; - case ValueType::BT_Int: - switch (VT.Size) { - case ValueType::ST_8: - if (VT.Signed) - printLiteralT(&E->as(), SS); - else - printLiteralT(&E->as(), SS); - return; - case ValueType::ST_16: - if (VT.Signed) - printLiteralT(&E->as(), SS); - else - printLiteralT(&E->as(), SS); - return; - case ValueType::ST_32: - if (VT.Signed) - printLiteralT(&E->as(), SS); - else - printLiteralT(&E->as(), SS); - return; - case ValueType::ST_64: - if (VT.Signed) - printLiteralT(&E->as(), SS); - else - printLiteralT(&E->as(), SS); - return; - default: - break; - } - break; - case ValueType::BT_Float: - switch (VT.Size) { - case ValueType::ST_32: - printLiteralT(&E->as(), SS); - return; - case ValueType::ST_64: - printLiteralT(&E->as(), SS); - return; - default: - break; - } - break; - case ValueType::BT_String: - SS << "\""; - printLiteralT(&E->as(), SS); - SS << "\""; - return; - case ValueType::BT_Pointer: - SS << "#ptr"; - return; - case ValueType::BT_ValueRef: - SS << "#vref"; - return; - } + case ValueType::BT_String: + SS << '\"' << E->as().value() << '\"'; + return; + case ValueType::BT_Pointer: + SS << "nullptr"; // currently the only supported pointer literal. + return; } - SS << "#lit"; + llvm_unreachable("Invalid BaseType"); } void printLiteralPtr(const LiteralPtr *E, StreamType &SS) { @@ -919,7 +882,7 @@ class PrettyPrinter { } }; -class StdPrinter : public PrettyPrinter {}; +class StdPrinter : public PrettyPrinter {}; } // namespace til } // namespace threadSafety diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index ddbd0a9ca904b..0797593f30377 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -300,16 +300,37 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) { return translate(cast(S)->getSubExpr(), Ctx); // Collect all literals - case Stmt::CharacterLiteralClass: + case Stmt::CharacterLiteralClass: { + const auto *CL = cast(S); + unsigned Value = CL->getValue(); + switch (CL->getKind()) { + case CharacterLiteralKind::Ascii: + case CharacterLiteralKind::UTF8: + return new (Arena) til::LiteralT(Value); + case CharacterLiteralKind::Wide: + return new (Arena) til::LiteralT(Value); + case CharacterLiteralKind::UTF16: + return new (Arena) til::LiteralT(Value); + case CharacterLiteralKind::UTF32: + return new (Arena) til::LiteralT(Value); + } + llvm_unreachable("Invalid CharacterLiteralKind"); + } case Stmt::CXXNullPtrLiteralExprClass: case Stmt::GNUNullExprClass: + return new (Arena) til::LiteralT(nullptr); case Stmt::CXXBoolLiteralExprClass: - case Stmt::FloatingLiteralClass: - case Stmt::ImaginaryLiteralClass: + return new (Arena) + til::LiteralT(cast(S)->getValue()); case Stmt::IntegerLiteralClass: + return new (Arena) + til::LiteralT(cast(S)->getValue()); case Stmt::StringLiteralClass: + return new (Arena) + til::LiteralT(cast(S)->getString()); case Stmt::ObjCStringLiteralClass: - return new (Arena) til::Literal(cast(S)); + return new (Arena) til::LiteralT( + cast(S)->getString()->getString()); case Stmt::DeclStmtClass: return translateDeclStmt(cast(S), Ctx); diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index d64ed1e5f260a..f416c62aaf71a 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -2487,6 +2487,10 @@ class Bar { Foo& getFoo() { return *f; } Foo& getFoo2(int c) { return *f; } Foo& getFoo3(int c, int d) { return *f; } + Foo& getFoo4(bool) { return *f; } + Foo& getFoo5(char) { return *f; } + Foo& getFoo6(char16_t) { return *f; } + Foo& getFoo7(const char*) { return *f; } Foo& getFooey() { return *f; } }; @@ -2518,6 +2522,22 @@ void test() { bar.getFoo3(a, b).a = 0; bar.getFoo3(a, b).mu_.Unlock(); + bar.getFoo4(true).mu_.Lock(); + bar.getFoo4(true).a = 0; + bar.getFoo4(true).mu_.Unlock(); + + bar.getFoo5('a').mu_.Lock(); + bar.getFoo5('a').a = 0; + bar.getFoo5('a').mu_.Unlock(); + + bar.getFoo6(u'\u1234').mu_.Lock(); + bar.getFoo6(u'\u1234').a = 0; + bar.getFoo6(u'\u1234').mu_.Unlock(); + + bar.getFoo7("foo").mu_.Lock(); + bar.getFoo7("foo").a = 0; + bar.getFoo7("foo").mu_.Unlock(); + getBarFoo(bar, a).mu_.Lock(); getBarFoo(bar, a).a = 0; getBarFoo(bar, a).mu_.Unlock(); @@ -2559,12 +2579,42 @@ void test2() { // expected-note {{found near match 'bar.getFoo2(a).mu_'}} bar.getFoo2(a).mu_.Unlock(); + bar.getFoo2(0).mu_.Lock(); + bar.getFoo2(1).a = 0; // \ + // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo2(1).mu_' exclusively}} \ + // expected-note {{found near match 'bar.getFoo2(0).mu_'}} + bar.getFoo2(0).mu_.Unlock(); + bar.getFoo3(a, b).mu_.Lock(); bar.getFoo3(a, c).a = 0; // \ // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo3(a, c).mu_' exclusively}} \ // expected-note {{found near match 'bar.getFoo3(a, b).mu_'}} bar.getFoo3(a, b).mu_.Unlock(); + bar.getFoo4(true).mu_.Lock(); + bar.getFoo4(false).a = 0; // \ + // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo4(false).mu_' exclusively}} \ + // expected-note {{found near match 'bar.getFoo4(true).mu_'}} + bar.getFoo4(true).mu_.Unlock(); + + bar.getFoo5('x').mu_.Lock(); + bar.getFoo5('y').a = 0; // \ + // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo5('y').mu_' exclusively}} \ + // expected-note {{found near match 'bar.getFoo5('x').mu_'}} + bar.getFoo5('x').mu_.Unlock(); + + bar.getFoo6(u'\u1234').mu_.Lock(); + bar.getFoo6(u'\u4321').a = 0; // \ + // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo6(u'\u4321').mu_' exclusively}} \ + // expected-note {{found near match 'bar.getFoo6(u'\u1234').mu_'}} + bar.getFoo6(u'\u1234').mu_.Unlock(); + + bar.getFoo7("foo").mu_.Lock(); + bar.getFoo7("bar").a = 0; // \ + // expected-warning {{writing variable 'a' requires holding mutex 'bar.getFoo7("bar").mu_' exclusively}} \ + // expected-note {{found near match 'bar.getFoo7("foo").mu_'}} + bar.getFoo7("foo").mu_.Unlock(); + getBarFoo(bar, a).mu_.Lock(); getBarFoo(bar, b).a = 0; // \ // expected-warning {{writing variable 'a' requires holding mutex 'getBarFoo(bar, b).mu_' exclusively}} \