Skip to content

Thread Safety Analysis: Compare values of literals#148551

Merged
aaronpuchert merged 1 commit intollvm:mainfrom
aaronpuchert:thread-safety-literal-comparison
Feb 2, 2026
Merged

Thread Safety Analysis: Compare values of literals#148551
aaronpuchert merged 1 commit intollvm:mainfrom
aaronpuchert:thread-safety-literal-comparison

Conversation

@aaronpuchert
Copy link
Member

@aaronpuchert aaronpuchert commented Jul 13, 2025

The typical case for literals is an array of mutexes, where we want to distinguish mutex[0] from mutex[1] and so on. Currently they're treated as the same expression, in fact all literals are treated as the same expression.

The infrastructure for literals is already there, although it required some changes, and some simplifications seemed opportune:

  • The ValueType had fields for size and signedness. But only integer have signedness, and the size is irrelevant if we don't want to emit machine code. For the abstract semantics that we're interested in, only the number matters.
  • We remove the BT_Void: void literals don't exist in C++.
  • We remove BT_Float and BT_ValueRef: floating-point numbers and complex numbers are probably not used in lock expressions.
  • We replace BT_Pointer with BT_NullPointer. There are no pointer literals, only null pointer literals.

It seems to me that ValueType was intended to assign types to any node in the TIL, but it is currently only used for literals. I was trying to come up with reasons to assign types everywhere, but we're not trying to type check anything, and we don't support overloading. The comparison of expressions surely doesn't need it. But maybe this should be clarified at some point. (Including the fact that it is called Typed Intermediate Language.)

We turn Literal into a pure base class, as it seems to have been intended, and only create LiteralT instances of the correct type. Assertions on as ensure we're not mixing up types.

We print to llvm::raw_ostream instead of std::ostream because that's required for CharacterLiteral::print.

Fixes #58535.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:analysis labels Jul 13, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 13, 2025

@llvm/pr-subscribers-clang

Author: Aaron Puchert (aaronpuchert)

Changes

The typical case for literals is an array of mutexes, where we want to distinguish mutex[0] from mutex[1] and so on. Currently they're treated as the same expression, in fact all literals are treated as the same expression.

The infrastructure for literals is already there, although it required some changes, and some simplifications seemed opportune:

  • The ValueType had fields for size and signedness. But Clang doesn't use native types and stores integer and (floating-point) literals as llvm::APInt regardless of size, so we don't need these properties. We could use them for characters, but it seems easier to just create different base types for now.
  • We remove the BT_Void: void literals don't exist in C++.
  • We remove BT_Float and BT_ValueRef: floating-point numbers and complex numbers are probably not used in lock expressions.

We turn Literal into a pure base class, as it seems to have been intended, and only create LiteralT instances of the correct type. Assertions on as ensure we're not mixing up types.

We print to llvm::raw_ostream instead of std::ostream because that's required for CharacterLiteral::print. Perhaps we should implement that ourselves though.

Fixes #58535.


Patch is 20.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/148551.diff

5 Files Affected:

  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (+4-3)
  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h (+68-145)
  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h (+43-80)
  • (modified) clang/lib/Analysis/ThreadSafetyCommon.cpp (+25-4)
  • (modified) clang/test/SemaCXX/warn-thread-safety-analysis.cpp (+50)
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 <sstream>
+#include "llvm/Support/raw_ostream.h"
 #include <string>
 #include <utility>
 #include <vector>
@@ -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 <class T>
   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<void>() {
-  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<bool>() {
-  return ValueType(BT_Bool, ST_1, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int8_t>() {
-  return ValueType(BT_Int, ST_8, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint8_t>() {
-  return ValueType(BT_Int, ST_8, false, 0);
+  return ValueType(BT_Bool);
 }
 
-template<>
-inline ValueType ValueType::getValueType<int16_t>() {
-  return ValueType(BT_Int, ST_16, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint16_t>() {
-  return ValueType(BT_Int, ST_16, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int32_t>() {
-  return ValueType(BT_Int, ST_32, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint32_t>() {
-  return ValueType(BT_Int, ST_32, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int64_t>() {
-  return ValueType(BT_Int, ST_64, true, 0);
+template <> inline ValueType ValueType::getValueType<char>() {
+  return ValueType(BT_AsciiChar);
 }
 
-template<>
-inline ValueType ValueType::getValueType<uint64_t>() {
-  return ValueType(BT_Int, ST_64, false, 0);
+template <> inline ValueType ValueType::getValueType<wchar_t>() {
+  return ValueType(BT_WideChar);
 }
 
-template<>
-inline ValueType ValueType::getValueType<float>() {
-  return ValueType(BT_Float, ST_32, true, 0);
+template <> inline ValueType ValueType::getValueType<char16_t>() {
+  return ValueType(BT_UTF16Char);
 }
 
-template<>
-inline ValueType ValueType::getValueType<double>() {
-  return ValueType(BT_Float, ST_64, true, 0);
+template <> inline ValueType ValueType::getValueType<char32_t>() {
+  return ValueType(BT_UTF32Char);
 }
 
-template<>
-inline ValueType ValueType::getValueType<long double>() {
-  return ValueType(BT_Float, ST_128, true, 0);
+template <> inline ValueType ValueType::getValueType<llvm::APInt>() {
+  return ValueType(BT_Int);
 }
 
 template<>
 inline ValueType ValueType::getValueType<StringRef>() {
-  return ValueType(BT_String, getSizeType(sizeof(StringRef)), false, 0);
+  return ValueType(BT_String);
 }
 
 template<>
 inline ValueType ValueType::getValueType<void*>() {
-  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 T> class LiteralT;
 
 // Base class for literal values.
 class Literal : public SExpr {
-public:
-  Literal(const Expr *C)
-     : SExpr(COP_Literal), ValType(ValueType::getValueType<void>()), 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<class T> const LiteralT<T>& as() const {
+    assert(ValType == ValueType::getValueType<T>());
     return *static_cast<const LiteralT<T>*>(this);
   }
   template<class T> LiteralT<T>& as() {
+    assert(ValType == ValueType::getValueType<T>());
     return *static_cast<LiteralT<T>*>(this);
   }
 
   template <class V> typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx);
 
-  template <class C>
-  typename C::CType compare(const Literal* E, C& Cmp) const {
-    // TODO: defer actual comparison to LiteralT
-    return Cmp.trueResult();
-  }
+  template <class C> 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 <class V>
 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<bool>());
-  case ValueType::BT_Int: {
-    switch (ValType.Size) {
-    case ValueType::ST_8:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int8_t>());
-      else
-        return Vs.reduceLiteralT(as<uint8_t>());
-    case ValueType::ST_16:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int16_t>());
-      else
-        return Vs.reduceLiteralT(as<uint16_t>());
-    case ValueType::ST_32:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int32_t>());
-      else
-        return Vs.reduceLiteralT(as<uint32_t>());
-    case ValueType::ST_64:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int64_t>());
-      else
-        return Vs.reduceLiteralT(as<uint64_t>());
-    default:
-      break;
-    }
-  }
-  case ValueType::BT_Float: {
-    switch (ValType.Size) {
-    case ValueType::ST_32:
-      return Vs.reduceLiteralT(as<float>());
-    case ValueType::ST_64:
-      return Vs.reduceLiteralT(as<double>());
-    default:
-      break;
-    }
-  }
+  case ValueType::BT_AsciiChar:
+    return Vs.reduceLiteralT(as<char>());
+  case ValueType::BT_WideChar:
+    return Vs.reduceLiteralT(as<wchar_t>());
+  case ValueType::BT_UTF16Char:
+    return Vs.reduceLiteralT(as<char16_t>());
+  case ValueType::BT_UTF32Char:
+    return Vs.reduceLiteralT(as<char32_t>());
+  case ValueType::BT_Int:
+    return Vs.reduceLiteralT(as<llvm::APInt>());
   case ValueType::BT_String:
     return Vs.reduceLiteralT(as<StringRef>());
   case ValueType::BT_Pointer:
-    return Vs.reduceLiteralT(as<void*>());
-  case ValueType::BT_ValueRef:
-    break;
+    return Vs.reduceLiteralT(as<void *>());
+  }
+  llvm_unreachable("Invalid BaseType");
+}
+
+template <class C>
+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<bool>().value(), E->as<bool>().value());
+  case ValueType::BT_AsciiChar:
+    return Cmp.compareIntegers(as<char>().value(), E->as<char>().value());
+  case ValueType::BT_WideChar:
+    return Cmp.compareIntegers(as<wchar_t>().value(), E->as<wchar_t>().value());
+  case ValueType::BT_UTF16Char:
+    return Cmp.compareIntegers(as<char16_t>().value(),
+                               E->as<char16_t>().value());
+  case ValueType::BT_UTF32Char:
+    return Cmp.compareIntegers(as<char32_t>().value(),
+                               E->as<char32_t>().value());
+  case ValueType::BT_Int:
+    return Cmp.compareIntegers(as<llvm::APInt>().value(),
+                               E->as<llvm::APInt>().value());
+  case ValueType::BT_String:
+    return Cmp.compareStrings(as<StringRef>().value(),
+                              E->as<StringRef>().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<Self, VisitReducerBase>,
   R_SExpr reduceUndefined(Undefined &Orig) { return true; }
   R_SExpr reduceWildcard(Wildcard &Orig) { return true; }
 
-  R_SExpr reduceLiteral(Literal &Orig) { return true; }
   template<class T>
   R_SExpr reduceLiteralT(LiteralT<T> &Orig) { return true; }
   R_SExpr reduceLiteralPtr(Literal &Orig) { return true; }
@@ -337,6 +336,9 @@ class EqualsComparator : public Comparator<EqualsComparator> {
   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<MatchComparator> {
   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<class T>
-  void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
-    SS << E->value();
-  }
-
-  void printLiteralT(const LiteralT<uint8_t> *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<bool>().value())
+        SS << "true";
+      else
+        SS << "false";
+      return;
+    case ValueType::BT_AsciiChar:
+      CharacterLiteral::print(E->as<char>().value(),
+                              CharacterLiteralKind::Ascii, SS);
+      return;
+    case ValueType::BT_WideChar:
+      CharacterLiteral::print(E->as<wchar_t>().value(),
+                              CharacterLiteralKind::Wide, SS);
+      return;
+    case ValueType::BT_UTF16Char:
+      CharacterLiteral::print(E->as<char16_t>().value(),
+                              CharacterLiteralKind::UTF16, SS);
+      return;
+    case ValueType::BT_UTF32Char:
+      CharacterLiteral::print(E->as<char32_t>().value(),
+                              CharacterLiteralKind::UTF32, SS);
+      return;
+    case ValueType::BT_Int: {
+      SmallVector<char, 32> Str;
+      E->as<llvm::APInt>().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<bool>().value())
-          SS << "true";
-        else
-          SS << "false";
-        return;
-      case ValueType::BT_Int:
-        switch (VT.Size) {
-        case ValueType::ST_8:
-          if (VT.Signed)
-            printLiteralT(&E->as<int8_t>(), SS);
-          else
-            printLiteralT(&E->as<uint8_t>(), SS);
-          return;
-        case ValueType::ST_16:
-          if (VT.Signed)
-            printLiteralT(&E->as<int16_t>(), SS);
-          else
-            printLiteralT(&E->as<uint16_t>(), SS);
-          return;
-        case ValueType::ST_32:
-          if (VT.Signed)
-            printLiteralT(&E->as<int32_t>(), SS);
-          else
-            printLiteralT(&E->as<uint32_t>(), SS);
-          return;
-        case ValueType::ST_64:
-          if (VT.Signed)
-            printLiteralT(&E->as<int64_t>(), SS);
-          else
-            printLiteralT(&E->as<uint64_t>(), SS);
-          return;
-        default:
-          break;
-        }
-        break;
-      case ValueType::BT_Float:
-        switch (VT.Size) {
-        case ValueType::ST_32:
-          printLiteralT(&E->as<float>(), SS);
-          return;
-        case ValueType::ST_64:
-          printLiteralT(&E->as<double>(), SS);
-          return;
-        default:
-          break;
-        }
-        break;
-      case ValueType::BT_String:
-        SS << "\"";
-        printLiteralT(&E->as<StringRef>(), SS);
-        SS << "\"";
-        return;
-      case ValueType::BT_Pointer:
-        SS << "#ptr";
-        return;
-      case ValueType::BT_ValueRef:
-        SS << "#vref";
-        return;
-      }
+    case ValueType::BT_String:
+      SS << '\"' << E->as<StringRef>().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<StdPrinter, std::ostream> {};
+class StdPrinter : public PrettyPrinter<StdPrinter, llvm::raw_ostream> {};
 
 } // 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<MaterializeTemporaryExpr>(S)->getSubExpr(), Ctx);
 
   // Collect all literals
-  case Stmt::CharacterLiteralClass:
+  case Stmt::CharacterLiteralClass: {
+    const auto *CL = cast<CharacterLiteral>(S);
+    unsigned Value = CL->getValue();
+    switch (CL->getKind()) {
+    case CharacterLiteralKind::Ascii:
+    case CharacterLiteralKind::UTF8:
+      return new (Arena) til::LiteralT<char>(Value);
+    case CharacterLiteralKind::Wide:
+      return new (Arena) til::LiteralT<wchar_t>(Value);
+    case CharacterLiteralKind::UTF16:
+      return new (Arena) til::LiteralT<char16_t>(Value);
+    case CharacterLiteralKind::UTF32:
+      return new (Arena) til::LiteralT<char32_t>(Value);
+    }
+    llvm_unreachable("Invalid CharacterLiteralKind");
+  }
   case Stmt::CXXNullPtrLiteralExprClass:
   case Stmt::GNUNullExprClass:
+    return new (Arena) til::LiteralT<void *>(nullptr);
   case Stmt::CXXBoolLiteralExprClass:
-  case Stmt::FloatingLiteralClass:
-  case Stmt::ImaginaryLiteralClass:
+    return new (Arena)
+        til::LiteralT<bool>(cast<CXXBoolLiteralExpr>(S)->getValue());
   case Stmt::IntegerLiteralClass:
+    return new (Arena)
+        til::LiteralT<llvm::APInt>(cast<IntegerLiteral>(S)->getValue());
   case Stmt::StringLiteralClass:
+    return new (Arena)
+        til::LiteralT<StringRef>(cast<StringLiteral>(S)->getString());
   case Stmt::ObjCStringLiteralClass:
-    return new (Arena) til::Literal(cast<Expr>(S));
+    return new (Arena) til::LiteralT<StringRef>(
+        cast<ObjCStringLiteral>(S)->getString()->getString());
 
   case Stmt::DeclStmtClass:
     return translateDeclStmt(cast<DeclStmt>(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; // \
+    // e...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 13, 2025

@llvm/pr-subscribers-clang-analysis

Author: Aaron Puchert (aaronpuchert)

Changes

The typical case for literals is an array of mutexes, where we want to distinguish mutex[0] from mutex[1] and so on. Currently they're treated as the same expression, in fact all literals are treated as the same expression.

The infrastructure for literals is already there, although it required some changes, and some simplifications seemed opportune:

  • The ValueType had fields for size and signedness. But Clang doesn't use native types and stores integer and (floating-point) literals as llvm::APInt regardless of size, so we don't need these properties. We could use them for characters, but it seems easier to just create different base types for now.
  • We remove the BT_Void: void literals don't exist in C++.
  • We remove BT_Float and BT_ValueRef: floating-point numbers and complex numbers are probably not used in lock expressions.

We turn Literal into a pure base class, as it seems to have been intended, and only create LiteralT instances of the correct type. Assertions on as ensure we're not mixing up types.

We print to llvm::raw_ostream instead of std::ostream because that's required for CharacterLiteral::print. Perhaps we should implement that ourselves though.

Fixes #58535.


Patch is 20.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/148551.diff

5 Files Affected:

  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (+4-3)
  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyTIL.h (+68-145)
  • (modified) clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h (+43-80)
  • (modified) clang/lib/Analysis/ThreadSafetyCommon.cpp (+25-4)
  • (modified) clang/test/SemaCXX/warn-thread-safety-analysis.cpp (+50)
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 <sstream>
+#include "llvm/Support/raw_ostream.h"
 #include <string>
 #include <utility>
 #include <vector>
@@ -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 <class T>
   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<void>() {
-  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<bool>() {
-  return ValueType(BT_Bool, ST_1, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int8_t>() {
-  return ValueType(BT_Int, ST_8, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint8_t>() {
-  return ValueType(BT_Int, ST_8, false, 0);
+  return ValueType(BT_Bool);
 }
 
-template<>
-inline ValueType ValueType::getValueType<int16_t>() {
-  return ValueType(BT_Int, ST_16, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint16_t>() {
-  return ValueType(BT_Int, ST_16, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int32_t>() {
-  return ValueType(BT_Int, ST_32, true, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<uint32_t>() {
-  return ValueType(BT_Int, ST_32, false, 0);
-}
-
-template<>
-inline ValueType ValueType::getValueType<int64_t>() {
-  return ValueType(BT_Int, ST_64, true, 0);
+template <> inline ValueType ValueType::getValueType<char>() {
+  return ValueType(BT_AsciiChar);
 }
 
-template<>
-inline ValueType ValueType::getValueType<uint64_t>() {
-  return ValueType(BT_Int, ST_64, false, 0);
+template <> inline ValueType ValueType::getValueType<wchar_t>() {
+  return ValueType(BT_WideChar);
 }
 
-template<>
-inline ValueType ValueType::getValueType<float>() {
-  return ValueType(BT_Float, ST_32, true, 0);
+template <> inline ValueType ValueType::getValueType<char16_t>() {
+  return ValueType(BT_UTF16Char);
 }
 
-template<>
-inline ValueType ValueType::getValueType<double>() {
-  return ValueType(BT_Float, ST_64, true, 0);
+template <> inline ValueType ValueType::getValueType<char32_t>() {
+  return ValueType(BT_UTF32Char);
 }
 
-template<>
-inline ValueType ValueType::getValueType<long double>() {
-  return ValueType(BT_Float, ST_128, true, 0);
+template <> inline ValueType ValueType::getValueType<llvm::APInt>() {
+  return ValueType(BT_Int);
 }
 
 template<>
 inline ValueType ValueType::getValueType<StringRef>() {
-  return ValueType(BT_String, getSizeType(sizeof(StringRef)), false, 0);
+  return ValueType(BT_String);
 }
 
 template<>
 inline ValueType ValueType::getValueType<void*>() {
-  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 T> class LiteralT;
 
 // Base class for literal values.
 class Literal : public SExpr {
-public:
-  Literal(const Expr *C)
-     : SExpr(COP_Literal), ValType(ValueType::getValueType<void>()), 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<class T> const LiteralT<T>& as() const {
+    assert(ValType == ValueType::getValueType<T>());
     return *static_cast<const LiteralT<T>*>(this);
   }
   template<class T> LiteralT<T>& as() {
+    assert(ValType == ValueType::getValueType<T>());
     return *static_cast<LiteralT<T>*>(this);
   }
 
   template <class V> typename V::R_SExpr traverse(V &Vs, typename V::R_Ctx Ctx);
 
-  template <class C>
-  typename C::CType compare(const Literal* E, C& Cmp) const {
-    // TODO: defer actual comparison to LiteralT
-    return Cmp.trueResult();
-  }
+  template <class C> 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 <class V>
 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<bool>());
-  case ValueType::BT_Int: {
-    switch (ValType.Size) {
-    case ValueType::ST_8:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int8_t>());
-      else
-        return Vs.reduceLiteralT(as<uint8_t>());
-    case ValueType::ST_16:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int16_t>());
-      else
-        return Vs.reduceLiteralT(as<uint16_t>());
-    case ValueType::ST_32:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int32_t>());
-      else
-        return Vs.reduceLiteralT(as<uint32_t>());
-    case ValueType::ST_64:
-      if (ValType.Signed)
-        return Vs.reduceLiteralT(as<int64_t>());
-      else
-        return Vs.reduceLiteralT(as<uint64_t>());
-    default:
-      break;
-    }
-  }
-  case ValueType::BT_Float: {
-    switch (ValType.Size) {
-    case ValueType::ST_32:
-      return Vs.reduceLiteralT(as<float>());
-    case ValueType::ST_64:
-      return Vs.reduceLiteralT(as<double>());
-    default:
-      break;
-    }
-  }
+  case ValueType::BT_AsciiChar:
+    return Vs.reduceLiteralT(as<char>());
+  case ValueType::BT_WideChar:
+    return Vs.reduceLiteralT(as<wchar_t>());
+  case ValueType::BT_UTF16Char:
+    return Vs.reduceLiteralT(as<char16_t>());
+  case ValueType::BT_UTF32Char:
+    return Vs.reduceLiteralT(as<char32_t>());
+  case ValueType::BT_Int:
+    return Vs.reduceLiteralT(as<llvm::APInt>());
   case ValueType::BT_String:
     return Vs.reduceLiteralT(as<StringRef>());
   case ValueType::BT_Pointer:
-    return Vs.reduceLiteralT(as<void*>());
-  case ValueType::BT_ValueRef:
-    break;
+    return Vs.reduceLiteralT(as<void *>());
+  }
+  llvm_unreachable("Invalid BaseType");
+}
+
+template <class C>
+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<bool>().value(), E->as<bool>().value());
+  case ValueType::BT_AsciiChar:
+    return Cmp.compareIntegers(as<char>().value(), E->as<char>().value());
+  case ValueType::BT_WideChar:
+    return Cmp.compareIntegers(as<wchar_t>().value(), E->as<wchar_t>().value());
+  case ValueType::BT_UTF16Char:
+    return Cmp.compareIntegers(as<char16_t>().value(),
+                               E->as<char16_t>().value());
+  case ValueType::BT_UTF32Char:
+    return Cmp.compareIntegers(as<char32_t>().value(),
+                               E->as<char32_t>().value());
+  case ValueType::BT_Int:
+    return Cmp.compareIntegers(as<llvm::APInt>().value(),
+                               E->as<llvm::APInt>().value());
+  case ValueType::BT_String:
+    return Cmp.compareStrings(as<StringRef>().value(),
+                              E->as<StringRef>().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<Self, VisitReducerBase>,
   R_SExpr reduceUndefined(Undefined &Orig) { return true; }
   R_SExpr reduceWildcard(Wildcard &Orig) { return true; }
 
-  R_SExpr reduceLiteral(Literal &Orig) { return true; }
   template<class T>
   R_SExpr reduceLiteralT(LiteralT<T> &Orig) { return true; }
   R_SExpr reduceLiteralPtr(Literal &Orig) { return true; }
@@ -337,6 +336,9 @@ class EqualsComparator : public Comparator<EqualsComparator> {
   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<MatchComparator> {
   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<class T>
-  void printLiteralT(const LiteralT<T> *E, StreamType &SS) {
-    SS << E->value();
-  }
-
-  void printLiteralT(const LiteralT<uint8_t> *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<bool>().value())
+        SS << "true";
+      else
+        SS << "false";
+      return;
+    case ValueType::BT_AsciiChar:
+      CharacterLiteral::print(E->as<char>().value(),
+                              CharacterLiteralKind::Ascii, SS);
+      return;
+    case ValueType::BT_WideChar:
+      CharacterLiteral::print(E->as<wchar_t>().value(),
+                              CharacterLiteralKind::Wide, SS);
+      return;
+    case ValueType::BT_UTF16Char:
+      CharacterLiteral::print(E->as<char16_t>().value(),
+                              CharacterLiteralKind::UTF16, SS);
+      return;
+    case ValueType::BT_UTF32Char:
+      CharacterLiteral::print(E->as<char32_t>().value(),
+                              CharacterLiteralKind::UTF32, SS);
+      return;
+    case ValueType::BT_Int: {
+      SmallVector<char, 32> Str;
+      E->as<llvm::APInt>().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<bool>().value())
-          SS << "true";
-        else
-          SS << "false";
-        return;
-      case ValueType::BT_Int:
-        switch (VT.Size) {
-        case ValueType::ST_8:
-          if (VT.Signed)
-            printLiteralT(&E->as<int8_t>(), SS);
-          else
-            printLiteralT(&E->as<uint8_t>(), SS);
-          return;
-        case ValueType::ST_16:
-          if (VT.Signed)
-            printLiteralT(&E->as<int16_t>(), SS);
-          else
-            printLiteralT(&E->as<uint16_t>(), SS);
-          return;
-        case ValueType::ST_32:
-          if (VT.Signed)
-            printLiteralT(&E->as<int32_t>(), SS);
-          else
-            printLiteralT(&E->as<uint32_t>(), SS);
-          return;
-        case ValueType::ST_64:
-          if (VT.Signed)
-            printLiteralT(&E->as<int64_t>(), SS);
-          else
-            printLiteralT(&E->as<uint64_t>(), SS);
-          return;
-        default:
-          break;
-        }
-        break;
-      case ValueType::BT_Float:
-        switch (VT.Size) {
-        case ValueType::ST_32:
-          printLiteralT(&E->as<float>(), SS);
-          return;
-        case ValueType::ST_64:
-          printLiteralT(&E->as<double>(), SS);
-          return;
-        default:
-          break;
-        }
-        break;
-      case ValueType::BT_String:
-        SS << "\"";
-        printLiteralT(&E->as<StringRef>(), SS);
-        SS << "\"";
-        return;
-      case ValueType::BT_Pointer:
-        SS << "#ptr";
-        return;
-      case ValueType::BT_ValueRef:
-        SS << "#vref";
-        return;
-      }
+    case ValueType::BT_String:
+      SS << '\"' << E->as<StringRef>().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<StdPrinter, std::ostream> {};
+class StdPrinter : public PrettyPrinter<StdPrinter, llvm::raw_ostream> {};
 
 } // 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<MaterializeTemporaryExpr>(S)->getSubExpr(), Ctx);
 
   // Collect all literals
-  case Stmt::CharacterLiteralClass:
+  case Stmt::CharacterLiteralClass: {
+    const auto *CL = cast<CharacterLiteral>(S);
+    unsigned Value = CL->getValue();
+    switch (CL->getKind()) {
+    case CharacterLiteralKind::Ascii:
+    case CharacterLiteralKind::UTF8:
+      return new (Arena) til::LiteralT<char>(Value);
+    case CharacterLiteralKind::Wide:
+      return new (Arena) til::LiteralT<wchar_t>(Value);
+    case CharacterLiteralKind::UTF16:
+      return new (Arena) til::LiteralT<char16_t>(Value);
+    case CharacterLiteralKind::UTF32:
+      return new (Arena) til::LiteralT<char32_t>(Value);
+    }
+    llvm_unreachable("Invalid CharacterLiteralKind");
+  }
   case Stmt::CXXNullPtrLiteralExprClass:
   case Stmt::GNUNullExprClass:
+    return new (Arena) til::LiteralT<void *>(nullptr);
   case Stmt::CXXBoolLiteralExprClass:
-  case Stmt::FloatingLiteralClass:
-  case Stmt::ImaginaryLiteralClass:
+    return new (Arena)
+        til::LiteralT<bool>(cast<CXXBoolLiteralExpr>(S)->getValue());
   case Stmt::IntegerLiteralClass:
+    return new (Arena)
+        til::LiteralT<llvm::APInt>(cast<IntegerLiteral>(S)->getValue());
   case Stmt::StringLiteralClass:
+    return new (Arena)
+        til::LiteralT<StringRef>(cast<StringLiteral>(S)->getString());
   case Stmt::ObjCStringLiteralClass:
-    return new (Arena) til::Literal(cast<Expr>(S));
+    return new (Arena) til::LiteralT<StringRef>(
+        cast<ObjCStringLiteral>(S)->getString()->getString());
 
   case Stmt::DeclStmtClass:
     return translateDeclStmt(cast<DeclStmt>(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; // \
+    // e...
[truncated]

@aaronpuchert aaronpuchert force-pushed the thread-safety-literal-comparison branch from 6edbe84 to b6bd8b5 Compare July 27, 2025 19:50
@aaronpuchert
Copy link
Member Author

@rupprecht, maybe you want to try this out?

@aaronpuchert aaronpuchert force-pushed the thread-safety-literal-comparison branch from b6bd8b5 to 21e3a8a Compare February 2, 2026 15:44
@github-actions
Copy link

github-actions bot commented Feb 2, 2026

✅ With the latest revision this PR passed the C/C++ code formatter.

@aaronpuchert aaronpuchert force-pushed the thread-safety-literal-comparison branch from 21e3a8a to 4a87e8e Compare February 2, 2026 15:47
@melver melver self-requested a review February 2, 2026 15:49
Copy link
Contributor

@melver melver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely forgot this PR was still not submitted.

I suppose it supersedes my attempt: #179041 ?

The typical case for literals is an array of mutexes, where we want to
distinguish `mutex[0]` from `mutex[1]` and so on. Currently they're
treated as the same expression, in fact all literals are treated as the
same expression.

The infrastructure for literals is already there, although it required
some changes, and some simplifications seemed opportune:
* The `ValueType` had fields for size and signedness. But only integer
  have signedness, and the size is irrelevant if we don't want to emit
  machine code. For the abstract semantics that we're interested in,
  only the number matters.
* We remove the `BT_Void`: `void` literals don't exist in C++.
* We remove `BT_Float` and `BT_ValueRef`: floating-point numbers and
  complex numbers are probably not used in lock expressions.
* We replace `BT_Pointer` with `BT_NullPointer`. There are no pointer
  literals, only null pointer literals.

It seems to me that `ValueType` was intended to assign types to any node
in the TIL, but it is currently only used for literals. I was trying to
come up with reasons to assign types everywhere, but we're not trying to
type check anything, and we don't support overloading. The comparison of
expressions surely doesn't need it. But maybe this should be clarified
at some point. (Including the fact that it is called Typed Intermediate
Language.)

We turn `Literal` into a pure base class, as it seems to have been
intended, and only create `LiteralT` instances of the correct type.
Assertions on `as` ensure we're not mixing up types.

We print to `llvm::raw_ostream` instead of `std::ostream` because that's
required for `CharacterLiteral::print`.

Fixes llvm#58535.
@aaronpuchert aaronpuchert force-pushed the thread-safety-literal-comparison branch from 4a87e8e to 52b022e Compare February 2, 2026 17:01
@aaronpuchert
Copy link
Member Author

My fault, I'm the one who forgot about it. And thanks for the reminder.

I'd hope that it also solves your problem, and I just checked that your added tests would also be green.

@melver
Copy link
Contributor

melver commented Feb 2, 2026

My fault, I'm the one who forgot about it. And thanks for the reminder.

I'd hope that it also solves your problem, and I just checked that your added tests would also be green.

Thanks! Feel free to copy my new tests from the other PR into this PR.

@aaronpuchert aaronpuchert merged commit 73417aa into llvm:main Feb 2, 2026
10 checks passed
@melver
Copy link
Contributor

melver commented Feb 3, 2026

If this survives a week+ in main, would it be appropriate to cherry-pick into the 22 release branch?

While it's been a long-standing "bug", I've seen this being a source of false positives on a few occasions now that it might be worth fixing in the 22.1 release.

@aaronpuchert
Copy link
Member Author

It's a bit large, but not a substantial change. It's just closing a gap. So I think it's Ok.

@melver
Copy link
Contributor

melver commented Feb 6, 2026

I was able to crash Clang with this commit (if I revert it, it no longer crashes):

clang -cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-obj -disable-free -clear-ast-before-backend -main-file-name cs-amp-lib.c -function-alignment 6 -falign-loops=1 -mrelocation-model static -fno-delete-null-pointer-checks -fwarn-stack-size=2048 -fno-jump-tables -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -mcmodel=kernel -target-cpu skylake-avx512 -target-feature +prfchw -target-feature -cldemote -target-feature +aes -target-feature +sahf -target-feature +pclmul -target-feature -xop -target-feature +crc32 -target-feature -amx-fp8 -target-feature +xsaves -target-feature -avx512fp16 -target-feature -usermsr -target-feature -sm4 -target-feature -egpr -target-feature +sse4.1 -target-feature -avx10.1 -target-feature -avx512ifma -target-feature +xsave -target-feature +sse4.2 -target-feature -tsxldtrk -target-feature -sm3 -target-feature -ptwrite -target-feature -widekl -target-feature -movrs -target-feature +invpcid -target-feature +64bit -target-feature +xsavec -target-feature -avx512vpopcntdq -target-feature +cmov -target-feature -avx512vp2intersect -target-feature +avx512cd -target-feature +movbe -target-feature -avxvnniint8 -target-feature -ccmp -target-feature -amx-int8 -target-feature -kl -target-feature -sha512 -target-feature -avxvnni -target-feature +rtm -target-feature +adx -target-feature +avx2 -target-feature -hreset -target-feature -movdiri -target-feature -serialize -target-feature -vpclmulqdq -target-feature +avx512vl -target-feature -uintr -target-feature -cf -target-feature +clflushopt -target-feature -raoint -target-feature -cmpccxadd -target-feature +bmi -target-feature -amx-tile -target-feature -gfni -target-feature -avxvnniint16 -target-feature -amx-fp16 -target-feature -zu -target-feature -ndd -target-feature +xsaveopt -target-feature +rdrnd -target-feature +avx512f -target-feature -amx-bf16 -target-feature -avx512bf16 -target-feature -avx512vnni -target-feature -push2pop2 -target-feature +cx8 -target-feature +avx512bw -target-feature +sse3 -target-feature +pku -target-feature -nf -target-feature -amx-tf32 -target-feature -amx-avx512 -target-feature +fsgsbase -target-feature -clzero -target-feature -mwaitx -target-feature -lwp -target-feature +lzcnt -target-feature -sha -target-feature -movdir64b -target-feature -ppx -target-feature -wbnoinvd -target-feature -enqcmd -target-feature -avxneconvert -target-feature -tbm -target-feature -pconfig -target-feature -amx-complex -target-feature +ssse3 -target-feature +cx16 -target-feature -avx10.2 -target-feature +bmi2 -target-feature +fma -target-feature +popcnt -target-feature -avxifma -target-feature +f16c -target-feature -avx512bitalg -target-feature -rdpru -target-feature +clwb -target-feature +rdseed -target-feature -avx512vbmi2 -target-feature -prefetchi -target-feature -amx-movrs -target-feature -rdpid -target-feature -fma4 -target-feature -avx512vbmi -target-feature -shstk -target-feature -vaes -target-feature -waitpkg -target-feature -sgx -target-feature +fxsr -target-feature +avx512dq -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse -target-feature -mmx -target-feature -sse2 -target-feature -avx -target-feature -sse4a -target-feature -x87 -target-feature +retpoline-external-thunk -target-feature +harden-sls-ijmp -target-feature +harden-sls-ret -disable-red-zone -mskip-rax-setup -fdebug-info-for-profiling -gkey-instructions -debug-info-kind=line-tables-only -dwarf-version=5 -debugger-tuning=gdb -fdebug-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp -fbasic-block-address-map -fcoverage-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp -nostdsysteminc -nobuiltininc -D __KERNEL__ -D CC_USING_NOP_MCOUNT -D CC_USING_FENTRY -D RANDSTRUCT -D WARN_CONTEXT_ANALYSIS -D KBUILD_MODFILE=\"sound/soc/codecs/snd-soc-cs-amp-lib\" -D KBUILD_BASENAME=\"cs_amp_lib\" -D KBUILD_MODNAME=\"snd_soc_cs_amp_lib\" -D __KBUILD_MODNAME=snd_soc_cs_amp_lib -Werror=unknown-warning-option -Werror=ignored-optimization-argument -Werror=option-ignored -Werror=unused-command-line-argument -Wno-sign-compare -Wall -Wextra -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-gnu -Wno-microsoft-anon-tag -Wno-format-overflow-non-kprintf -Wno-format-truncation-non-kprintf -Wno-default-const-init-unsafe -Wno-type-limits -Wno-pointer-sign -Wcast-function-type -Wno-unterminated-string-initialization -Wimplicit-fallthrough -Werror=date-time -Werror=incompatible-pointer-types -Wenum-conversion -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-format-overflow -Wno-override-init -Wno-pointer-to-enum-cast -Wno-tautological-constant-out-of-range-compare -Wno-unaligned-access -Wno-enum-compare-conditional -Wno-missing-field-initializers -Wno-shift-negative-value -Wno-enum-enum-conversion -Wno-sign-compare -Wno-unused-parameter -Wthread-safety -Wthread-safety-pointer -Wthread-safety-beta -std=gnu11 -ferror-limit 19 -fzero-call-used-regs=used-gpr -fpatchable-function-entry=59 -fpatchable-function-entry-offset=59 -pg -mfentry -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -mstack-protector-guard-reg=gs -mstack-protector-guard-symbol=__ref_stack_chk_guard -ftrivial-auto-var-init=pattern -mstack-alignment=8 -fcf-protection=branch -mfunction-return=thunk-extern -mindirect-branch-cs-prefix -fno-builtin-wcslen -fno-signed-char -fwchar-type=short -fno-signed-wchar -fms-extensions -fms-anonymous-structs -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -fexperimental-late-parse-attributes -mllvm -asan-mapping-offset=0xdffffc0000000000 -mllvm -asan-instrumentation-with-call-threshold=10000 -mllvm -asan-stack=0 -mllvm -asan-globals=1 -mllvm -asan-kernel-mem-intrinsic-prefix=1 -mllvm -enable-fs-discriminator=true -mllvm -improved-fs-discriminator=true -fsanitize-coverage-type=1 -fsanitize-coverage-type=3 -fsanitize-coverage-trace-cmp -fsanitize-coverage-trace-pc -fsanitize-coverage-stack-depth -fsanitize-coverage-stack-depth-callback-min=100 -fsanitize=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi -fsanitize-recover=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi -fsanitize-merge=array-bounds,bool,enum,shift-base,shift-exponent -fno-sanitize-memory-param-retval -fsanitize-cfi-icall-experimental-normalize-integers -fsanitize-kcfi-arity -fsanitize-address-use-after-scope -fno-sanitize-address-use-odr-indicator -faddrsig -fdwarf2-cfi-asm -x c cs-amp-lib-d6b847.c
1.      sound/soc/codecs/cs-amp-lib.c:805:1: current parser token 'const'
2.      sound/soc/codecs/cs-amp-lib.c:735:1: parsing function body 'cs_amp_devm_get_dell_ssidex'
 #0 0x000056007f8e7ed8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3bb6ed8)
 #1 0x000056007f8e55d5 llvm::sys::RunSignalHandlers() (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3bb45d5)
 #2 0x000056007f8e8cd1 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007ffab1a18df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x00007ffab1a6d95c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007ffab1a18cc2 raise ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x00007ffab1a014ac abort ./stdlib/abort.c:81:3
 #7 0x00007ffab1a01420 __assert_perror_fail ./assert/assert-perr.c:31:1
 #8 0x000056007fd07adc (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3fd6adc)
 #9 0x000056008231110e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e010e)
#10 0x000056008231252e clang::threadSafety::SExprBuilder::translateCallExpr(clang::CallExpr const*, clang::threadSafety::SExprBuilder::CallingContext*, clang::Expr const*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e152e)
#11 0x0000560082311783 clang::threadSafety::SExprBuilder::translateVariable(clang::VarDecl const*, clang::threadSafety::SExprBuilder::CallingContext*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e0783)
#12 0x00005600822fe80c (anonymous namespace)::ThreadSafetyAnalyzer::runAnalysis(clang::AnalysisDeclContext&) ThreadSafety.cpp:0:0
#13 0x00005600822fa4cf clang::threadSafety::runThreadSafetyAnalysis(clang::AnalysisDeclContext&, clang::threadSafety::ThreadSafetyHandler&, clang::threadSafety::BeforeSet**) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65c94cf)
#14 0x00005600821a0310 clang::sema::AnalysisBasedWarnings::IssueWarnings(clang::sema::AnalysisBasedWarnings::Policy, clang::sema::FunctionScopeInfo*, clang::Decl const*, clang::QualType) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x646f310)
#15 0x00005600818ed5d5 clang::Sema::PopFunctionScopeInfo(clang::sema::AnalysisBasedWarnings::Policy const*, clang::Decl*, clang::QualType) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5bbc5d5)
#16 0x0000560081a96110 clang::Sema::ActOnFinishFunctionBody(clang::Decl*, clang::Stmt*, bool, bool) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5d65110)
#17 0x000056008186e608 clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5b3d608)
#18 0x000056008179712e clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a6612e)
#19 0x00005600817be594 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::Parser::ParsedTemplateInfo&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a8d594)
#20 0x000056008179604f clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a6504f)
#21 0x0000560081795936 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a64936)
#22 0x00005600817949de clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a639de)
#23 0x0000560081792c00 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a61c00)
#24 0x0000560081786cfe clang::ParseAST(clang::Sema&, bool, bool) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a55cfe)
#25 0x0000560080324866 clang::FrontendAction::Execute() (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x45f3866)
#26 0x000056008028ab2d clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x4559b2d)
#27 0x000056008040d57a clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x46dc57a)
#28 0x000056007cc554da cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf244da)
#29 0x000056007cc5137f ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) driver.cpp:0:0
#30 0x000056007cc503cb clang_main(int, char**, llvm::ToolContext const&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf1f3cb)
#31 0x000056007cc60917 main (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf2f917)
#32 0x00007ffab1a02ca8 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#33 0x00007ffab1a02d65 call_init ./csu/../csu/libc-start.c:128:20
#34 0x00007ffab1a02d65 __libc_start_main ./csu/../csu/libc-start.c:347:5
#35 0x000056007cc4ed81 _start (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf1dd81)
./cs-amp-lib-d6b847.sh: line 1: 1928721 Aborted                    (core dumped) "$CC" "-cc1" "-triple" "x86_64-unknown-linux-gnu" "-O2" "-emit-obj" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "cs-amp-lib.c" "-function-alignment" "6" "-falign-loops=1" "-mrelocation-model" "static" "-fno-delete-null-pointer-checks" "-fwarn-stack-size=2048" "-fno-jump-tables" "-mframe-pointer=none" "-relaxed-aliasing" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-mcmodel=kernel" "-target-cpu" "skylake-avx512" "-target-feature" "+prfchw" "-target-feature" "-cldemote" "-target-feature" "+aes" "-target-feature" "+sahf" "-target-feature" "+pclmul" "-target-feature" "-xop" "-target-feature" "+crc32" "-target-feature" "-amx-fp8" "-target-feature" "+xsaves" "-target-feature" "-avx512fp16" "-target-feature" "-usermsr" "-target-feature" "-sm4" "-target-feature" "-egpr" "-target-feature" "+sse4.1" "-target-feature" "-avx10.1" "-target-feature" "-avx512ifma" "-target-feature" "+xsave" "-target-feature" "+sse4.2" "-target-feature" "-tsxldtrk" "-target-feature" "-sm3" "-target-feature" "-ptwrite" "-target-feature" "-widekl" "-target-feature" "-movrs" "-target-feature" "+invpcid" "-target-feature" "+64bit" "-target-feature" "+xsavec" "-target-feature" "-avx512vpopcntdq" "-target-feature" "+cmov" "-target-feature" "-avx512vp2intersect" "-target-feature" "+avx512cd" "-target-feature" "+movbe" "-target-feature" "-avxvnniint8" "-target-feature" "-ccmp" "-target-feature" "-amx-int8" "-target-feature" "-kl" "-target-feature" "-sha512" "-target-feature" "-avxvnni" "-target-feature" "+rtm" "-target-feature" "+adx" "-target-feature" "+avx2" "-target-feature" "-hreset" "-target-feature" "-movdiri" "-target-feature" "-serialize" "-target-feature" "-vpclmulqdq" "-target-feature" "+avx512vl" "-target-feature" "-uintr" "-target-feature" "-cf" "-target-feature" "+clflushopt" "-target-feature" "-raoint" "-target-feature" "-cmpccxadd" "-target-feature" "+bmi" "-target-feature" "-amx-tile" "-target-feature" "-gfni" "-target-feature" "-avxvnniint16" "-target-feature" "-amx-fp16" "-target-feature" "-zu" "-target-feature" "-ndd" "-target-feature" "+xsaveopt" "-target-feature" "+rdrnd" "-target-feature" "+avx512f" "-target-feature" "-amx-bf16" "-target-feature" "-avx512bf16" "-target-feature" "-avx512vnni" "-target-feature" "-push2pop2" "-target-feature" "+cx8" "-target-feature" "+avx512bw" "-target-feature" "+sse3" "-target-feature" "+pku" "-target-feature" "-nf" "-target-feature" "-amx-tf32" "-target-feature" "-amx-avx512" "-target-feature" "+fsgsbase" "-target-feature" "-clzero" "-target-feature" "-mwaitx" "-target-feature" "-lwp" "-target-feature" "+lzcnt" "-target-feature" "-sha" "-target-feature" "-movdir64b" "-target-feature" "-ppx" "-target-feature" "-wbnoinvd" "-target-feature" "-enqcmd" "-target-feature" "-avxneconvert" "-target-feature" "-tbm" "-target-feature" "-pconfig" "-target-feature" "-amx-complex" "-target-feature" "+ssse3" "-target-feature" "+cx16" "-target-feature" "-avx10.2" "-target-feature" "+bmi2" "-target-feature" "+fma" "-target-feature" "+popcnt" "-target-feature" "-avxifma" "-target-feature" "+f16c" "-target-feature" "-avx512bitalg" "-target-feature" "-rdpru" "-target-feature" "+clwb" "-target-feature" "+rdseed" "-target-feature" "-avx512vbmi2" "-target-feature" "-prefetchi" "-target-feature" "-amx-movrs" "-target-feature" "-rdpid" "-target-feature" "-fma4" "-target-feature" "-avx512vbmi" "-target-feature" "-shstk" "-target-feature" "-vaes" "-target-feature" "-waitpkg" "-target-feature" "-sgx" "-target-feature" "+fxsr" "-target-feature" "+avx512dq" "-target-feature" "+retpoline-indirect-calls" "-target-feature" "+retpoline-indirect-branches" "-target-feature" "-sse" "-target-feature" "-mmx" "-target-feature" "-sse2" "-target-feature" "-avx" "-target-feature" "-sse4a" "-target-feature" "-x87" "-target-feature" "+retpoline-external-thunk" "-target-feature" "+harden-sls-ijmp" "-target-feature" "+harden-sls-ret" "-disable-red-zone" "-mskip-rax-setup" "-fdebug-info-for-profiling" "-gkey-instructions" "-debug-info-kind=line-tables-only" "-dwarf-version=5" "-debugger-tuning=gdb" "-fdebug-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp" "-fbasic-block-address-map" "-fcoverage-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp" "-nostdsysteminc" "-nobuiltininc" "-D" "__KERNEL__" "-D" "CC_USING_NOP_MCOUNT" "-D" "CC_USING_FENTRY" "-D" "RANDSTRUCT" "-D" "WARN_CONTEXT_ANALYSIS" "-D" "KBUILD_MODFILE=\"sound/soc/codecs/snd-soc-cs-amp-lib\"" "-D" "KBUILD_BASENAME=\"cs_amp_lib\"" "-D" "KBUILD_MODNAME=\"snd_soc_cs_amp_lib\"" "-D" "__KBUILD_MODNAME=snd_soc_cs_amp_lib" "-Werror=unknown-warning-option" "-Werror=ignored-optimization-argument" "-Werror=option-ignored" "-Werror=unused-command-line-argument" "-Wno-sign-compare" "-Wall" "-Wextra" "-Wundef" "-Werror=implicit-function-declaration" "-Werror=implicit-int" "-Werror=return-type" "-Werror=strict-prototypes" "-Wno-format-security" "-Wno-trigraphs" "-Wno-frame-address" "-Wno-address-of-packed-member" "-Wmissing-declarations" "-Wmissing-prototypes" "-Wframe-larger-than=2048" "-Wno-gnu" "-Wno-microsoft-anon-tag" "-Wno-format-overflow-non-kprintf" "-Wno-format-truncation-non-kprintf" "-Wno-default-const-init-unsafe" "-Wno-type-limits" "-Wno-pointer-sign" "-Wcast-function-type" "-Wno-unterminated-string-initialization" "-Wimplicit-fallthrough" "-Werror=date-time" "-Werror=incompatible-pointer-types" "-Wenum-conversion" "-Wunused" "-Wno-unused-but-set-variable" "-Wno-unused-const-variable" "-Wno-format-overflow" "-Wno-override-init" "-Wno-pointer-to-enum-cast" "-Wno-tautological-constant-out-of-range-compare" "-Wno-unaligned-access" "-Wno-enum-compare-conditional" "-Wno-missing-field-initializers" "-Wno-shift-negative-value" "-Wno-enum-enum-conversion" "-Wno-sign-compare" "-Wno-unused-parameter" "-Wthread-safety" "-Wthread-safety-pointer" "-Wthread-safety-beta" "-std=gnu11" "-ferror-limit" "19" "-fzero-call-used-regs=used-gpr" "-fpatchable-function-entry=59" "-fpatchable-function-entry-offset=59" "-pg" "-mfentry" "-fwrapv" "-fwrapv-pointer" "-fstrict-flex-arrays=3" "-stack-protector" "2" "-mstack-protector-guard-reg=gs" "-mstack-protector-guard-symbol=__ref_stack_chk_guard" "-ftrivial-auto-var-init=pattern" "-mstack-alignment=8" "-fcf-protection=branch" "-mfunction-return=thunk-extern" "-mindirect-branch-cs-prefix" "-fno-builtin-wcslen" "-fno-signed-char" "-fwchar-type=short" "-fno-signed-wchar" "-fms-extensions" "-fms-anonymous-structs" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf" "-vectorize-loops" "-vectorize-slp" "-fexperimental-late-parse-attributes" "-mllvm" "-asan-mapping-offset=0xdffffc0000000000" "-mllvm" "-asan-instrumentation-with-call-threshold=10000" "-mllvm" "-asan-stack=0" "-mllvm" "-asan-globals=1" "-mllvm" "-asan-kernel-mem-intrinsic-prefix=1" "-mllvm" "-enable-fs-discriminator=true" "-mllvm" "-improved-fs-discriminator=true" "-fsanitize-coverage-type=1" "-fsanitize-coverage-type=3" "-fsanitize-coverage-trace-cmp" "-fsanitize-coverage-trace-pc" "-fsanitize-coverage-stack-depth" "-fsanitize-coverage-stack-depth-callback-min=100" "-fsanitize=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi" "-fsanitize-recover=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi" "-fsanitize-merge=array-bounds,bool,enum,shift-base,shift-exponent" "-fno-sanitize-memory-param-retval" "-fsanitize-cfi-icall-experimental-normalize-integers" "-fsanitize-kcfi-arity" "-fsanitize-address-use-after-scope" "-fno-sanitize-address-use-odr-indicator" "-faddrsig" "-fdwarf2-cfi-asm" "-x" "c" "cs-amp-lib-d6b847.c"

cs-amp-lib-d6b847.c
cs-amp-lib-d6b847.sh

cc @bvanassche

melver added a commit to melver/llvm-project that referenced this pull request Feb 7, 2026
The SExprBuilder was previously using StringLiteral::getString() to
extract the value of string literals. This method asserts that the
string is a narrow string (char width == 1):

```
clang/include/clang/AST/Expr.h:1872: StringRef clang::StringLiteral::getString() const: Assertion `(isUnevaluated() || getCharByteWidth() == 1) && "This function is used in places that assume strings use char"' failed.
[...]
 llvm#9 0x0000556247fcfe3e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*)
[...]
```

This fails when using wide string literals as in the new test case
added.

Switch to using StringLiteral::getBytes(), which provides the raw byte
representation of the string. This is sufficient to compare expressions
for identity.

Fixes: llvm#148551
@melver
Copy link
Contributor

melver commented Feb 7, 2026

I was able to crash Clang with this commit (if I revert it, it no longer crashes):

clang -cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-obj -disable-free -clear-ast-before-backend -main-file-name cs-amp-lib.c -function-alignment 6 -falign-loops=1 -mrelocation-model static -fno-delete-null-pointer-checks -fwarn-stack-size=2048 -fno-jump-tables -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -mcmodel=kernel -target-cpu skylake-avx512 -target-feature +prfchw -target-feature -cldemote -target-feature +aes -target-feature +sahf -target-feature +pclmul -target-feature -xop -target-feature +crc32 -target-feature -amx-fp8 -target-feature +xsaves -target-feature -avx512fp16 -target-feature -usermsr -target-feature -sm4 -target-feature -egpr -target-feature +sse4.1 -target-feature -avx10.1 -target-feature -avx512ifma -target-feature +xsave -target-feature +sse4.2 -target-feature -tsxldtrk -target-feature -sm3 -target-feature -ptwrite -target-feature -widekl -target-feature -movrs -target-feature +invpcid -target-feature +64bit -target-feature +xsavec -target-feature -avx512vpopcntdq -target-feature +cmov -target-feature -avx512vp2intersect -target-feature +avx512cd -target-feature +movbe -target-feature -avxvnniint8 -target-feature -ccmp -target-feature -amx-int8 -target-feature -kl -target-feature -sha512 -target-feature -avxvnni -target-feature +rtm -target-feature +adx -target-feature +avx2 -target-feature -hreset -target-feature -movdiri -target-feature -serialize -target-feature -vpclmulqdq -target-feature +avx512vl -target-feature -uintr -target-feature -cf -target-feature +clflushopt -target-feature -raoint -target-feature -cmpccxadd -target-feature +bmi -target-feature -amx-tile -target-feature -gfni -target-feature -avxvnniint16 -target-feature -amx-fp16 -target-feature -zu -target-feature -ndd -target-feature +xsaveopt -target-feature +rdrnd -target-feature +avx512f -target-feature -amx-bf16 -target-feature -avx512bf16 -target-feature -avx512vnni -target-feature -push2pop2 -target-feature +cx8 -target-feature +avx512bw -target-feature +sse3 -target-feature +pku -target-feature -nf -target-feature -amx-tf32 -target-feature -amx-avx512 -target-feature +fsgsbase -target-feature -clzero -target-feature -mwaitx -target-feature -lwp -target-feature +lzcnt -target-feature -sha -target-feature -movdir64b -target-feature -ppx -target-feature -wbnoinvd -target-feature -enqcmd -target-feature -avxneconvert -target-feature -tbm -target-feature -pconfig -target-feature -amx-complex -target-feature +ssse3 -target-feature +cx16 -target-feature -avx10.2 -target-feature +bmi2 -target-feature +fma -target-feature +popcnt -target-feature -avxifma -target-feature +f16c -target-feature -avx512bitalg -target-feature -rdpru -target-feature +clwb -target-feature +rdseed -target-feature -avx512vbmi2 -target-feature -prefetchi -target-feature -amx-movrs -target-feature -rdpid -target-feature -fma4 -target-feature -avx512vbmi -target-feature -shstk -target-feature -vaes -target-feature -waitpkg -target-feature -sgx -target-feature +fxsr -target-feature +avx512dq -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse -target-feature -mmx -target-feature -sse2 -target-feature -avx -target-feature -sse4a -target-feature -x87 -target-feature +retpoline-external-thunk -target-feature +harden-sls-ijmp -target-feature +harden-sls-ret -disable-red-zone -mskip-rax-setup -fdebug-info-for-profiling -gkey-instructions -debug-info-kind=line-tables-only -dwarf-version=5 -debugger-tuning=gdb -fdebug-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp -fbasic-block-address-map -fcoverage-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp -nostdsysteminc -nobuiltininc -D __KERNEL__ -D CC_USING_NOP_MCOUNT -D CC_USING_FENTRY -D RANDSTRUCT -D WARN_CONTEXT_ANALYSIS -D KBUILD_MODFILE=\"sound/soc/codecs/snd-soc-cs-amp-lib\" -D KBUILD_BASENAME=\"cs_amp_lib\" -D KBUILD_MODNAME=\"snd_soc_cs_amp_lib\" -D __KBUILD_MODNAME=snd_soc_cs_amp_lib -Werror=unknown-warning-option -Werror=ignored-optimization-argument -Werror=option-ignored -Werror=unused-command-line-argument -Wno-sign-compare -Wall -Wextra -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-gnu -Wno-microsoft-anon-tag -Wno-format-overflow-non-kprintf -Wno-format-truncation-non-kprintf -Wno-default-const-init-unsafe -Wno-type-limits -Wno-pointer-sign -Wcast-function-type -Wno-unterminated-string-initialization -Wimplicit-fallthrough -Werror=date-time -Werror=incompatible-pointer-types -Wenum-conversion -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-format-overflow -Wno-override-init -Wno-pointer-to-enum-cast -Wno-tautological-constant-out-of-range-compare -Wno-unaligned-access -Wno-enum-compare-conditional -Wno-missing-field-initializers -Wno-shift-negative-value -Wno-enum-enum-conversion -Wno-sign-compare -Wno-unused-parameter -Wthread-safety -Wthread-safety-pointer -Wthread-safety-beta -std=gnu11 -ferror-limit 19 -fzero-call-used-regs=used-gpr -fpatchable-function-entry=59 -fpatchable-function-entry-offset=59 -pg -mfentry -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -mstack-protector-guard-reg=gs -mstack-protector-guard-symbol=__ref_stack_chk_guard -ftrivial-auto-var-init=pattern -mstack-alignment=8 -fcf-protection=branch -mfunction-return=thunk-extern -mindirect-branch-cs-prefix -fno-builtin-wcslen -fno-signed-char -fwchar-type=short -fno-signed-wchar -fms-extensions -fms-anonymous-structs -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -vectorize-loops -vectorize-slp -fexperimental-late-parse-attributes -mllvm -asan-mapping-offset=0xdffffc0000000000 -mllvm -asan-instrumentation-with-call-threshold=10000 -mllvm -asan-stack=0 -mllvm -asan-globals=1 -mllvm -asan-kernel-mem-intrinsic-prefix=1 -mllvm -enable-fs-discriminator=true -mllvm -improved-fs-discriminator=true -fsanitize-coverage-type=1 -fsanitize-coverage-type=3 -fsanitize-coverage-trace-cmp -fsanitize-coverage-trace-pc -fsanitize-coverage-stack-depth -fsanitize-coverage-stack-depth-callback-min=100 -fsanitize=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi -fsanitize-recover=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi -fsanitize-merge=array-bounds,bool,enum,shift-base,shift-exponent -fno-sanitize-memory-param-retval -fsanitize-cfi-icall-experimental-normalize-integers -fsanitize-kcfi-arity -fsanitize-address-use-after-scope -fno-sanitize-address-use-odr-indicator -faddrsig -fdwarf2-cfi-asm -x c cs-amp-lib-d6b847.c
1.      sound/soc/codecs/cs-amp-lib.c:805:1: current parser token 'const'
2.      sound/soc/codecs/cs-amp-lib.c:735:1: parsing function body 'cs_amp_devm_get_dell_ssidex'
 #0 0x000056007f8e7ed8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3bb6ed8)
 #1 0x000056007f8e55d5 llvm::sys::RunSignalHandlers() (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3bb45d5)
 #2 0x000056007f8e8cd1 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x00007ffab1a18df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0)
 #4 0x00007ffab1a6d95c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007ffab1a18cc2 raise ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x00007ffab1a014ac abort ./stdlib/abort.c:81:3
 #7 0x00007ffab1a01420 __assert_perror_fail ./assert/assert-perr.c:31:1
 #8 0x000056007fd07adc (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x3fd6adc)
 #9 0x000056008231110e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e010e)
#10 0x000056008231252e clang::threadSafety::SExprBuilder::translateCallExpr(clang::CallExpr const*, clang::threadSafety::SExprBuilder::CallingContext*, clang::Expr const*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e152e)
#11 0x0000560082311783 clang::threadSafety::SExprBuilder::translateVariable(clang::VarDecl const*, clang::threadSafety::SExprBuilder::CallingContext*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65e0783)
#12 0x00005600822fe80c (anonymous namespace)::ThreadSafetyAnalyzer::runAnalysis(clang::AnalysisDeclContext&) ThreadSafety.cpp:0:0
#13 0x00005600822fa4cf clang::threadSafety::runThreadSafetyAnalysis(clang::AnalysisDeclContext&, clang::threadSafety::ThreadSafetyHandler&, clang::threadSafety::BeforeSet**) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x65c94cf)
#14 0x00005600821a0310 clang::sema::AnalysisBasedWarnings::IssueWarnings(clang::sema::AnalysisBasedWarnings::Policy, clang::sema::FunctionScopeInfo*, clang::Decl const*, clang::QualType) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x646f310)
#15 0x00005600818ed5d5 clang::Sema::PopFunctionScopeInfo(clang::sema::AnalysisBasedWarnings::Policy const*, clang::Decl*, clang::QualType) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5bbc5d5)
#16 0x0000560081a96110 clang::Sema::ActOnFinishFunctionBody(clang::Decl*, clang::Stmt*, bool, bool) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5d65110)
#17 0x000056008186e608 clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5b3d608)
#18 0x000056008179712e clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a6612e)
#19 0x00005600817be594 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::Parser::ParsedTemplateInfo&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a8d594)
#20 0x000056008179604f clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a6504f)
#21 0x0000560081795936 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a64936)
#22 0x00005600817949de clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a639de)
#23 0x0000560081792c00 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a61c00)
#24 0x0000560081786cfe clang::ParseAST(clang::Sema&, bool, bool) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x5a55cfe)
#25 0x0000560080324866 clang::FrontendAction::Execute() (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x45f3866)
#26 0x000056008028ab2d clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x4559b2d)
#27 0x000056008040d57a clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0x46dc57a)
#28 0x000056007cc554da cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf244da)
#29 0x000056007cc5137f ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) driver.cpp:0:0
#30 0x000056007cc503cb clang_main(int, char**, llvm::ToolContext const&) (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf1f3cb)
#31 0x000056007cc60917 main (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf2f917)
#32 0x00007ffab1a02ca8 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#33 0x00007ffab1a02d65 call_init ./csu/../csu/libc-start.c:128:20
#34 0x00007ffab1a02d65 __libc_start_main ./csu/../csu/libc-start.c:347:5
#35 0x000056007cc4ed81 _start (/usr/local/google/home/elver/third_party/llvm/build/bin/clang+0xf1dd81)
./cs-amp-lib-d6b847.sh: line 1: 1928721 Aborted                    (core dumped) "$CC" "-cc1" "-triple" "x86_64-unknown-linux-gnu" "-O2" "-emit-obj" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "cs-amp-lib.c" "-function-alignment" "6" "-falign-loops=1" "-mrelocation-model" "static" "-fno-delete-null-pointer-checks" "-fwarn-stack-size=2048" "-fno-jump-tables" "-mframe-pointer=none" "-relaxed-aliasing" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-mcmodel=kernel" "-target-cpu" "skylake-avx512" "-target-feature" "+prfchw" "-target-feature" "-cldemote" "-target-feature" "+aes" "-target-feature" "+sahf" "-target-feature" "+pclmul" "-target-feature" "-xop" "-target-feature" "+crc32" "-target-feature" "-amx-fp8" "-target-feature" "+xsaves" "-target-feature" "-avx512fp16" "-target-feature" "-usermsr" "-target-feature" "-sm4" "-target-feature" "-egpr" "-target-feature" "+sse4.1" "-target-feature" "-avx10.1" "-target-feature" "-avx512ifma" "-target-feature" "+xsave" "-target-feature" "+sse4.2" "-target-feature" "-tsxldtrk" "-target-feature" "-sm3" "-target-feature" "-ptwrite" "-target-feature" "-widekl" "-target-feature" "-movrs" "-target-feature" "+invpcid" "-target-feature" "+64bit" "-target-feature" "+xsavec" "-target-feature" "-avx512vpopcntdq" "-target-feature" "+cmov" "-target-feature" "-avx512vp2intersect" "-target-feature" "+avx512cd" "-target-feature" "+movbe" "-target-feature" "-avxvnniint8" "-target-feature" "-ccmp" "-target-feature" "-amx-int8" "-target-feature" "-kl" "-target-feature" "-sha512" "-target-feature" "-avxvnni" "-target-feature" "+rtm" "-target-feature" "+adx" "-target-feature" "+avx2" "-target-feature" "-hreset" "-target-feature" "-movdiri" "-target-feature" "-serialize" "-target-feature" "-vpclmulqdq" "-target-feature" "+avx512vl" "-target-feature" "-uintr" "-target-feature" "-cf" "-target-feature" "+clflushopt" "-target-feature" "-raoint" "-target-feature" "-cmpccxadd" "-target-feature" "+bmi" "-target-feature" "-amx-tile" "-target-feature" "-gfni" "-target-feature" "-avxvnniint16" "-target-feature" "-amx-fp16" "-target-feature" "-zu" "-target-feature" "-ndd" "-target-feature" "+xsaveopt" "-target-feature" "+rdrnd" "-target-feature" "+avx512f" "-target-feature" "-amx-bf16" "-target-feature" "-avx512bf16" "-target-feature" "-avx512vnni" "-target-feature" "-push2pop2" "-target-feature" "+cx8" "-target-feature" "+avx512bw" "-target-feature" "+sse3" "-target-feature" "+pku" "-target-feature" "-nf" "-target-feature" "-amx-tf32" "-target-feature" "-amx-avx512" "-target-feature" "+fsgsbase" "-target-feature" "-clzero" "-target-feature" "-mwaitx" "-target-feature" "-lwp" "-target-feature" "+lzcnt" "-target-feature" "-sha" "-target-feature" "-movdir64b" "-target-feature" "-ppx" "-target-feature" "-wbnoinvd" "-target-feature" "-enqcmd" "-target-feature" "-avxneconvert" "-target-feature" "-tbm" "-target-feature" "-pconfig" "-target-feature" "-amx-complex" "-target-feature" "+ssse3" "-target-feature" "+cx16" "-target-feature" "-avx10.2" "-target-feature" "+bmi2" "-target-feature" "+fma" "-target-feature" "+popcnt" "-target-feature" "-avxifma" "-target-feature" "+f16c" "-target-feature" "-avx512bitalg" "-target-feature" "-rdpru" "-target-feature" "+clwb" "-target-feature" "+rdseed" "-target-feature" "-avx512vbmi2" "-target-feature" "-prefetchi" "-target-feature" "-amx-movrs" "-target-feature" "-rdpid" "-target-feature" "-fma4" "-target-feature" "-avx512vbmi" "-target-feature" "-shstk" "-target-feature" "-vaes" "-target-feature" "-waitpkg" "-target-feature" "-sgx" "-target-feature" "+fxsr" "-target-feature" "+avx512dq" "-target-feature" "+retpoline-indirect-calls" "-target-feature" "+retpoline-indirect-branches" "-target-feature" "-sse" "-target-feature" "-mmx" "-target-feature" "-sse2" "-target-feature" "-avx" "-target-feature" "-sse4a" "-target-feature" "-x87" "-target-feature" "+retpoline-external-thunk" "-target-feature" "+harden-sls-ijmp" "-target-feature" "+harden-sls-ret" "-disable-red-zone" "-mskip-rax-setup" "-fdebug-info-for-profiling" "-gkey-instructions" "-debug-info-kind=line-tables-only" "-dwarf-version=5" "-debugger-tuning=gdb" "-fdebug-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp" "-fbasic-block-address-map" "-fcoverage-compilation-dir=/usr/local/google/home/elver/workspace/linux-tmp" "-nostdsysteminc" "-nobuiltininc" "-D" "__KERNEL__" "-D" "CC_USING_NOP_MCOUNT" "-D" "CC_USING_FENTRY" "-D" "RANDSTRUCT" "-D" "WARN_CONTEXT_ANALYSIS" "-D" "KBUILD_MODFILE=\"sound/soc/codecs/snd-soc-cs-amp-lib\"" "-D" "KBUILD_BASENAME=\"cs_amp_lib\"" "-D" "KBUILD_MODNAME=\"snd_soc_cs_amp_lib\"" "-D" "__KBUILD_MODNAME=snd_soc_cs_amp_lib" "-Werror=unknown-warning-option" "-Werror=ignored-optimization-argument" "-Werror=option-ignored" "-Werror=unused-command-line-argument" "-Wno-sign-compare" "-Wall" "-Wextra" "-Wundef" "-Werror=implicit-function-declaration" "-Werror=implicit-int" "-Werror=return-type" "-Werror=strict-prototypes" "-Wno-format-security" "-Wno-trigraphs" "-Wno-frame-address" "-Wno-address-of-packed-member" "-Wmissing-declarations" "-Wmissing-prototypes" "-Wframe-larger-than=2048" "-Wno-gnu" "-Wno-microsoft-anon-tag" "-Wno-format-overflow-non-kprintf" "-Wno-format-truncation-non-kprintf" "-Wno-default-const-init-unsafe" "-Wno-type-limits" "-Wno-pointer-sign" "-Wcast-function-type" "-Wno-unterminated-string-initialization" "-Wimplicit-fallthrough" "-Werror=date-time" "-Werror=incompatible-pointer-types" "-Wenum-conversion" "-Wunused" "-Wno-unused-but-set-variable" "-Wno-unused-const-variable" "-Wno-format-overflow" "-Wno-override-init" "-Wno-pointer-to-enum-cast" "-Wno-tautological-constant-out-of-range-compare" "-Wno-unaligned-access" "-Wno-enum-compare-conditional" "-Wno-missing-field-initializers" "-Wno-shift-negative-value" "-Wno-enum-enum-conversion" "-Wno-sign-compare" "-Wno-unused-parameter" "-Wthread-safety" "-Wthread-safety-pointer" "-Wthread-safety-beta" "-std=gnu11" "-ferror-limit" "19" "-fzero-call-used-regs=used-gpr" "-fpatchable-function-entry=59" "-fpatchable-function-entry-offset=59" "-pg" "-mfentry" "-fwrapv" "-fwrapv-pointer" "-fstrict-flex-arrays=3" "-stack-protector" "2" "-mstack-protector-guard-reg=gs" "-mstack-protector-guard-symbol=__ref_stack_chk_guard" "-ftrivial-auto-var-init=pattern" "-mstack-alignment=8" "-fcf-protection=branch" "-mfunction-return=thunk-extern" "-mindirect-branch-cs-prefix" "-fno-builtin-wcslen" "-fno-signed-char" "-fwchar-type=short" "-fno-signed-wchar" "-fms-extensions" "-fms-anonymous-structs" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf" "-vectorize-loops" "-vectorize-slp" "-fexperimental-late-parse-attributes" "-mllvm" "-asan-mapping-offset=0xdffffc0000000000" "-mllvm" "-asan-instrumentation-with-call-threshold=10000" "-mllvm" "-asan-stack=0" "-mllvm" "-asan-globals=1" "-mllvm" "-asan-kernel-mem-intrinsic-prefix=1" "-mllvm" "-enable-fs-discriminator=true" "-mllvm" "-improved-fs-discriminator=true" "-fsanitize-coverage-type=1" "-fsanitize-coverage-type=3" "-fsanitize-coverage-trace-cmp" "-fsanitize-coverage-trace-pc" "-fsanitize-coverage-stack-depth" "-fsanitize-coverage-stack-depth-callback-min=100" "-fsanitize=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi" "-fsanitize-recover=kernel-address,array-bounds,bool,enum,shift-base,shift-exponent,kcfi" "-fsanitize-merge=array-bounds,bool,enum,shift-base,shift-exponent" "-fno-sanitize-memory-param-retval" "-fsanitize-cfi-icall-experimental-normalize-integers" "-fsanitize-kcfi-arity" "-fsanitize-address-use-after-scope" "-fno-sanitize-address-use-odr-indicator" "-faddrsig" "-fdwarf2-cfi-asm" "-x" "c" "cs-amp-lib-d6b847.c"

cs-amp-lib-d6b847.c cs-amp-lib-d6b847.sh

cc @bvanassche

Should be fixed by #180349

rishabhmadan19 pushed a commit to rishabhmadan19/llvm-project that referenced this pull request Feb 9, 2026
The typical case for literals is an array of mutexes, where we want to
distinguish `mutex[0]` from `mutex[1]` and so on. Currently they're
treated as the same expression, in fact all literals are treated as the
same expression.

The infrastructure for literals is already there, although it required
some changes, and some simplifications seemed opportune:
* The `ValueType` had fields for size and signedness. But only integer
have signedness, and the size is irrelevant if we don't want to emit
machine code. For the abstract semantics that we're interested in, only
the number matters.
* We remove the `BT_Void`: `void` literals don't exist in C++.
* We remove `BT_Float` and `BT_ValueRef`: floating-point numbers and
complex numbers are probably not used in lock expressions.
* We replace `BT_Pointer` with `BT_NullPointer`. There are no pointer
literals, only null pointer literals.

It seems to me that `ValueType` was intended to assign types to any node
in the TIL, but it is currently only used for literals. I was trying to
come up with reasons to assign types everywhere, but we're not trying to
type check anything, and we don't support overloading. The comparison of
expressions surely doesn't need it. But maybe this should be clarified
at some point. (Including the fact that it is called Typed Intermediate
Language.)

We turn `Literal` into a pure base class, as it seems to have been
intended, and only create `LiteralT` instances of the correct type.
Assertions on `as` ensure we're not mixing up types.

We print to `llvm::raw_ostream` instead of `std::ostream` because that's
required for `CharacterLiteral::print`.

Fixes llvm#58535.
melver added a commit that referenced this pull request Feb 10, 2026
The SExprBuilder was previously using StringLiteral::getString() to
extract the value of string literals. This method asserts that the
string is a narrow string (char width == 1):

```
clang/include/clang/AST/Expr.h:1872: StringRef clang::StringLiteral::getString() const: Assertion `(isUnevaluated() || getCharByteWidth() == 1) && "This function is used in places that assume strings use char"' failed.
[...]
 #9 0x0000556247fcfe3e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*)
[...]
```

This fails when using wide string literals as in the new test case
added.

Switch to using StringLiteral::getBytes(), which provides the raw byte
representation of the string. This is sufficient to compare expressions
for identity.

Fixes: #148551
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 10, 2026
…s (#180349)

The SExprBuilder was previously using StringLiteral::getString() to
extract the value of string literals. This method asserts that the
string is a narrow string (char width == 1):

```
clang/include/clang/AST/Expr.h:1872: StringRef clang::StringLiteral::getString() const: Assertion `(isUnevaluated() || getCharByteWidth() == 1) && "This function is used in places that assume strings use char"' failed.
[...]
 #9 0x0000556247fcfe3e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*)
[...]
```

This fails when using wide string literals as in the new test case
added.

Switch to using StringLiteral::getBytes(), which provides the raw byte
representation of the string. This is sufficient to compare expressions
for identity.

Fixes: llvm/llvm-project#148551
manasij7479 pushed a commit to manasij7479/llvm-project that referenced this pull request Feb 18, 2026
)

The SExprBuilder was previously using StringLiteral::getString() to
extract the value of string literals. This method asserts that the
string is a narrow string (char width == 1):

```
clang/include/clang/AST/Expr.h:1872: StringRef clang::StringLiteral::getString() const: Assertion `(isUnevaluated() || getCharByteWidth() == 1) && "This function is used in places that assume strings use char"' failed.
[...]
 llvm#9 0x0000556247fcfe3e clang::threadSafety::SExprBuilder::translate(clang::Stmt const*, clang::threadSafety::SExprBuilder::CallingContext*)
[...]
```

This fails when using wide string literals as in the new test case
added.

Switch to using StringLiteral::getBytes(), which provides the raw byte
representation of the string. This is sufficient to compare expressions
for identity.

Fixes: llvm#148551
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:analysis clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive in -Wthread-safety-analysis when using mutexes in collections

3 participants