Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][dataflow] Add reverse null checker #1

Closed
wants to merge 1 commit into from
Closed

Conversation

Discookie
Copy link
Owner

@Discookie Discookie commented Oct 18, 2023

TITLE: [clang][dataflow] Add null-check after dereference checker

This checker implements a null-pointer analysis checker using the data-flow framework. In particular, the checker finds cases where the pointer's nullability is checked after it has been dereferenced:

int f(int *ptr) {
  *ptr += 20; // note: one of the locations where the pointer's value cannot be null
  
  if (ptr) {
    *ptr += 42; // warning: pointer value is checked even though it cannot be null at this point
    return *ptr;
  }
  return 0;

It currently recognizes the following operations:

  • Dereference and arrow operators
  • Pointer-to-boolean cast
  • Assigning &obj, nullptr and other values

Notable limitations:

The checker only supports C++ due to the limitations of the framework (llvm#65301).

Function calls taking a reference of a pointer are not handled yet.
void external(int *&ref);

The note tag is only displayed at one location, and not all operations are supported or displayed.

More examples and limitations in the checker docs.


Results on open-source projects:
https://codechecker-demo.eastus.cloudapp.azure.com/Default/runs?run=Discookie_reverse-null

The reference-of-ptr limitation leads to a class of false positives across the tested projects (example).

But the checker also found quite a few true positives, on LLVM: 1 2 3 and a couple more, listed here.


This checker corresponds to the the CERT rule EXP34-C, Do not dereference null pointers, example 3.

@github-actions
Copy link

github-actions bot commented Oct 18, 2023

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 35d771fd4ffd300e527da87b8329bd120b220a83 086c092dedf063d327cc118aac56f6d2345208eb -- clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h clang-tools-extra/test/clang-tidy/checkers/bugprone/reverse-null.cpp clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
View the diff from clang-format here.
diff --git a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
index 253bfb130..2fdf8b497 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.cpp
@@ -32,7 +32,6 @@ using ast_matchers::MatchFinder;
 using dataflow::ReverseNullDiagnoser;
 using dataflow::ReverseNullModel;
 
-
 static constexpr llvm::StringLiteral FuncID("fun");
 
 static std::optional<ReverseNullDiagnoser::ResultType>
@@ -61,19 +60,21 @@ analyzeFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx) {
       std::optional<DataflowAnalysisState<ReverseNullModel::Lattice>>>>
       BlockToOutputState = dataflow::runDataflowAnalysis(
           *Context, Analysis, Env,
-          [&ASTCtx, &Diagnoser, &Diagnostics](
-              const CFGElement &Elt,
-              const DataflowAnalysisState<ReverseNullModel::Lattice>
-                  &State) mutable {
+          [&ASTCtx, &Diagnoser,
+           &Diagnostics](const CFGElement &Elt,
+                         const DataflowAnalysisState<ReverseNullModel::Lattice>
+                             &State) mutable {
             auto EltDiagnostics = Diagnoser.diagnose(ASTCtx, &Elt, State.Env);
-            llvm::move(EltDiagnostics.first, std::back_inserter(Diagnostics.first));
-            llvm::move(EltDiagnostics.second, std::back_inserter(Diagnostics.second));
+            llvm::move(EltDiagnostics.first,
+                       std::back_inserter(Diagnostics.first));
+            llvm::move(EltDiagnostics.second,
+                       std::back_inserter(Diagnostics.second));
           });
 
   // FIXME: Port this over into UncheckedOptionalAccessCheck
   if (llvm::Error E = BlockToOutputState.takeError()) {
-    llvm::dbgs() << "Dataflow analysis failed: "
-        << llvm::toString(std::move(E)) << ".\n";
+    llvm::dbgs() << "Dataflow analysis failed: " << llvm::toString(std::move(E))
+                 << ".\n";
     return std::nullopt;
   }
 
@@ -96,8 +97,7 @@ void ReverseNullCheck::registerMatchers(MatchFinder *Finder) {
       this);
 }
 
-void ReverseNullCheck::check(
-    const MatchFinder::MatchResult &Result) {
+void ReverseNullCheck::check(const MatchFinder::MatchResult &Result) {
   // llvm::errs() << "Starting result parse\n";
   if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred())
     return;
@@ -114,7 +114,8 @@ void ReverseNullCheck::check(
       diag(Loc, "pointer value is checked despite dereferencing it earlier");
 
     for (const SourceLocation &Loc : Errors->first)
-      diag(Loc, "pointer value is checked but it can only be null at this point");
+      diag(Loc,
+           "pointer value is checked but it can only be null at this point");
   }
 }
 
diff --git a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
index 2ab182c9a..4295bae75 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/ReverseNullCheck.h
@@ -20,7 +20,7 @@ class ReverseNullCheck : public ClangTidyCheck {
 public:
   ReverseNullCheck(StringRef Name, ClangTidyContext *Context)
       : ClangTidyCheck(Name, Context) {}
-  
+
   bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
     return LangOpts.CPlusPlus;
   }
@@ -29,7 +29,7 @@ public:
 };
 
 } // namespace bugprone
-}
-}
+} // namespace tidy
+} // namespace clang
 
 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REVERSENULLCHECK_H
diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
index 9ee8a0d28..683cdfcfb 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Models/ReverseNullModel.h
@@ -29,7 +29,6 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 
-
 namespace clang {
 namespace dataflow {
 
@@ -39,13 +38,13 @@ namespace dataflow {
 // invalidate the analysis. It also could be optimized to drop out-of-scope
 // variables from the map.
 class ReverseNullModel
-    : public DataflowAnalysis<ReverseNullModel,
-                              NoopLattice> {
+    : public DataflowAnalysis<ReverseNullModel, NoopLattice> {
 public:
   struct TransferArgs {
     bool Branch;
     Environment &Env;
   };
+
 private:
   CFGMatchSwitch<Environment> TransferMatchSwitch;
   ASTMatchSwitch<Stmt, TransferArgs> BranchTransferMatchSwitch;
@@ -53,30 +52,27 @@ private:
 public:
   explicit ReverseNullModel(ASTContext &Context);
 
-  static NoopLattice initialElement() {
-    return {};
-  }
+  static NoopLattice initialElement() { return {}; }
 
   static ast_matchers::StatementMatcher ptrValueMatcher();
 
   // Used to initialize the storage locations of function arguments.
   // Required to merge these values within the environment.
-  void initializeFunctionParameters(const ControlFlowContext &CFCtx, Environment &Env);
+  void initializeFunctionParameters(const ControlFlowContext &CFCtx,
+                                    Environment &Env);
 
-  void transfer(const CFGElement &E, NoopLattice &State,
-                Environment &Env);
+  void transfer(const CFGElement &E, NoopLattice &State, Environment &Env);
 
   void transferBranch(bool Branch, const Stmt *E, NoopLattice &State,
-                Environment &Env);
+                      Environment &Env);
 
-  bool merge(QualType Type,
-              const Value &Val1, const Environment &Env1,
-              const Value &Val2, const Environment &Env2,
-              Value &MergedVal, Environment &MergedEnv) override;
+  bool merge(QualType Type, const Value &Val1, const Environment &Env1,
+             const Value &Val2, const Environment &Env2, Value &MergedVal,
+             Environment &MergedEnv) override;
 
-  ComparisonResult compare(QualType Type,
-                           const Value &Val1, const Environment &Env1,
-                           const Value &Val2, const Environment &Env2) override;
+  ComparisonResult compare(QualType Type, const Value &Val1,
+                           const Environment &Env1, const Value &Val2,
+                           const Environment &Env2) override;
 
   Value *widen(QualType Type, Value &Prev, const Environment &PrevEnv,
                Value &Current, Environment &CurrentEnv) override;
@@ -85,17 +81,17 @@ public:
 class ReverseNullDiagnoser {
 public:
   ReverseNullDiagnoser();
-  using ResultType = std::pair<std::vector<SourceLocation>, std::vector<SourceLocation>>;
+  using ResultType =
+      std::pair<std::vector<SourceLocation>, std::vector<SourceLocation>>;
 
   ResultType diagnose(ASTContext &Ctx, const CFGElement *Elt,
-                                       const Environment &Env);
+                      const Environment &Env);
 
 private:
-  CFGMatchSwitch<const Environment, ResultType>
-      DiagnoseMatchSwitch;
+  CFGMatchSwitch<const Environment, ResultType> DiagnoseMatchSwitch;
 };
 
-}
-}
+} // namespace dataflow
+} // namespace clang
 
 #endif // CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_REVERSENULLMODEL_H
diff --git a/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
index 3f0a5f31e..e2e9dbe54 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/ReverseNullModel.cpp
@@ -37,24 +37,19 @@ constexpr char kVar[] = "var"; // FIXME: Make into prvalue, rvalue
 constexpr char kIsNonnull[] = "is-nonnull";
 constexpr char kIsNull[] = "is-null";
 
-enum class SatisfiabilityResult {
-  Nullptr,
-  Top,
-  True,
-  False,
-  Unknown
-};
+enum class SatisfiabilityResult { Nullptr, Top, True, False, Unknown };
 
 using SR = SatisfiabilityResult;
 
-auto ptrToVar(llvm::StringRef VarName = kVar) { 
-  return traverse(TK_IgnoreUnlessSpelledInSource, 
+auto ptrToVar(llvm::StringRef VarName = kVar) {
+  return traverse(TK_IgnoreUnlessSpelledInSource,
                   declRefExpr(hasType(isAnyPointer())).bind(VarName));
 }
-auto namedPtrToVar(llvm::StringRef name) { 
-  return functionDecl(hasDescendant(declRefExpr(allOf(hasType(isAnyPointer()),
-                     hasDeclaration(namedDecl(hasName(name))))).bind(kVar)
-  )); 
+auto namedPtrToVar(llvm::StringRef name) {
+  return functionDecl(
+      hasDescendant(declRefExpr(allOf(hasType(isAnyPointer()),
+                                      hasDeclaration(namedDecl(hasName(name)))))
+                        .bind(kVar)));
 }
 
 auto derefMatcher() {
@@ -72,56 +67,56 @@ auto arrowMatcher() {
 // FIXME: Deliberately simple - a proper check would only match direct ptr casts
 auto castExprMatcher() {
   return castExpr(hasCastKind(CK_PointerToBoolean),
-                  hasSourceExpression(
-                    ptrToVar()))
+                  hasSourceExpression(ptrToVar()))
       .bind(kCond);
 }
 
-auto nullptrMatcher() {
-  return expr(nullPointerConstant()).bind(kVar);
-}
+auto nullptrMatcher() { return expr(nullPointerConstant()).bind(kVar); }
 
 auto addressofMatcher() {
   return unaryOperator(hasOperatorName("&")).bind(kVar);
 }
 
 auto functionCallMatcher() {
-  return callExpr(hasDeclaration(functionDecl(returns(isAnyPointer())))).bind(kVar);
+  return callExpr(hasDeclaration(functionDecl(returns(isAnyPointer()))))
+      .bind(kVar);
 }
 
-auto anyPointerMatcher() {
-  return expr(hasType(isAnyPointer())).bind(kVar);
-}
+auto anyPointerMatcher() { return expr(hasType(isAnyPointer())).bind(kVar); }
 
 // Only computes simple comparisons against boolean True and False values.
 // Faster, but produces many Unknown values.
-SatisfiabilityResult shallowComputeSatisfiability(BoolValue *Val, const Environment &Env) {
+SatisfiabilityResult shallowComputeSatisfiability(BoolValue *Val,
+                                                  const Environment &Env) {
   if (!Val)
     return SR::Nullptr;
-  
+
   if (isa<TopBoolValue>(Val))
     return SR::Top;
 
   if (Val == &Env.getBoolLiteralValue(true))
     return SR::True;
-  
+
   if (Val == &Env.getBoolLiteralValue(false))
     return SR::False;
 
   return SR::Unknown;
 }
 
-// Computes satisfiability by using the flow condition. Slower, but more precise.
-SatisfiabilityResult computeSatisfiability(BoolValue *Val, const Environment &Env) {
+// Computes satisfiability by using the flow condition. Slower, but more
+// precise.
+SatisfiabilityResult computeSatisfiability(BoolValue *Val,
+                                           const Environment &Env) {
   // Early return with the fast compute values.
-  if (SatisfiabilityResult ShallowResult = shallowComputeSatisfiability(Val, Env);
+  if (SatisfiabilityResult ShallowResult =
+          shallowComputeSatisfiability(Val, Env);
       ShallowResult != SR::Unknown) {
     return ShallowResult;
   }
 
   if (Env.flowConditionImplies(Val->formula()))
     return SR::True;
-  
+
   if (Env.flowConditionImplies(Env.arena().makeNot(Val->formula())))
     return SR::False;
 
@@ -134,14 +129,14 @@ inline BoolValue &getVal(llvm::StringRef Name, Value &RootValue) {
 
 void initializeRootValue(Value &RootValue, Environment &Env) {
 
-    Arena &A = Env.arena();
+  Arena &A = Env.arena();
 
-    BoolValue &IsNull = A.makeAtomValue();
-    BoolValue &IsNonnull = A.makeAtomValue();
-    RootValue.setProperty(kIsNull, IsNull);
-    RootValue.setProperty(kIsNonnull, IsNonnull);
+  BoolValue &IsNull = A.makeAtomValue();
+  BoolValue &IsNonnull = A.makeAtomValue();
+  RootValue.setProperty(kIsNull, IsNull);
+  RootValue.setProperty(kIsNonnull, IsNonnull);
 
-    Env.addToFlowCondition(A.makeOr(IsNull.formula(), IsNonnull.formula()));
+  Env.addToFlowCondition(A.makeOr(IsNull.formula(), IsNonnull.formula()));
 }
 
 void setLValue(const Expr &E, Value &Val, Environment &Env) {
@@ -165,40 +160,41 @@ void setUnknownValue(const Expr &E, Value &Val, Environment &Env) {
 }
 
 Value *getValue(const Expr &Var, Environment &Env) {
-    if (auto *EnvVal = Env.getValue(Var)) {
-        // FIXME: Hack
-        if (!EnvVal->getProperty(kIsNull) && !EnvVal->getProperty(kIsNonnull)) {
-            initializeRootValue(*EnvVal, Env);
-        }
-
-        return EnvVal;
+  if (auto *EnvVal = Env.getValue(Var)) {
+    // FIXME: Hack
+    if (!EnvVal->getProperty(kIsNull) && !EnvVal->getProperty(kIsNonnull)) {
+      initializeRootValue(*EnvVal, Env);
     }
 
-    auto *RootValue = Env.createValue(Var.getType());
+    return EnvVal;
+  }
 
-    if (!RootValue) 
-        return nullptr;
+  auto *RootValue = Env.createValue(Var.getType());
 
-    initializeRootValue(*RootValue, Env);
+  if (!RootValue)
+    return nullptr;
+
+  initializeRootValue(*RootValue, Env);
 
-    setLValue(Var, *RootValue, Env);
+  setLValue(Var, *RootValue, Env);
 
-    return RootValue;
+  return RootValue;
 }
 
 void matchDereferenceExpr(const Stmt *stmt,
                           const MatchFinder::MatchResult &Result,
                           Environment &Env) {
-    const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
-    assert(Var != nullptr);
+  const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+  assert(Var != nullptr);
 
-    // FIXME: Optimize
-    auto *RootValue = getValue(*Var, Env);
-    if (!RootValue) {
-       return;
-    }
+  // FIXME: Optimize
+  auto *RootValue = getValue(*Var, Env);
+  if (!RootValue) {
+    return;
+  }
 
-    Env.addToFlowCondition(Env.arena().makeNot(getVal(kIsNull, *RootValue).formula()));
+  Env.addToFlowCondition(
+      Env.arena().makeNot(getVal(kIsNull, *RootValue).formula()));
 }
 
 void matchCastExpr(const CastExpr *cond, const MatchFinder::MatchResult &Result,
@@ -211,109 +207,117 @@ void matchCastExpr(const CastExpr *cond, const MatchFinder::MatchResult &Result,
   auto *RootValue = getValue(*Var, Env);
   if (!RootValue) {
     return;
-    }
+  }
 
-    auto *NewRootValue = Env.createValue(Var->getType());
-    if (!NewRootValue) 
-        return;
+  auto *NewRootValue = Env.createValue(Var->getType());
+  if (!NewRootValue)
+    return;
 
-    setLValue(*Var, *NewRootValue, Env);
+  setLValue(*Var, *NewRootValue, Env);
 
-    Arena &A = Env.arena();
+  Arena &A = Env.arena();
 
-    if (IsNonnullBranch) {
-        BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
-        BoolValue &IsNull = getVal(kIsNull, *RootValue);
+  if (IsNonnullBranch) {
+    BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
+    BoolValue &IsNull = getVal(kIsNull, *RootValue);
 
-        Env.addToFlowCondition(A.makeNot(IsNull.formula()));
-        NewRootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
+    Env.addToFlowCondition(A.makeNot(IsNull.formula()));
+    NewRootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
 
-        NewRootValue->setProperty(kIsNonnull, IsNonnull);
-    } else {
-        BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
-        BoolValue &IsNull = getVal(kIsNull, *RootValue);
+    NewRootValue->setProperty(kIsNonnull, IsNonnull);
+  } else {
+    BoolValue &IsNonnull = getVal(kIsNonnull, *RootValue);
+    BoolValue &IsNull = getVal(kIsNull, *RootValue);
 
-        NewRootValue->setProperty(kIsNull, IsNull);
+    NewRootValue->setProperty(kIsNull, IsNull);
 
-        Env.addToFlowCondition(A.makeNot(IsNonnull.formula()));
-        NewRootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
-    }
+    Env.addToFlowCondition(A.makeNot(IsNonnull.formula()));
+    NewRootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
+  }
 }
 
-void matchNullptrExpr(const Expr *expr, const MatchFinder::MatchResult &Result, Environment &Env) {
-    const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
-    assert(PrVar != nullptr);
+void matchNullptrExpr(const Expr *expr, const MatchFinder::MatchResult &Result,
+                      Environment &Env) {
+  const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
+  assert(PrVar != nullptr);
 
-    if (Env.getValue(*PrVar))
-      return;
+  if (Env.getValue(*PrVar))
+    return;
 
-    // FIXME: Nullptr does not create a value
-    auto *RootValue = Env.createValue(PrVar->getType());
-    if (!RootValue) {
-       return;
-    }
+  // FIXME: Nullptr does not create a value
+  auto *RootValue = Env.createValue(PrVar->getType());
+  if (!RootValue) {
+    return;
+  }
 
-    RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(true));
-    RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
-    Env.setValue(*PrVar, *RootValue);
+  RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(true));
+  RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(false));
+  Env.setValue(*PrVar, *RootValue);
 }
 
-void matchAddressofExpr(const Expr *expr, const MatchFinder::MatchResult &Result, Environment &Env) {
-    const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
-    assert(PrVar != nullptr);
+void matchAddressofExpr(const Expr *expr,
+                        const MatchFinder::MatchResult &Result,
+                        Environment &Env) {
+  const auto *PrVar = Result.Nodes.getNodeAs<Expr>(kVar);
+  assert(PrVar != nullptr);
 
-    auto *RootValue = Env.createValue(PrVar->getType());
-    if (!RootValue) {
-       return;
-    }
+  auto *RootValue = Env.createValue(PrVar->getType());
+  if (!RootValue) {
+    return;
+  }
 
-    RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
-    RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(true));
-    Env.setValue(*PrVar, *RootValue);
+  RootValue->setProperty(kIsNull, Env.getBoolLiteralValue(false));
+  RootValue->setProperty(kIsNonnull, Env.getBoolLiteralValue(true));
+  Env.setValue(*PrVar, *RootValue);
 }
 
-void matchFunctionCallExpr(const Expr *fncall, const MatchFinder::MatchResult &Result, Environment &Env) {
-    // This is not necessarily a prvalue, since operators such as prefix ++ are lvalues.
-    const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
-    assert(Var != nullptr);
+void matchFunctionCallExpr(const Expr *fncall,
+                           const MatchFinder::MatchResult &Result,
+                           Environment &Env) {
+  // This is not necessarily a prvalue, since operators such as prefix ++ are
+  // lvalues.
+  const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+  assert(Var != nullptr);
 
-    // Initialize to (Unknown, Unknown)
-    if (Env.getValue(*Var))
-      return;
+  // Initialize to (Unknown, Unknown)
+  if (Env.getValue(*Var))
+    return;
 
-    auto *RootValue = Env.createValue(Var->getType());
-    if (!RootValue)
-       return;
-    
-    initializeRootValue(*RootValue, Env);
-    setUnknownValue(*Var, *RootValue, Env);
+  auto *RootValue = Env.createValue(Var->getType());
+  if (!RootValue)
+    return;
+
+  initializeRootValue(*RootValue, Env);
+  setUnknownValue(*Var, *RootValue, Env);
 }
 
 ReverseNullDiagnoser::ResultType
 diagnoseCastExpr(const CastExpr *Stmt, const MatchFinder::MatchResult &Result,
-               const Environment &Env) {
-    const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
-    assert(Var != nullptr);
-
-    Arena &A = Env.arena();
-
-    if (auto *RootValue = Env.getValue(*Var)) {
-        // FIXME: Hack
-        if (RootValue->getProperty(kIsNull) && RootValue->getProperty(kIsNonnull)) {
-            bool IsNull = !Env.flowConditionImplies(A.makeNot(getVal(kIsNull, *RootValue).formula()));
-            bool IsNonnull = !Env.flowConditionImplies(A.makeNot(getVal(kIsNonnull, *RootValue).formula()));
-
-            if (!IsNull && IsNonnull) {
-                return {{}, {Var->getBeginLoc()}};
-            }
-
-            if (IsNull && !IsNonnull) {
-                return {{Var->getBeginLoc()}, {}};
-            }
-        }
+                 const Environment &Env) {
+  const auto *Var = Result.Nodes.getNodeAs<Expr>(kVar);
+  assert(Var != nullptr);
+
+  Arena &A = Env.arena();
+
+  if (auto *RootValue = Env.getValue(*Var)) {
+    // FIXME: Hack
+    if (RootValue->getProperty(kIsNull) && RootValue->getProperty(kIsNonnull)) {
+      bool IsNull = !Env.flowConditionImplies(
+          A.makeNot(getVal(kIsNull, *RootValue).formula()));
+      bool IsNonnull = !Env.flowConditionImplies(
+          A.makeNot(getVal(kIsNonnull, *RootValue).formula()));
+
+      if (!IsNull && IsNonnull) {
+        return {{}, {Var->getBeginLoc()}};
+      }
+
+      if (IsNull && !IsNonnull) {
+        return {{Var->getBeginLoc()}, {}};
+      }
     }
+  }
 
-    return {};
+  return {};
 }
 
 auto buildTransferMatchSwitch() {
@@ -334,7 +338,8 @@ auto buildBranchTransferMatchSwitch() {
 }
 
 auto buildDiagnoseMatchSwitch() {
-  return CFGMatchSwitchBuilder<const Environment, ReverseNullDiagnoser::ResultType>()
+  return CFGMatchSwitchBuilder<const Environment,
+                               ReverseNullDiagnoser::ResultType>()
       .CaseOfCFGStmt<CastExpr>(castExprMatcher(), diagnoseCastExpr)
       .Build();
 }
@@ -344,14 +349,14 @@ auto buildDiagnoseMatchSwitch() {
 ReverseNullModel::ReverseNullModel(ASTContext &Context)
     : DataflowAnalysis<ReverseNullModel, NoopLattice>(Context),
       TransferMatchSwitch(buildTransferMatchSwitch()),
-      BranchTransferMatchSwitch(buildBranchTransferMatchSwitch()) {
-}
+      BranchTransferMatchSwitch(buildBranchTransferMatchSwitch()) {}
 
 ast_matchers::StatementMatcher ReverseNullModel::ptrValueMatcher() {
   return ptrToVar();
 }
 
-void ReverseNullModel::initializeFunctionParameters(const ControlFlowContext &CFCtx, Environment &Env) {
+void ReverseNullModel::initializeFunctionParameters(
+    const ControlFlowContext &CFCtx, Environment &Env) {
   const FunctionDecl &FD = cast<FunctionDecl>(CFCtx.getDecl());
 
   for (const auto *Param : FD.parameters()) {
@@ -381,12 +386,12 @@ void ReverseNullModel::transfer(const CFGElement &E, NoopLattice &State,
   TransferMatchSwitch(E, getASTContext(), Env);
 }
 
-void ReverseNullModel::transferBranch(bool Branch, const Stmt *E, NoopLattice &State,
-                                      Environment &Env) {
+void ReverseNullModel::transferBranch(bool Branch, const Stmt *E,
+                                      NoopLattice &State, Environment &Env) {
   if (!E)
     return;
 
-  TransferArgs Args = { Branch, Env };
+  TransferArgs Args = {Branch, Env};
   BranchTransferMatchSwitch(*E, getASTContext(), Args);
 }
 
@@ -395,310 +400,319 @@ bool ReverseNullModel::merge(QualType Type, const Value &Val1,
                              const Environment &Env1, const Value &Val2,
                              const Environment &Env2, Value &MergedVal,
                              Environment &MergedEnv) {
-    if (!Type->isAnyPointerType())
-      return false; // FIXME: Maybe requires true?
-
-    // Alternative attempt: is any branch is true ie. can be a pointer, merging it is also a pointer
-    const auto FastMergeToTrue = [&](llvm::StringRef Name) -> BoolValue & {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+  if (!Type->isAnyPointerType())
+    return false; // FIXME: Maybe requires true?
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+  // Alternative attempt: is any branch is true ie. can be a pointer, merging it
+  // is also a pointer
+  const auto FastMergeToTrue = [&](llvm::StringRef Name) -> BoolValue & {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top) 
-        return MergedEnv.makeTopBoolValue();
-      else if (lhsResult == SR::True || rhsResult == SR::True)
-        return MergedEnv.getBoolLiteralValue(true);
-      
-      if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
-        return MergedEnv.makeAtomicBoolValue();
-      else if (lhsResult == SR::False && rhsResult == SR::False)
-        return MergedEnv.getBoolLiteralValue(false);
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
 
+    if (lhsResult == SR::Top || rhsResult == SR::Top)
       return MergedEnv.makeTopBoolValue();
-    };
-    
-    // Attempt 1: Merge different values to Top.
-    const auto FastMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+    else if (lhsResult == SR::True || rhsResult == SR::True)
+      return MergedEnv.getBoolLiteralValue(true);
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+    if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
+      return MergedEnv.makeAtomicBoolValue();
+    else if (lhsResult == SR::False && rhsResult == SR::False)
+      return MergedEnv.getBoolLiteralValue(false);
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top) 
-        return MergedEnv.makeTopBoolValue();
-      
-      if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
-        return MergedEnv.makeAtomicBoolValue();
+    return MergedEnv.makeTopBoolValue();
+  };
 
-      if (lhsResult == SR::True || rhsResult == SR::True)
-        return MergedEnv.getBoolLiteralValue(true);
-      else if (lhsResult == SR::False && rhsResult == SR::False)
-        return MergedEnv.getBoolLiteralValue(false);
+  // Attempt 1: Merge different values to Top.
+  const auto FastMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
 
+    if (lhsResult == SR::Top || rhsResult == SR::Top)
       return MergedEnv.makeTopBoolValue();
-    };
 
-    // Attempt 2: Slow evaluation, but only produces simple variables.
-    const auto MergeValues = [&](llvm::StringRef Name) -> BoolValue & {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+    if (lhsResult == SR::Nullptr && rhsResult == SR::Nullptr)
+      return MergedEnv.makeAtomicBoolValue();
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+    if (lhsResult == SR::True || rhsResult == SR::True)
+      return MergedEnv.getBoolLiteralValue(true);
+    else if (lhsResult == SR::False && rhsResult == SR::False)
+      return MergedEnv.getBoolLiteralValue(false);
 
-      // Handle special cases.
-      if (lhsResult == SR::Top || rhsResult == SR::Top) {
-        return MergedEnv.makeTopBoolValue();
-      } else if (lhsResult == rhsResult) {
-        switch (lhsResult) {
-          case SR::Nullptr:
-            return MergedEnv.makeAtomicBoolValue();
-          case SR::Top:
-            return *lhsVar;
-          case SR::True:
-            return MergedEnv.getBoolLiteralValue(true);
-          case SR::False:
-            return MergedEnv.getBoolLiteralValue(false);
-          case SR::Unknown:
-            if (MergedEnv.flowConditionImplies(MergedEnv.arena().makeEquals(lhsVar->formula(), rhsVar->formula())))
-              return *lhsVar;
-            
-            return MergedEnv.makeTopBoolValue();
-        }
-      }
+    return MergedEnv.makeTopBoolValue();
+  };
 
-      return MergedEnv.makeTopBoolValue();
-    };
+  // Attempt 2: Slow evaluation, but only produces simple variables.
+  const auto MergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
-    // Attempt 3: Merge to (L or R), a more complex variable
-    const auto SlowMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+    // Handle special cases.
+    if (lhsResult == SR::Top || rhsResult == SR::Top) {
+      return MergedEnv.makeTopBoolValue();
+    } else if (lhsResult == rhsResult) {
+      switch (lhsResult) {
+      case SR::Nullptr:
+        return MergedEnv.makeAtomicBoolValue();
+      case SR::Top:
+        return *lhsVar;
+      case SR::True:
+        return MergedEnv.getBoolLiteralValue(true);
+      case SR::False:
+        return MergedEnv.getBoolLiteralValue(false);
+      case SR::Unknown:
+        if (MergedEnv.flowConditionImplies(MergedEnv.arena().makeEquals(
+                lhsVar->formula(), rhsVar->formula())))
+          return *lhsVar;
 
-      // Handle special cases.
-      if (lhsResult == SR::Top || rhsResult == SR::Top) {
         return MergedEnv.makeTopBoolValue();
-      } else if (lhsResult == rhsResult) {
-        switch (lhsResult) {
-          case SR::Nullptr:
-            return MergedEnv.makeAtomicBoolValue();
-          case SR::Top:
-            return *lhsVar;
-          case SR::True:
-            return MergedEnv.getBoolLiteralValue(true);
-          case SR::False:
-            return MergedEnv.getBoolLiteralValue(false);
-          case SR::Unknown:
-            break;
-        }
       }
-      
-      const Formula *mergedLhsVar;
-      const Formula *mergedRhsVar;
+    }
+
+    return MergedEnv.makeTopBoolValue();
+  };
 
-      Arena &A = MergedEnv.arena();
-      const Formula *lhsToken = &A.makeAtomRef(Env1.getFlowConditionToken());
-      const Formula *rhsToken = &A.makeAtomRef(Env2.getFlowConditionToken());
+  // Attempt 3: Merge to (L or R), a more complex variable
+  const auto SlowMergeValues = [&](llvm::StringRef Name) -> BoolValue & {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
 
+    // Handle special cases.
+    if (lhsResult == SR::Top || rhsResult == SR::Top) {
+      return MergedEnv.makeTopBoolValue();
+    } else if (lhsResult == rhsResult) {
       switch (lhsResult) {
-        case SR::Nullptr:
-        case SR::Top: // would have returned earlier
-          mergedLhsVar = nullptr;
-          break;
-        case SR::True:
-          mergedLhsVar = lhsToken;
-          break;
-        case SR::False:
-          mergedLhsVar = &A.makeNot(*lhsToken);
-          break;
-        case SR::Unknown:
-          mergedLhsVar = &A.makeImplies(*lhsToken, lhsVar->formula());
-          break;
+      case SR::Nullptr:
+        return MergedEnv.makeAtomicBoolValue();
+      case SR::Top:
+        return *lhsVar;
+      case SR::True:
+        return MergedEnv.getBoolLiteralValue(true);
+      case SR::False:
+        return MergedEnv.getBoolLiteralValue(false);
+      case SR::Unknown:
+        break;
       }
+    }
 
-      switch (rhsResult) {
-        case SR::Nullptr:
-        case SR::Top:
-          mergedRhsVar = nullptr;
-          break;
-        case SR::True:
-          mergedRhsVar = rhsToken;
-          break;
-        case SR::False:
-          mergedRhsVar = &A.makeNot(*rhsToken);
-          break;
-        case SR::Unknown:
-          mergedRhsVar = &A.makeImplies(*rhsToken, rhsVar->formula());
-          break;
-      }
+    const Formula *mergedLhsVar;
+    const Formula *mergedRhsVar;
+
+    Arena &A = MergedEnv.arena();
+    const Formula *lhsToken = &A.makeAtomRef(Env1.getFlowConditionToken());
+    const Formula *rhsToken = &A.makeAtomRef(Env2.getFlowConditionToken());
+
+    switch (lhsResult) {
+    case SR::Nullptr:
+    case SR::Top: // would have returned earlier
+      mergedLhsVar = nullptr;
+      break;
+    case SR::True:
+      mergedLhsVar = lhsToken;
+      break;
+    case SR::False:
+      mergedLhsVar = &A.makeNot(*lhsToken);
+      break;
+    case SR::Unknown:
+      mergedLhsVar = &A.makeImplies(*lhsToken, lhsVar->formula());
+      break;
+    }
 
-      if (!lhsVar && !rhsVar)
-        return MergedEnv.makeAtomicBoolValue();
-      else if (!lhsVar)
-        return A.makeBoolValue(*mergedRhsVar);
-      else if (!rhsVar)
-        return A.makeBoolValue(*mergedLhsVar);
-      else
-        return A.makeBoolValue(A.makeOr(*mergedLhsVar, *mergedRhsVar));
-    };
+    switch (rhsResult) {
+    case SR::Nullptr:
+    case SR::Top:
+      mergedRhsVar = nullptr;
+      break;
+    case SR::True:
+      mergedRhsVar = rhsToken;
+      break;
+    case SR::False:
+      mergedRhsVar = &A.makeNot(*rhsToken);
+      break;
+    case SR::Unknown:
+      mergedRhsVar = &A.makeImplies(*rhsToken, rhsVar->formula());
+      break;
+    }
 
-    BoolValue &NonnullValue = FastMergeValues(kIsNonnull);
-    BoolValue &NullValue = FastMergeValues(kIsNull);
+    if (!lhsVar && !rhsVar)
+      return MergedEnv.makeAtomicBoolValue();
+    else if (!lhsVar)
+      return A.makeBoolValue(*mergedRhsVar);
+    else if (!rhsVar)
+      return A.makeBoolValue(*mergedLhsVar);
+    else
+      return A.makeBoolValue(A.makeOr(*mergedLhsVar, *mergedRhsVar));
+  };
 
-    MergedVal.setProperty(kIsNonnull, NonnullValue);
-    MergedVal.setProperty(kIsNull, NullValue);
+  BoolValue &NonnullValue = FastMergeValues(kIsNonnull);
+  BoolValue &NullValue = FastMergeValues(kIsNull);
 
-    MergedEnv.addToFlowCondition(MergedEnv.makeOr(NonnullValue, NullValue).formula());
+  MergedVal.setProperty(kIsNonnull, NonnullValue);
+  MergedVal.setProperty(kIsNull, NullValue);
 
-    return true;
+  MergedEnv.addToFlowCondition(
+      MergedEnv.makeOr(NonnullValue, NullValue).formula());
+
+  return true;
 }
 
-ComparisonResult ReverseNullModel::compare(QualType Type,
-        const Value &Val1, const Environment &Env1,
-        const Value &Val2, const Environment &Env2) {
+ComparisonResult ReverseNullModel::compare(QualType Type, const Value &Val1,
+                                           const Environment &Env1,
+                                           const Value &Val2,
+                                           const Environment &Env2) {
 
-    if (!Type->isAnyPointerType())
-      return ComparisonResult::Unknown;
+  if (!Type->isAnyPointerType())
+    return ComparisonResult::Unknown;
 
-    // Attempt 1: Different values by pointer compare Unknown, Top to Different, same to Same.
-    auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+  // Attempt 1: Different values by pointer compare Unknown, Top to Different,
+  // same to Same.
+  auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
-      SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
+    SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top)
-        return ComparisonResult::Same; // FIXME: Should this be different?
+    if (lhsResult == SR::Top || rhsResult == SR::Top)
+      return ComparisonResult::Same; // FIXME: Should this be different?
 
-      if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
-        return ComparisonResult::Unknown;
-      
-      if (lhsResult == rhsResult)
-        return ComparisonResult::Same;
+    if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+      return ComparisonResult::Unknown;
 
-      return ComparisonResult::Different;
-    };
+    if (lhsResult == rhsResult)
+      return ComparisonResult::Same;
 
-    // Attempt 2: Evaluate values, but different values compare to Unknown.
-    auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+    return ComparisonResult::Different;
+  };
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+  // Attempt 2: Evaluate values, but different values compare to Unknown.
+  auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top)
-        return ComparisonResult::Same; // FIXME: Should this be different?
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
 
-      if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
-        return ComparisonResult::Unknown;
-      
-      if (lhsResult == rhsResult)
-        return ComparisonResult::Same;
+    if (lhsResult == SR::Top || rhsResult == SR::Top)
+      return ComparisonResult::Same; // FIXME: Should this be different?
 
-      return ComparisonResult::Different;
-    };
+    if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+      return ComparisonResult::Unknown;
 
-    ComparisonResult NullComparison = FastCompareValues(kIsNull);
-    ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+    if (lhsResult == rhsResult)
+      return ComparisonResult::Same;
 
-    if (NullComparison == ComparisonResult::Different || NonnullComparison == ComparisonResult::Different)
-      return ComparisonResult::Different;
+    return ComparisonResult::Different;
+  };
 
-    if (NullComparison == ComparisonResult::Unknown || NonnullComparison == ComparisonResult::Unknown)
-      return ComparisonResult::Unknown;
+  ComparisonResult NullComparison = FastCompareValues(kIsNull);
+  ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
 
-    return ComparisonResult::Same;
-}
+  if (NullComparison == ComparisonResult::Different ||
+      NonnullComparison == ComparisonResult::Different)
+    return ComparisonResult::Different;
 
-// Different in that it replaces differing boolean values with Top.
-ComparisonResult compareAndReplace(QualType Type,
-        Value &Val1, const Environment &Env1,
-        Value &Val2, Environment &Env2) {
+  if (NullComparison == ComparisonResult::Unknown ||
+      NonnullComparison == ComparisonResult::Unknown)
+    return ComparisonResult::Unknown;
 
-    if (!Type->isAnyPointerType())
-      return ComparisonResult::Unknown;
+  return ComparisonResult::Same;
+}
 
-    // Attempt 1: Different values by pointer compare Unknown, Top to Different, same to Same.
-    auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+// Different in that it replaces differing boolean values with Top.
+ComparisonResult compareAndReplace(QualType Type, Value &Val1,
+                                   const Environment &Env1, Value &Val2,
+                                   Environment &Env2) {
 
-      SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
+  if (!Type->isAnyPointerType())
+    return ComparisonResult::Unknown;
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top) {
-        Val2.setProperty(Name, Env2.makeTopBoolValue());
-        return ComparisonResult::Same; // FIXME: Should this be different?
-      }
+  // Attempt 1: Different values by pointer compare Unknown, Top to Different,
+  // same to Same.
+  auto FastCompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
-      if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
-        return ComparisonResult::Unknown;
-      
-      if (lhsResult == rhsResult)
-        return ComparisonResult::Same;
+    SatisfiabilityResult lhsResult = shallowComputeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = shallowComputeSatisfiability(rhsVar, Env2);
 
+    if (lhsResult == SR::Top || rhsResult == SR::Top) {
       Val2.setProperty(Name, Env2.makeTopBoolValue());
-      return ComparisonResult::Different;
-    };
+      return ComparisonResult::Same; // FIXME: Should this be different?
+    }
 
-    // Attempt 2: Evaluate values, but different values compare to Unknown.
-    auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
-      BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
-      BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
+    if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+      return ComparisonResult::Unknown;
 
-      SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
-      SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+    if (lhsResult == rhsResult)
+      return ComparisonResult::Same;
 
-      if (lhsResult == SR::Top || rhsResult == SR::Top) {
-        Val2.setProperty(Name, Env2.makeTopBoolValue());
-        return ComparisonResult::Same; // FIXME: Should this be different?
-      }
+    Val2.setProperty(Name, Env2.makeTopBoolValue());
+    return ComparisonResult::Different;
+  };
 
-      if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
-        return ComparisonResult::Unknown;
-      
-      if (lhsResult == rhsResult)
-        return ComparisonResult::Same;
+  // Attempt 2: Evaluate values, but different values compare to Unknown.
+  auto CompareValues = [&](llvm::StringRef Name) -> ComparisonResult {
+    BoolValue *lhsVar = cast_or_null<BoolValue>(Val1.getProperty(Name));
+    BoolValue *rhsVar = cast_or_null<BoolValue>(Val2.getProperty(Name));
 
+    SatisfiabilityResult lhsResult = computeSatisfiability(lhsVar, Env1);
+    SatisfiabilityResult rhsResult = computeSatisfiability(rhsVar, Env2);
+
+    if (lhsResult == SR::Top || rhsResult == SR::Top) {
       Val2.setProperty(Name, Env2.makeTopBoolValue());
-      return ComparisonResult::Different;
-    };
+      return ComparisonResult::Same; // FIXME: Should this be different?
+    }
+
+    if (lhsResult == SR::Unknown || rhsResult == SR::Unknown)
+      return ComparisonResult::Unknown;
 
-    ComparisonResult NullComparison = FastCompareValues(kIsNull);
-    ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+    if (lhsResult == rhsResult)
+      return ComparisonResult::Same;
 
-    if (NullComparison == ComparisonResult::Different || NonnullComparison == ComparisonResult::Different)
-      return ComparisonResult::Different;
+    Val2.setProperty(Name, Env2.makeTopBoolValue());
+    return ComparisonResult::Different;
+  };
 
-    if (NullComparison == ComparisonResult::Unknown || NonnullComparison == ComparisonResult::Unknown)
-      return ComparisonResult::Unknown;
+  ComparisonResult NullComparison = FastCompareValues(kIsNull);
+  ComparisonResult NonnullComparison = FastCompareValues(kIsNonnull);
+
+  if (NullComparison == ComparisonResult::Different ||
+      NonnullComparison == ComparisonResult::Different)
+    return ComparisonResult::Different;
 
-    return ComparisonResult::Same;
+  if (NullComparison == ComparisonResult::Unknown ||
+      NonnullComparison == ComparisonResult::Unknown)
+    return ComparisonResult::Unknown;
+
+  return ComparisonResult::Same;
 }
 
-Value *ReverseNullModel::widen(QualType Type,
-                               Value &Prev, const Environment &PrevEnv,
-                               Value &Current, Environment &CurrentEnv) {
-    if (!Type->isAnyPointerType()) 
-      return nullptr;
+Value *ReverseNullModel::widen(QualType Type, Value &Prev,
+                               const Environment &PrevEnv, Value &Current,
+                               Environment &CurrentEnv) {
+  if (!Type->isAnyPointerType())
+    return nullptr;
 
-    switch (compareAndReplace(Type, Prev, PrevEnv, Current, CurrentEnv)) {
-      case ComparisonResult::Same:
-        return &Prev;
-      case ComparisonResult::Unknown:
-        return nullptr;
-      case ComparisonResult::Different:
-        return &Current; // FIXME: Replace with Tops in compareAndReplace
-      }
+  switch (compareAndReplace(Type, Prev, PrevEnv, Current, CurrentEnv)) {
+  case ComparisonResult::Same:
+    return &Prev;
+  case ComparisonResult::Unknown:
+    return nullptr;
+  case ComparisonResult::Different:
+    return &Current; // FIXME: Replace with Tops in compareAndReplace
+  }
 }
 
 ReverseNullDiagnoser::ReverseNullDiagnoser()
diff --git a/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
index 6e4148bf4..4d0b933bc 100644
--- a/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/ReverseNullModelTest.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-//  This file defines a test for pointer nullability, specifically focused on 
+//  This file defines a test for pointer nullability, specifically focused on
 //  finding invalid dereferences, and unnecessary null-checks. Only a limited
 //  set of operations are currently recognized.
 //
@@ -14,6 +14,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Analysis/FlowSensitive/Models/ReverseNullModel.h"
 #include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -28,7 +29,6 @@
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/MapLattice.h"
 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
-#include "clang/Analysis/FlowSensitive/Models/ReverseNullModel.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/Error.h"
@@ -61,7 +61,7 @@ constexpr char kBoolInvalid[] = "truefalse";
 constexpr char kBoolUnknown[] = "unknown";
 constexpr char kBoolNullptr[] = "is-nullptr";
 
-std::string debugBoolValue(BoolValue *value, const Environment &Env) { 
+std::string debugBoolValue(BoolValue *value, const Environment &Env) {
   if (value == nullptr) {
     return std::string(kBoolNullptr);
   } else {
@@ -77,46 +77,42 @@ std::string debugBoolValue(BoolValue *value, const Environment &Env) {
 }
 
 auto ptrToVar() { return declRefExpr(hasType(isAnyPointer())).bind(kVar); }
-auto namedPtrToVar(llvm::StringRef name) { 
+auto namedPtrToVar(llvm::StringRef name) {
   return declRefExpr(allOf(hasType(isAnyPointer()),
-                     hasDeclaration(namedDecl(hasName(name)))
-  )).bind(kVar); 
+                           hasDeclaration(namedDecl(hasName(name)))))
+      .bind(kVar);
 }
 
 auto nameToVar(llvm::StringRef name) {
-  return declRefExpr(hasType(isAnyPointer()), hasDeclaration(
-    namedDecl(hasName(name))
-  )).bind(kVar);
+  return declRefExpr(hasType(isAnyPointer()),
+                     hasDeclaration(namedDecl(hasName(name))))
+      .bind(kVar);
 }
 
 auto voidCastMatcher() {
-  return stmt(hasDescendant(castExpr(hasCastKind(CK_ToVoid), 
-    hasSourceExpression(ptrToVar())
-  )));
+  return stmt(hasDescendant(
+      castExpr(hasCastKind(CK_ToVoid), hasSourceExpression(ptrToVar()))));
 }
 
 auto derefMatcher() {
-  return traverse(TK_IgnoreUnlessSpelledInSource,
-    unaryOperator(
-      hasOperatorName("*"),
-      hasUnaryOperand(ptrToVar())
-    )
-  );
+  return traverse(
+      TK_IgnoreUnlessSpelledInSource,
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(ptrToVar())));
 }
 
 // FIXME: Deliberately simple - a proper check would only match direct ptr casts
 auto castExprMatcher() {
-  return castExpr(hasCastKind(CK_PointerToBoolean), 
-      hasSourceExpression(ptrToVar())).bind(kCond)
-  ;
+  return castExpr(hasCastKind(CK_PointerToBoolean),
+                  hasSourceExpression(ptrToVar()))
+      .bind(kCond);
 }
 
 using ::clang::dataflow::test::AnalysisInputs;
 using ::clang::dataflow::test::AnalysisOutputs;
 using ::clang::dataflow::test::checkDataflow;
 using ::llvm::IsStringMapEntry;
-using ::testing::Pair;
 using ::testing::Args;
+using ::testing::Pair;
 using ::testing::UnorderedElementsAre;
 
 MATCHER_P2(HasNullabilityState, null, nonnull, "") {
@@ -128,14 +124,14 @@ MATCHER_P2(HasNullabilityState, null, nonnull, "") {
              *arg.second) == null;
 }
 
-MATCHER_P3(HoldsVariable, name, output, checks, 
-          ((negation ? "doesn't hold" : "holds") +
+MATCHER_P3(HoldsVariable, name, output, checks,
+           ((negation ? "doesn't hold" : "holds") +
             llvm::StringRef(" a variable in its environment that ") +
-            ::testing::DescribeMatcher<std::pair<Value *, Environment *>>(checks, negation)
-          ).str()) {
+            ::testing::DescribeMatcher<std::pair<Value *, Environment *>>(
+                checks, negation))
+               .str()) {
   auto MatchResults = match(functionDecl(hasDescendant(nameToVar(name))),
-                            *output.Target,
-                            output.ASTCtx);
+                            *output.Target, output.ASTCtx);
   // FIXME: Use gtest functions
   assert(!MatchResults.empty());
 
@@ -143,12 +139,13 @@ MATCHER_P3(HoldsVariable, name, output, checks,
   assert(pointerExpr != nullptr);
 
   const auto *ExprValue = arg.Env.getValue(*pointerExpr);
-  
+
   if (ExprValue == nullptr) {
     return false;
   }
 
-  return ExplainMatchResult(checks, std::pair { ExprValue, &arg.Env }, result_listener);
+  return ExplainMatchResult(checks, std::pair{ExprValue, &arg.Env},
+                            result_listener);
 }
 
 template <typename MatcherFactory>
@@ -166,15 +163,17 @@ void ExpectDataflowResult(llvm::StringRef Code, MatcherFactory Expectations) {
               })
               .withSetupTest([&InitEnv](AnalysisOutputs &AO) {
                 assert(InitEnv && "SetupTest ran at an unexpected time");
-                ReverseNullModel &Analysis = static_cast<ReverseNullModel&>(AO.Analysis);
+                ReverseNullModel &Analysis =
+                    static_cast<ReverseNullModel &>(AO.Analysis);
                 Analysis.initializeFunctionParameters(AO.CFCtx, *InitEnv);
                 return llvm::Error::success();
               })
               .withASTBuildArgs({"-fsyntax-only", "-std=c++17"}),
           /*VerifyResults=*/
-          [&Expectations](const llvm::StringMap<DataflowAnalysisState<
-                              ReverseNullModel::Lattice>> &Results,
-                          const AnalysisOutputs &Output) {
+          [&Expectations](
+              const llvm::StringMap<
+                  DataflowAnalysisState<ReverseNullModel::Lattice>> &Results,
+              const AnalysisOutputs &Output) {
             EXPECT_THAT(Results, Expectations(Output));
           }),
       llvm::Succeeded());
@@ -194,10 +193,12 @@ TEST(ReverseNullTest, DereferenceTypes) {
   )";
   ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
     return UnorderedElementsAre(
-        IsStringMapEntry("p", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))),
-        IsStringMapEntry("q", HoldsVariable("q", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))));
+        IsStringMapEntry(
+            "p", HoldsVariable("p", Output,
+                               HasNullabilityState(kBoolFalse, kBoolTrue))),
+        IsStringMapEntry(
+            "q", HoldsVariable("q", Output,
+                               HasNullabilityState(kBoolFalse, kBoolTrue))));
   });
 }
 
@@ -216,9 +217,11 @@ TEST(ReverseNullTest, ConditionalTypes) {
   ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
     return UnorderedElementsAre(
         IsStringMapEntry("p_true", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))),
+                                                 HasNullabilityState(
+                                                     kBoolFalse, kBoolTrue))),
         IsStringMapEntry("p_false", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolTrue, kBoolFalse))));
+                                                  HasNullabilityState(
+                                                      kBoolTrue, kBoolFalse))));
   });
 }
 
@@ -248,17 +251,27 @@ TEST(ReverseNullTest, UnrelatedCondition) {
   ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
     return UnorderedElementsAre(
         IsStringMapEntry("p_b_true", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))),
-        IsStringMapEntry("p_b_false", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolUnknown, kBoolUnknown))),
-        IsStringMapEntry("p_merged", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+                                                   HasNullabilityState(
+                                                       kBoolFalse, kBoolTrue))),
+        IsStringMapEntry(
+            "p_b_false",
+            HoldsVariable("p", Output,
+                          HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+        IsStringMapEntry(
+            "p_merged",
+            HoldsVariable("p", Output,
+                          HasNullabilityState(kBoolUnknown, kBoolUnknown))),
         IsStringMapEntry("b_true", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))),
+                                                 HasNullabilityState(
+                                                     kBoolFalse, kBoolTrue))),
         IsStringMapEntry("b_p_true", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolInvalid, kBoolInvalid))), // FIXME
-        IsStringMapEntry("b_p_false", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolInvalid, kBoolInvalid))));
+                                                   HasNullabilityState(
+                                                       kBoolInvalid,
+                                                       kBoolInvalid))), // FIXME
+        IsStringMapEntry(
+            "b_p_false",
+            HoldsVariable("p", Output,
+                          HasNullabilityState(kBoolInvalid, kBoolInvalid))));
   });
 }
 
@@ -287,17 +300,28 @@ TEST(ReverseNullTest, AssignmentOfCommonValues) {
   ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
     return UnorderedElementsAre(
         IsStringMapEntry("p_malloc", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
+                                                   HasNullabilityState(
+                                                       kBoolNullptr,
+                                                       kBoolNullptr))), // FIXME
         IsStringMapEntry("p_true", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolFalse, kBoolTrue))),
+                                                 HasNullabilityState(
+                                                     kBoolFalse, kBoolTrue))),
         IsStringMapEntry("p_false", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolTrue, kBoolFalse))),
-        IsStringMapEntry("p_merge", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolUnknown, kBoolUnknown))),
-        IsStringMapEntry("p_nullptr", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
-        IsStringMapEntry("p_extern", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolUnknown, kBoolUnknown))));
+                                                  HasNullabilityState(
+                                                      kBoolTrue, kBoolFalse))),
+        IsStringMapEntry(
+            "p_merge",
+            HoldsVariable("p", Output,
+                          HasNullabilityState(kBoolUnknown, kBoolUnknown))),
+        IsStringMapEntry(
+            "p_nullptr",
+            HoldsVariable(
+                "p", Output,
+                HasNullabilityState(kBoolNullptr, kBoolNullptr))), // FIXME
+        IsStringMapEntry(
+            "p_extern",
+            HoldsVariable("p", Output,
+                          HasNullabilityState(kBoolUnknown, kBoolUnknown))));
   });
 }
 
@@ -317,9 +341,10 @@ TEST(ReverseNullTest, MergeValues) {
     }
   )";
   ExpectDataflowResult(Code, [](const AnalysisOutputs &Output) -> auto {
-    return UnorderedElementsAre(
-        IsStringMapEntry("p_merge", HoldsVariable("p", Output,
-            HasNullabilityState(kBoolUnknown, kBoolTrue))));
+    return UnorderedElementsAre(IsStringMapEntry(
+        "p_merge",
+        HoldsVariable("p", Output,
+                      HasNullabilityState(kBoolUnknown, kBoolTrue))));
   });
 }
 

@Discookie Discookie force-pushed the reverse-null branch 2 times, most recently from 2bb84a6 to 159c114 Compare October 25, 2023 07:37
Discookie pushed a commit that referenced this pull request Oct 30, 2023
…tePluginObject

After llvm#68052 this function changed from returning
a nullptr with `return {};` to returning Expected and hitting `llvm_unreachable` before it could
do so.

I gather that we're never supposed to call this function, but on Windows we actually do call
this function because `interpreter->CreateScriptedProcessInterface()` returns
`ScriptedProcessInterface` not `ScriptedProcessPythonInterface`. Likely because
`target_sp->GetDebugger().GetScriptInterpreter()` also does not return a Python related class.

The previously XFAILed test crashed with:
```
 # .---command stderr------------
 # | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
 # | Stack dump:
 # | 0.  Program arguments: c:\\users\\tcwg\\david.spickett\\build-llvm\\bin\\lldb-test.exe ir-memory-map C:\\Users\\tcwg\\david.spickett\\build-llvm\\tools\\lldb\\test\\Shell\\Expr\\Output\\TestIRMemoryMapWindows.test.tmp C:\\Users\\tcwg\\david.spickett\\llvm-project\\lldb\\test\\Shell\\Expr/Inputs/ir-memory-map-basic
 # | 1.  HandleCommand(command = "run")
 # | Exception Code: 0xC000001D
 # | #0 0x00007ff696b5f588 lldb_private::ScriptedProcessInterface::CreatePluginObject(class llvm::StringRef, class lldb_private::ExecutionContext &, class std::shared_ptr<class lldb_private::StructuredData::Dictionary>, class lldb_private::StructuredData::Generic *) C:\Users\tcwg\david.spickett\llvm-project\lldb\include\lldb\Interpreter\Interfaces\ScriptedProcessInterface.h:28:0
 # | #1 0x00007ff696b1d808 llvm::Expected<std::shared_ptr<lldb_private::StructuredData::Generic> >::operator bool C:\Users\tcwg\david.spickett\llvm-project\llvm\include\llvm\Support\Error.h:567:0
 # | llvm#2 0x00007ff696b1d808 lldb_private::ScriptedProcess::ScriptedProcess(class std::shared_ptr<class lldb_private::Target>, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::ScriptedMetadata const &, class lldb_private::Status &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Process\scripted\ScriptedProcess.cpp:115:0
 # | llvm#3 0x00007ff696b1d124 std::shared_ptr<lldb_private::ScriptedProcess>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1478:0
 # | llvm#4 0x00007ff696b1d124 lldb_private::ScriptedProcess::CreateInstance(class std::shared_ptr<class lldb_private::Target>, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Process\scripted\ScriptedProcess.cpp:61:0
 # | llvm#5 0x00007ff69699c8f4 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0
 # | llvm#6 0x00007ff69699c8f4 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0
 # | llvm#7 0x00007ff69699c8f4 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0
 # | llvm#8 0x00007ff69699c8f4 lldb_private::Process::FindPlugin(class std::shared_ptr<class lldb_private::Target>, class llvm::StringRef, class std::shared_ptr<class lldb_private::Listener>, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Process.cpp:396:0
 # | llvm#9 0x00007ff6969bd708 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0
 # | llvm#10 0x00007ff6969bd708 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0
 # | llvm#11 0x00007ff6969bd708 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0
 # | llvm#12 0x00007ff6969bd708 lldb_private::Target::CreateProcess(class std::shared_ptr<class lldb_private::Listener>, class llvm::StringRef, class lldb_private::FileSpec const *, bool) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Target.cpp:215:0
 # | llvm#13 0x00007ff696b13af0 std::_Ptr_base<lldb_private::Process>::_Ptr_base C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1230:0
 # | llvm#14 0x00007ff696b13af0 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1524:0
 # | llvm#15 0x00007ff696b13af0 lldb_private::PlatformWindows::DebugProcess(class lldb_private::ProcessLaunchInfo &, class lldb_private::Debugger &, class lldb_private::Target &, class lldb_private::Status &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Plugins\Platform\Windows\PlatformWindows.cpp:495:0
 # | llvm#16 0x00007ff6969cf590 std::_Ptr_base<lldb_private::Process>::_Move_construct_from C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1237:0
 # | llvm#17 0x00007ff6969cf590 std::shared_ptr<lldb_private::Process>::shared_ptr C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1534:0
 # | llvm#18 0x00007ff6969cf590 std::shared_ptr<lldb_private::Process>::operator= C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32124\include\memory:1594:0
 # | llvm#19 0x00007ff6969cf590 lldb_private::Target::Launch(class lldb_private::ProcessLaunchInfo &, class lldb_private::Stream *) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Target\Target.cpp:3274:0
 # | llvm#20 0x00007ff696fff82c CommandObjectProcessLaunch::DoExecute(class lldb_private::Args &, class lldb_private::CommandReturnObject &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Commands\CommandObjectProcess.cpp:258:0
 # | llvm#21 0x00007ff696fab6c0 lldb_private::CommandObjectParsed::Execute(char const *, class lldb_private::CommandReturnObject &) C:\Users\tcwg\david.spickett\llvm-project\lldb\source\Interpreter\CommandObject.cpp:751:0
 # `-----------------------------
 # error: command failed with exit status: 0xc000001d
```

That might be a bug on the Windows side, or an artifact of how our build is setup,
but whatever it is, having `CreatePluginObject` return an error and
the caller check it, fixes the failing test.

The built lldb can run the script command to use Python, but I'm not sure if that means
anything.
@Discookie Discookie marked this pull request as draft November 2, 2023 08:39
@Discookie Discookie force-pushed the reverse-null branch 2 times, most recently from 9185f98 to 298cc6f Compare November 2, 2023 09:30
@whisperity
Copy link

I will detail my high-level comments first before delving into the nitty-gritty of the source code. At face value, and immediately looking at the very first line in the header, I got slapped in the face with the check's name as it stands right now. I really, really do not like it... If I only go off of the check's name and search Google for reverse null, the first possibly related idea (REVERSE_INULL False Positive “Dereference before NULL check" at Synopsys Coverity Community) in the result list is at the 7th position. Granted, other search engines like DDG or Bing show in 1st or 2nd location the "what is difference between REVERSE_INULL and FORWARD_NULL error in coverity scan(static code analysis)?" at Stack Overflow. It begs the question what does I mean in INULL anyway...??? Note, the only answer here is a guy who admittedly "used to work for Synopsys". Also, even with DDG and Bing, most of the results are completely derailing, unrelated things like operator ?./?[] (something I really love, btw...) in C# and whatnot.

The problem here is that to understand from the general name of the check what we are trying to achieve requires either:

  • knowing the related Coverity analysis rule
  • knowing the theoretical details of dataflow-based analyses

Both of these would mean the user is already seasoned in static analysis. They likely are not, especially when it comes to people who just drive Clang-Tidy through things like coc-clangd or CLion.

Thus, I would like to say that we should give some consideration to the check's name. I think a longer but more informative name (to everyday developers not knowledgeable about this field) would be preferable to a concise but very internal name. Something along the lines of bugprone-null-check-after-dereference? That could capture the meaning nicely.


I think we should consider whether this is truly just bug_prone_ in the first place. Assuming the pointer was indeed null, if you have already dereferenced it by the time you reached the condition, you are definitely not null, because otherwise the program "should have" crashed, right? Or am I missing something and this is a too simple overview?

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

Partial review for now. Unfortunately, I need to leave my work now, I will likely continue from another location tomorrow, and I do not know whether GitHub saves pending reviews clientside or serverside...

@@ -0,0 +1,126 @@

Choose a reason for hiding this comment

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

✏️ (Style nit: stale empty line.)

Comment on lines 10 to 11
// finding invalid dereferences, and unnecessary null-checks. Only a limited
// set of operations are currently recognized.

Choose a reason for hiding this comment

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

(Nit: It would be beneficial if this "limited set" was explained. What important bits that are meaningfully missing?)

Copy link
Owner Author

Choose a reason for hiding this comment

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

Will be documented.

Comment on lines 30 to 32
namespace clang {
namespace tidy {
namespace bugprone {

Choose a reason for hiding this comment

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

✏️ (Style nit: LLVM is now in C++17, so nested namespace specifiers namespace clang::tidy::bugprone could be used instead.)


constexpr char kBoolTrue[] = "true";
constexpr char kBoolFalse[] = "false";
constexpr char kBoolInvalid[] = "truefalse";

Choose a reason for hiding this comment

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

(Maybe this could spell "invalid" instead. Thinking about truefalse its not immediately apparent whether this means "true AND false" or "true OR false", and those are not the same things.)

Copy link
Owner Author

Choose a reason for hiding this comment

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

truefalse was just more convenient, but I will rework the debugBoolValue function that uses this.

Comment on lines 79 to 114
auto ptrToVar() { return declRefExpr(hasType(isAnyPointer())).bind(kVar); }
auto namedPtrToVar(llvm::StringRef name) {
return declRefExpr(allOf(hasType(isAnyPointer()),
hasDeclaration(namedDecl(hasName(name)))
)).bind(kVar);
}

auto nameToVar(llvm::StringRef name) {
return declRefExpr(hasType(isAnyPointer()), hasDeclaration(
namedDecl(hasName(name))
)).bind(kVar);
}

auto voidCastMatcher() {
return stmt(hasDescendant(castExpr(hasCastKind(CK_ToVoid),
hasSourceExpression(ptrToVar())
)));
}

auto derefMatcher() {
return traverse(TK_IgnoreUnlessSpelledInSource,
unaryOperator(
hasOperatorName("*"),
hasUnaryOperand(ptrToVar())
)
);
}

Choose a reason for hiding this comment

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

I'm not sure what is the convention for clang/unittests, but similar constructs in Clang-Tidy are usually expressed in the form of static const auto objects allocated directly in the TU for the check's implementation. (For matchers that take a parameter, the AST_MATCHER_P macro creates the appropriate matcher class.)

These factory functions can be simplified:

  • For matchers that do not have parameters and do not require imperative logic, a static const(expr?) auto X = matcher(foo()); object that lives only once can express the needed logic.
  • For matchers that do require imperative logic, AST_MATCHER can create the appropriately named matcher.
  • Similarly, where parameters are needed, AST_MATCHER_P does the same thing.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Indeed it does do the same thing. Ultimately I think instead of redefining matchers here, the ones from the ReverseNullModel itself should be somehow used.

Arena &A = Env.arena();

if (auto *RootValue = Env.getValue(*Var)) {
// FIXME: Hack

Choose a reason for hiding this comment

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

What is the hack here? What happens if the hack is removed, or how should it disappear?

Copy link
Owner Author

Choose a reason for hiding this comment

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

See the other hack.

Comment on lines 309 to 321
if (!IsNull && IsNonnull) {
return {{}, {Var->getBeginLoc()}};
}

if (IsNull && !IsNonnull) {
return {{Var->getBeginLoc()}, {}};
}

Choose a reason for hiding this comment

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

What are the semantics of this return value? At the definition of the type alias, it is only said it's a pair of Loc vectors. How is it special if one part of the pair has elements, and how if the other. Is it possible that BOTH vectors will contain an element?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Not documented, and will most likely be changed. One is for checking when the value is definitely not-null, and the other is for checking when the value is definitely null.

.CaseOfCFGStmt<Expr>(nullptrMatcher(), matchNullptrExpr)
.CaseOfCFGStmt<Expr>(addressofMatcher(), matchAddressofExpr)
.CaseOfCFGStmt<Expr>(functionCallMatcher(), matchFunctionCallExpr)
.CaseOfCFGStmt<Expr>(anyPointerMatcher(), matchFunctionCallExpr)

Choose a reason for hiding this comment

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

How is "any pointer" a "function call"?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Should be named the other way around, fixed.

if (!Type->isAnyPointerType())
return false; // FIXME: Maybe requires true?

// Alternative attempt: is any branch is true ie. can be a pointer, merging it is also a pointer

Choose a reason for hiding this comment

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

Can be a pointer, or can be a null pointer?
Does "can be a pointer" mean that it is not null?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Can be non-null.

Comment on lines 502 to 503
default:
llvm_unreachable("all cases covered in switch");

Choose a reason for hiding this comment

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

✏️ (My problem with having a default: here is that this kills -Wswitch, but I guess because not all branches of the switch actually return we can't really do any better than this... ☹️)

@whisperity
Copy link

Moreover, I have an architectural question as I just got to where the various alternative heuristics for calculating stuff are implemented. Could we somehow verify that alternates get the same consistent results somehow? If this is not feasible at low-level (i.e., running each alternate and comparing that they report the same value), then perhaps we should keep the alternates until the end so when the checker is ready for real-life testing on projects, we can run different configurations, and compare the user-facing reports from Tidy? Is this a good idea, even, or am I overthinking it? I see somewhere you mentioned things could simply just not conclude (and hey, bugprone-unchecked-optional-access at least used to infinitely hang; it might still do so...), so comparing the results eagerly inside the framework from all alternatives might not be a good idea.

Also, a schematic ASCII art for the lattice would be nice to have. (See the "Why are there 5 values?" question. So far, I don't know.)

@@ -0,0 +1,101 @@
//===-- ReverseNullModel.h --------------------------------------*- C++ -*-===//

Choose a reason for hiding this comment

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

If you can find this tomorrow, pending reviews are saved serverside.

Copy link
Owner Author

Choose a reason for hiding this comment

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

yay!

@@ -0,0 +1,723 @@
//===-- ReverseNullModel.h --------------------------------------*- C++ -*-===//

Choose a reason for hiding this comment

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

✏️ (Mismatch between file name and what is written in this comment.)

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

I think the high-level idea is convincing. In general, the presentation and the understandability of the code need to be improved. I did this review only by reading what's in the code without going into your external presentations and documents. I'm happy to look at them now, but the code in itself should still be capable of explaining what is going on and why it is going on.

@whisperity
Copy link

I've started running the check in the usual CI, and so far, there were no findings or no errors, but in TinyXML's xmltest.cpp, Clang-Tidy locked up. Almost constant 100% CPU use... I allowed it to go for 30 minutes at this point. To unblock the CI, I killed the job, so we'll be seeing what the results are for other projects...

There were some more hangs with the same symptoms, which I killed after letting the jobs stagnate for about 15-30 minutes, on libwebm (sample.cpp, sample_muxer.cpp, and mkvparser.cpp), Xerces-C (39 out of 348 hung), bitcoin (11 out of 251), protobuf (16 out of 161, but I think 1 of these were an explicit crash!), Qt (it doesn't even matter how many TUs failed; basically everything kept on hanging, so I killed the full analyze job...), Contour (2 out of 180 hung, +2 quick crashes), RCT2 (33 out of 541, but some of these were quick failing crashes). Likely, the analysis of llvm-project will behave similarly to Qt, and not conclude meaningfully in a reasonable time...

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

As discussed earlier today, if the checker is known to be crashy or hangy, we should preemptively forbid clangd from exercising it, as shown in this patch: llvm#69427

@Discookie Discookie force-pushed the reverse-null branch 3 times, most recently from 4d20f44 to 37d3a78 Compare November 28, 2023 06:13
Discookie pushed a commit that referenced this pull request Dec 4, 2023
…lvm#73463)

Despite CWG2497 not being resolved, it is reasonable to expect the
following code to compile (and which is supported by other compilers)

```cpp
  template<typename T> constexpr T f();
  constexpr int g() { return f<int>(); } // #1
  template<typename T> constexpr T f() { return 123; }
  int k[g()];
  // llvm#2
```

To that end, we eagerly instantiate all referenced specializations of
constexpr functions when they are defined.

We maintain a map of (pattern, [instantiations]) independent of
`PendingInstantiations` to avoid having to iterate that list after each
function definition.

We should apply the same logic to constexpr variables, but I wanted to
keep the PR small.

Fixes llvm#73232
Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

In general, I think the implementation is now much better understandable (also given the fact that this was the 2nd pass over the code). There are some crucial pain points that still need to be improved, however:

  1. The diagnostic output should be reviewed, especially this "Can we get to 'the' previous dereference?"-esque questions. If not, then at least the Checker's CPP file should contain a comment about this...
  2. The documentation file for the checker has a lot of spotty parts. I tried to give as many comments as I could. There are some conflated terminologies (like safety?) and in general the flow of the explanation is just not a good angle to ease the user into understanding what is going on. We're doing something rather complex and new here (especially this all path / no path "stuff").

I also couldn't find any discussion about the lattice merging strategies. I can't even find my own comment wrt. this, so this might be a significant Mandela effect, but I do remember saying that we should evaluate which strategy is better (if such a comparison can be drawn, even...) and leave only that in the suggested upstream patch. If such comparisons are not possible, then running this or that strategy should be configurable through a checker option. I would still like to have some sort of evaluation even if we can't pronounce a definite "winner".

Comment on lines 1 to 2
//===--- NullCheckAfterDereferenceCheck.cpp - clang-tidy
//--------------------------------===//

Choose a reason for hiding this comment

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

(✏️ Style nit: Something went wrong here with the formatting, the line became too long and split.)


using ast_matchers::MatchFinder;
using dataflow::NullCheckAfterDereferenceModel;
using dataflow::ReverseNullDiagnoser;

Choose a reason for hiding this comment

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

❓ The "reverse null" name remained here. This is not inherently a problem! But perhaps it would be worthwhile to think about renaming this to BackpropagatingNull or ...Nullness instead?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Renamed to NullCheckAfterDereference, since this diagnoser is specific to the checker, not to the framework.

Comment on lines 10 to 11
This check identifies potentially unsafe pointer null-checks,
by finding cases where the pointer cannot be null at the location of the check.

Choose a reason for hiding this comment

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

🐩 Is this true? Why would it be unsafe? The null conditional is redundant. What's unsafe is the unconditional dereference prior to the condition, right?

Copy link
Owner Author

Choose a reason for hiding this comment

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

In the first example, I wanted to give the exact warning the checker provides, so later I can expand on the specific edge-cases. Changed unsafe => bugprone where possible though.

if (Env.flowConditionImplies(Val->formula()))
return SR::True;

if (Env.flowConditionImplies(Env.arena().makeNot(Val->formula())))

Choose a reason for hiding this comment

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

That's actually neat! But it triggers a follow-up question: Here, you mean "analysis" as in the analysis of a single function (as driven by the helper function within the check's implementation), and not the analysis of an entire T.U., right?

*p = 42;

if (p) {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked despite dereferencing it earlier [bugprone-experimental-reverse-null]

Choose a reason for hiding this comment

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

Do we know, or can we know this "earlier" location? Here it would be trivial to give a note: to line 8 (*p = 42), but it might not be that trivial in the framework. Is it worthwhile to pursue this, or is it too troublesome and could only be done as a future, separate patch?

Copy link
Owner Author

Choose a reason for hiding this comment

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

In a past version of the data-flow framework, storing the location of a previous dereference was possible, but currently I don't think it can be implemented. I do want to get this feature working as well, even if not showing all previous dereferences/assignments, at least showing one, but I haven't found a way to do this yet.

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

(Oh yeah I forgot one thing which would end up being in 1288123 locations: in many places the variable names do not follow established LLVM conventions, e.g. lhsResult instead of LHSResult. This is more of a tedious nit than anything significant.)

@whisperity
Copy link

I put the reverse-null branch into the CI and the build broke because the tests are not passing. I'm retrying with skipped tests. 😋

@whisperity
Copy link

Re #1 (comment)

The analyses re-run in the CI, and I uploaded the results to the usual location, although the "detection status" diff of the reports had gone wrong due to the rename.

Nevertheless, here are the failure statistics. All the tallied failures are due to hung kills. ✅ No crashes! I killed an analysis manually after having observed more than 1 hr of runtime for the file. (CodeChecker analyze SHOULD have a --timeout flag or something like that, but I forgot to configure it.)

  • TinyXML: 1/2
  • libWebM: 2/12
  • Xerces-C: 37/348
  • Bitcoin: 11/251
  • ProtoBuf: 15/161
  • Qt: ❌ The whole job was killed, there is no point executing the checker, everything hangs.
  • Contour: 4/180
  • ACID: ❌ Build failed.
  • OpenRCT2: 20/574
  • LLVM: ❌ The whole job was killed, there is no point executing the checker, everything hangs.

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

Also, as the checker is known to hang we should position ourselves with this check immediately in a defensive way and add it to the list of forbidden-by-default checks when running through clangd. Please modify the file in the same way as in this commit: e63ab13.

@Discookie
Copy link
Owner Author

Changed the used merge/compare algos to the non-fast ones. Unit tests don't pass yet, but I'm debugging it.

@whisperity
Copy link

Given the changes in 37d3a78..34bdc9b, I do not have further in-line comments. The high-level comment about detecting and diagnosing the "previous dereference" location is still open.

@Discookie
Copy link
Owner Author

It is now the latest version, ready to test.

@Discookie
Copy link
Owner Author

(As soon as the commit tree syncs anyways.)

@whisperity
Copy link

It is now the latest version, ready to test.

I fired up b9fba0444d4ffbb35548fa7dd546157a6160c3c7 in the CI. No issues on any of the C projects so far, but I think this was intended that the check is incapable of dealing with C, so yeah...

I'll come back with the results and the hangs after C++ analyses succeeded. What I can see now is that xmltest.cpp in TinyXML is one of the usual suspects...

@whisperity
Copy link

(As soon as the commit tree syncs anyways.)

@Discookie I'm not convinced it will do that without a nudge. Next time you update the patch let's take care and do a separate push to master first and then to the PR branch, maybe it will not get into this botched state in that case.


Alright, the analysis concluded over C++ projects. I actually restarted the job (with --timeout 1800, aka. 30 minutes) and specified only the C++ projects from the test set, the grand total time of the full measurement job was 2 d 17 hr. I uploaded the results to the Demo server.

I observed no crashes at all. So all the failures you can see on the run list are from timeouts.

The 30-minute timeout might have been a bit eager, but at least we know that feature works in CodeChecker. This is a per-process timeout, so every time a TU hung for 30 minutes, the Tidy process got terminated. Observing the CI logs (it's CSA-measurements-driver#2923 if you want to check) it seemed that for TUs that do not hang, the analyses execute in a sufficiently dynamic fashion, so maybe a 5-minute timeout would be a more reasonable cut-off for the next test. (I just tried to simulate what I did manually in the past, killing processes after roughly 30 min of runtime... I didn't know about --timeout back then.)

In general, most projects succeeded appropriately. Performance was the worst on Qt, where only 172 TUs out of the 1175 succeeded, taking a whopping 34 hours to execute. LLVM was the second worst (it being on the stand isn't surprising, but Qt lapping it, at first glance, is...) with 23:13 runtime and 687 out of the 3089 jobs failing.

There are quite some new reports, almost 10 per project, except for LLVM, where there were previously no reports but now there are 69.


Potential false positives

Here are some of the interesting findings which I think should be mentioned in the documentation (and preferably also with a test with FIXME:) at least. I have not looked at EVERY report!

https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=libwebm_libwebm-1.0.0.27_Discookie_reverse-null&detection-status=New&report-id=3497414&report-hash=e75b81fcd825616e0ec904ffef1c99ac&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Flibwebm%2Fmkvparser.cpp

ParseTrackEntry(..., Track*&, ...) takes the pointer by reference so it could have been set to a non-null in that function. So saying the if is redundant because the value was invited to a NULL earlier but there is a potentially mutating call in between.

Dittos

True positives

Strange reports

@Discookie
Copy link
Owner Author

Hm. I thought to check for function calls that return pointers, but not for functions that take pointers as arguments. Will implement.

The strange report is not that strange, but I don't know how better to display it without spans. In the constructor a call is made with &(diagObj->...) as its argument, and this explicitly dereferences diagObj before the function starts.

@whisperity
Copy link

The strange report is not that strange, but I don't know how better to display it without spans. In the constructor a call is made with &(diagObj->...) as its argument, and this explicitly dereferences diagObj before the function starts.

I might have linked the wrong one there. That one with the ctor was easy to understand I found another one that was strange.

Anyway the reports are up on the server, you can go through them one by one. But most of them is the T*& case.

Discookie pushed a commit that referenced this pull request Mar 4, 2024
The concurrent tests all do a pthread_join at the end, and
concurrent_base.py stops after that pthread_join and sanity checks that
only 1 thread is running. On macOS, after pthread_join() has completed,
there can be an extra thread still running which is completing the
details of that task asynchronously; this causes testsuite failures.
When this happens, we see the second thread is in

```
frame #0: 0x0000000180ce7700 libsystem_kernel.dylib`__ulock_wake + 8
frame #1: 0x0000000180d25ad4 libsystem_pthread.dylib`_pthread_joiner_wake + 52
frame llvm#2: 0x0000000180d23c18 libsystem_pthread.dylib`_pthread_terminate + 384
frame llvm#3: 0x0000000180d23a98 libsystem_pthread.dylib`_pthread_terminate_invoke + 92
frame llvm#4: 0x0000000180d26740 libsystem_pthread.dylib`_pthread_exit + 112
frame llvm#5: 0x0000000180d26040 libsystem_pthread.dylib`_pthread_start + 148
```

there are none of the functions from the test file present on this
thread.

In this patch, instead of counting the number of threads, I iterate over
the threads looking for functions from our test file (by name) and only
count threads that have at least one of them.

It's a lower frequency failure than the darwin kernel bug causing an
extra step instruction mach exception when hardware
breakpoint/watchpoints are used, but once I fixed that, this came up as
the next most common failure for these tests.

rdar://110555062
Discookie pushed a commit that referenced this pull request Mar 4, 2024
…lvm#80904)"

This reverts commit b1ac052.

This commit breaks coroutine splitting for non-swift calling convention
functions. In this example:

```ll
; ModuleID = 'repro.ll'
source_filename = "stdlib/test/runtime/test_llcl.mojo"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@0 = internal constant { i32, i32 } { i32 trunc (i64 sub (i64 ptrtoint (ptr @craSH to i64), i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32 }, ptr @0, i32 0, i32 1) to i64)) to i32), i32 64 }

define dso_local void @af_suspend_fn(ptr %0, i64 %1, ptr %2) #0 {
  ret void
}

define dso_local void @craSH(ptr %0) #0 {
  %2 = call token @llvm.coro.id.async(i32 64, i32 8, i32 0, ptr @0)
  %3 = call ptr @llvm.coro.begin(token %2, ptr null)
  %4 = getelementptr inbounds { ptr, { ptr, ptr }, i64, { ptr, i1 }, i64, i64 }, ptr poison, i32 0, i32 0
  %5 = call ptr @llvm.coro.async.resume()
  store ptr %5, ptr %4, align 8
  %6 = call { ptr, ptr, ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0p0p0s(i32 0, ptr %5, ptr @ctxt_proj_fn, ptr @af_suspend_fn, ptr poison, i64 -1, ptr poison)
  ret void
}

define dso_local ptr @ctxt_proj_fn(ptr %0) #0 {
  ret ptr %0
}

; Function Attrs: nomerge nounwind
declare { ptr, ptr, ptr } @llvm.coro.suspend.async.sl_p0p0p0s(i32, ptr, ptr, ...) #1

; Function Attrs: nounwind
declare token @llvm.coro.id.async(i32, i32, i32, ptr) llvm#2

; Function Attrs: nounwind
declare ptr @llvm.coro.begin(token, ptr writeonly) llvm#2

; Function Attrs: nomerge nounwind
declare ptr @llvm.coro.async.resume() #1

attributes #0 = { "target-features"="+adx,+aes,+avx,+avx2,+bmi,+bmi2,+clflushopt,+clwb,+clzero,+crc32,+cx16,+cx8,+f16c,+fma,+fsgsbase,+fxsr,+invpcid,+lzcnt,+mmx,+movbe,+mwaitx,+pclmul,+pku,+popcnt,+prfchw,+rdpid,+rdpru,+rdrnd,+rdseed,+sahf,+sha,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+sse4a,+ssse3,+vaes,+vpclmulqdq,+wbnoinvd,+x87,+xsave,+xsavec,+xsaveopt,+xsaves" }
attributes #1 = { nomerge nounwind }
attributes llvm#2 = { nounwind }
```

This verifier crashes after the `coro-split` pass with

```
cannot guarantee tail call due to mismatched parameter counts
  musttail call void @af_suspend_fn(ptr poison, i64 -1, ptr poison)
LLVM ERROR: Broken function
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: opt ../../../reduced.ll -O0
 #0 0x00007f1d89645c0e __interceptor_backtrace.part.0 /build/gcc-11-XeT9lY/gcc-11-11.4.0/build/x86_64-linux-gnu/libsanitizer/asan/../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4193:28
 #1 0x0000556d94d254f7 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Unix/Signals.inc:723:22
 llvm#2 0x0000556d94d19a2f llvm::sys::RunSignalHandlers() /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Signals.cpp:105:20
 llvm#3 0x0000556d94d1aa42 SignalHandler(int) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/Unix/Signals.inc:371:36
 llvm#4 0x00007f1d88e42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 llvm#5 0x00007f1d88e969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 llvm#6 0x00007f1d88e969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 llvm#7 0x00007f1d88e969fc pthread_kill ./nptl/pthread_kill.c:89:10
 llvm#8 0x00007f1d88e42476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 llvm#9 0x00007f1d88e287f3 abort ./stdlib/abort.c:81:7
 llvm#10 0x0000556d8944be01 std::vector<llvm::json::Value, std::allocator<llvm::json::Value>>::size() const /usr/include/c++/11/bits/stl_vector.h:919:40
 llvm#11 0x0000556d8944be01 bool std::operator==<llvm::json::Value, std::allocator<llvm::json::Value>>(std::vector<llvm::json::Value, std::allocator<llvm::json::Value>> const&, std::vector<llvm::json::Value, std::allocator<llvm::json::Value>> const&) /usr/include/c++/11/bits/stl_vector.h:1893:23
 llvm#12 0x0000556d8944be01 llvm::json::operator==(llvm::json::Array const&, llvm::json::Array const&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/Support/JSON.h:572:69
 llvm#13 0x0000556d8944be01 llvm::json::operator==(llvm::json::Value const&, llvm::json::Value const&) (.cold) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/JSON.cpp:204:28
 llvm#14 0x0000556d949ed2bd llvm::report_fatal_error(char const*, bool) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Support/ErrorHandling.cpp:82:70
 llvm#15 0x0000556d8e37e876 llvm::SmallVectorBase<unsigned int>::size() const /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:91:32
 llvm#16 0x0000556d8e37e876 llvm::SmallVectorTemplateCommon<llvm::DiagnosticInfoOptimizationBase::Argument, void>::end() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:282:41
 llvm#17 0x0000556d8e37e876 llvm::SmallVector<llvm::DiagnosticInfoOptimizationBase::Argument, 4u>::~SmallVector() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallVector.h:1215:24
 llvm#18 0x0000556d8e37e876 llvm::DiagnosticInfoOptimizationBase::~DiagnosticInfoOptimizationBase() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:413:7
 llvm#19 0x0000556d8e37e876 llvm::DiagnosticInfoIROptimization::~DiagnosticInfoIROptimization() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:622:7
 llvm#20 0x0000556d8e37e876 llvm::OptimizationRemark::~OptimizationRemark() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/DiagnosticInfo.h:689:7
 llvm#21 0x0000556d8e37e876 operator() /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroSplit.cpp:2213:14
 llvm#22 0x0000556d8e37e876 emit<llvm::CoroSplitPass::run(llvm::LazyCallGraph::SCC&, llvm::CGSCCAnalysisManager&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&)::<lambda()> > /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/Analysis/OptimizationRemarkEmitter.h:83:12
 llvm#23 0x0000556d8e37e876 llvm::CoroSplitPass::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroSplit.cpp:2212:13
 llvm#24 0x0000556d8c36ecb1 llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::CoroSplitPass, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3
 llvm#25 0x0000556d91c1a84f llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Analysis/CGSCCPassManager.cpp:90:12
 llvm#26 0x0000556d8c3690d1 llvm::detail::PassModel<llvm::LazyCallGraph::SCC, llvm::PassManager<llvm::LazyCallGraph::SCC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&>::run(llvm::LazyCallGraph::SCC&, llvm::AnalysisManager<llvm::LazyCallGraph::SCC, llvm::LazyCallGraph&>&, llvm::LazyCallGraph&, llvm::CGSCCUpdateResult&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3
 llvm#27 0x0000556d91c2162d llvm::ModuleToPostOrderCGSCCPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Analysis/CGSCCPassManager.cpp:278:18
 llvm#28 0x0000556d8c369035 llvm::detail::PassModel<llvm::Module, llvm::ModuleToPostOrderCGSCCPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3
 llvm#29 0x0000556d9457abc5 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManager.h:247:20
 llvm#30 0x0000556d8e30979e llvm::CoroConditionalWrapper::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/lib/Transforms/Coroutines/CoroConditionalWrapper.cpp:19:74
 llvm#31 0x0000556d8c365755 llvm::detail::PassModel<llvm::Module, llvm::CoroConditionalWrapper, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:91:3
 llvm#32 0x0000556d9457abc5 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/PassManager.h:247:20
 llvm#33 0x0000556d89818556 llvm::SmallPtrSetImplBase::isSmall() const /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:196:33
 llvm#34 0x0000556d89818556 llvm::SmallPtrSetImplBase::~SmallPtrSetImplBase() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:84:17
 llvm#35 0x0000556d89818556 llvm::SmallPtrSetImpl<llvm::AnalysisKey*>::~SmallPtrSetImpl() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:321:7
 llvm#36 0x0000556d89818556 llvm::SmallPtrSet<llvm::AnalysisKey*, 2u>::~SmallPtrSet() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h:427:7
 llvm#37 0x0000556d89818556 llvm::PreservedAnalyses::~PreservedAnalyses() /home/ubuntu/modular/third-party/llvm-project/llvm/include/llvm/IR/Analysis.h:109:7
 llvm#38 0x0000556d89818556 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/NewPMDriver.cpp:532:10
 llvm#39 0x0000556d897e3939 optMain /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/optdriver.cpp:737:27
 llvm#40 0x0000556d89455461 main /home/ubuntu/modular/third-party/llvm-project/llvm/tools/opt/opt.cpp:25:33
 llvm#41 0x00007f1d88e29d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
 llvm#42 0x00007f1d88e29e40 call_init ./csu/../csu/libc-start.c:128:20
 llvm#43 0x00007f1d88e29e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
 llvm#44 0x0000556d897b6335 _start (/home/ubuntu/modular/.derived/third-party/llvm-project/build-relwithdebinfo-asan/bin/opt+0x150c335)
Aborted (core dumped)
Discookie pushed a commit that referenced this pull request Mar 4, 2024
…ter partial ordering when determining primary template (llvm#82417)

Consider the following:
```
struct A {
  static constexpr bool x = true;
};

template<typename T, typename U>
void f(T, U) noexcept(T::y); // #1, error: no member named 'y' in 'A'

template<typename T, typename U>
void f(T, U*) noexcept(T::x); // llvm#2

template<>
void f(A, int*) noexcept; // explicit specialization of llvm#2
```

We currently instantiate the exception specification of all candidate
function template specializations when deducting template arguments for
an explicit specialization, which results in a error despite `#1` not
being selected by partial ordering as the most specialized template.
According to [except.spec] p13:
> An exception specification is considered to be needed when: 
> - [...]
> - the exception specification is compared to that of another
declaration (e.g., an explicit specialization or an overriding virtual
function);

Assuming that "comparing declarations" means "determining whether the
declarations correspond and declare the same entity" (per [basic.scope.scope] p4 and
[basic.link] p11.1, respectively), the exception specification does _not_ need to be
instantiated until _after_ partial ordering, at which point we determine
whether the implicitly instantiated specialization and the explicit
specialization declare the same entity (the determination of whether two
functions/function templates correspond does not consider the exception
specifications).

This patch defers the instantiation of the exception specification until
a single function template specialization is selected via partial
ordering, matching the behavior of GCC, EDG, and
MSVC: see https://godbolt.org/z/Ebb6GTcWE.
@whisperity
Copy link

Alright, I've gone over most of the reports one more time. If something was very hard to understand due to complex code, I skipped it!

The false positives stemming from not modelling out ptrs need at least a test case (marked with a FIXME) and a sentence or two with an example about it in the documentation file! There are a lot of cases like such.

Questionable: 1 (Why is the report there at the parameter loc?) 2 (Fuzzy locations here as well) 3 (How can something that is dereferenced only be null???)

Strange and not understandable: 1 2 3 4 5 (all from the same function) 6 7 8 (Is this because it is a result of an &Obj?)


Some true positives in the LLVM project (albeit older version!): 1 2 3 4 5

These ones might be worthwhile to have some separate patches to fix, which would double as an immediate proof and reasoning for this check!

@Discookie
Copy link
Owner Author

Apparently the questionable reports are because of two reasons. First is that the checker does not put note tags on variable declarations, and the second is that the checker can't check if the note tag is lexically before the warning's location in the current system. The first few strange reports are also caused by this.

Strange 6, 7, and 8 look to be true positives, as &Obj can never be null yep, and in case 7 the pointer is already checked. Note tags are missing from some operations here as well, more note tag locations will be added later.

Copy link

@whisperity whisperity left a comment

Choose a reason for hiding this comment

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

[This can be closed.]

@Discookie Discookie closed this Mar 6, 2024
Discookie pushed a commit that referenced this pull request May 6, 2024
Builder alerted me to the failing test, attempt #1 in the blind.
Discookie pushed a commit that referenced this pull request Jun 18, 2024
…des (llvm#94453)

LSR will generate chains of related instructions with a known increment
between them. With SVE, in the case of the test case, this can include
increments like 'vscale * 16 + 8'. The idea of this patch is if we have
a '+8' increment already calculated in the chain, we can generate a
(legal) '+ vscale*16' addressing mode from it, allowing us to use the
'[x16, #1, mul vl]' addressing mode instructions.

In order to do this we keep track of the known 'bases' when generating
chains in GenerateIVChain, checking for each if the accumulated
increment expression from the base neatly folds into a legal addressing
mode. If they do not we fall back to the existing LeftOverExpr, whether
it is legal or not.

This is mostly orthogonal to llvm#88124, dealing with the generation of
chains as opposed to rest of LSR. The existing vscale addressing mode
work has greatly helped compared to the last time I looked at this,
allowing us to check that the addressing modes are indeed legal.
Discookie pushed a commit that referenced this pull request Jul 4, 2024
…arallel fusion llvm#94391 (llvm#97607)"

This reverts commit edbc0e3.

Reason for rollback. ASAN complains about this PR:

==4320==ERROR: AddressSanitizer: heap-use-after-free on address 0x502000006cd8 at pc 0x55e2978d63cf bp 0x7ffe6431c2b0 sp 0x7ffe6431c2a8
READ of size 8 at 0x502000006cd8 thread T0
    #0 0x55e2978d63ce in map<llvm::MutableArrayRef<mlir::BlockArgument> &, llvm::MutableArrayRef<mlir::BlockArgument>, nullptr> mlir/include/mlir/IR/IRMapping.h:40:11
    #1 0x55e2978d63ce in mlir::createFused(mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface, mlir::RewriterBase&, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)>, llvm::function_ref<void (mlir::RewriterBase&, mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface&, mlir::IRMapping)>) mlir/lib/Interfaces/LoopLikeInterface.cpp:156:11
    llvm#2 0x55e2952a614b in mlir::fuseIndependentSiblingForLoops(mlir::scf::ForOp, mlir::scf::ForOp, mlir::RewriterBase&) mlir/lib/Dialect/SCF/Utils/Utils.cpp:1398:43
    llvm#3 0x55e291480c6f in mlir::transform::LoopFuseSiblingOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp:482:17
    llvm#4 0x55e29149ed5e in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::LoopFuseSiblingOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56
    llvm#5 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14
    llvm#6 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48
    llvm#7 0x55e294646a8d in applySequenceBlock(mlir::Block&, mlir::transform::FailurePropagationMode, mlir::transform::TransformState&, mlir::transform::TransformResults&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:1788:15
    llvm#8 0x55e29464f927 in mlir::transform::NamedSequenceOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:2155:10
    llvm#9 0x55e2945d28ee in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::NamedSequenceOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56
    llvm#10 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14
    llvm#11 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48
    llvm#12 0x55e2974a5fe2 in mlir::transform::applyTransforms(mlir::Operation*, mlir::transform::TransformOpInterface, mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>> const&, mlir::transform::TransformOptions const&, bool) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:2016:16
    llvm#13 0x55e2945888d7 in mlir::transform::applyTransformNamedSequence(mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>>, mlir::transform::TransformOpInterface, mlir::ModuleOp, mlir::transform::TransformOptions const&) mlir/lib/Dialect/Transform/Transforms/TransformInterpreterUtils.cpp:234:10
    llvm#14 0x55e294582446 in (anonymous namespace)::InterpreterPass::runOnOperation() mlir/lib/Dialect/Transform/Transforms/InterpreterPass.cpp:147:16
    llvm#15 0x55e2978e93c6 in operator() mlir/lib/Pass/Pass.cpp:527:17
    llvm#16 0x55e2978e93c6 in void llvm::function_ref<void ()>::callback_fn<mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int)::$_1>(long) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#17 0x55e2978e207a in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#18 0x55e2978e207a in executeAction<mlir::PassExecutionAction, mlir::Pass &> mlir/include/mlir/IR/MLIRContext.h:275:7
    llvm#19 0x55e2978e207a in mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) mlir/lib/Pass/Pass.cpp:521:21
    llvm#20 0x55e2978e5fbf in runPipeline mlir/lib/Pass/Pass.cpp:593:16
    llvm#21 0x55e2978e5fbf in mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) mlir/lib/Pass/Pass.cpp:904:10
    llvm#22 0x55e2978e5b65 in mlir::PassManager::run(mlir::Operation*) mlir/lib/Pass/Pass.cpp:884:60
    llvm#23 0x55e291ebb460 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:408:17
    llvm#24 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9
    llvm#25 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12
    llvm#26 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#27 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#28 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16
    llvm#29 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3
    llvm#30 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3
    llvm#31 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3
    llvm#32 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10
    llvm#33 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14
    llvm#34 0x55e291eb15f8 in mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:605:10
    llvm#35 0x55e29130d1be in main mlir/tools/mlir-opt/mlir-opt.cpp:311:33
    llvm#36 0x7fbcf3fff3d3 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x613d3) (BuildId: 9a996398ce14a94560b0c642eb4f6e94)
    llvm#37 0x55e2912365a9 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120

0x502000006cd8 is located 8 bytes inside of 16-byte region [0x502000006cd0,0x502000006ce0)
freed by thread T0 here:
    #0 0x55e29130b7e2 in operator delete(void*, unsigned long) compiler-rt/lib/asan/asan_new_delete.cpp:155:3
    #1 0x55e2979eb657 in __libcpp_operator_delete<void *, unsigned long>
    llvm#2 0x55e2979eb657 in __do_deallocate_handle_size<>
    llvm#3 0x55e2979eb657 in __libcpp_deallocate
    llvm#4 0x55e2979eb657 in deallocate
    llvm#5 0x55e2979eb657 in deallocate
    llvm#6 0x55e2979eb657 in operator()
    llvm#7 0x55e2979eb657 in ~vector
    llvm#8 0x55e2979eb657 in mlir::Block::~Block() mlir/lib/IR/Block.cpp:24:1
    llvm#9 0x55e2979ebc17 in deleteNode llvm/include/llvm/ADT/ilist.h:42:39
    llvm#10 0x55e2979ebc17 in erase llvm/include/llvm/ADT/ilist.h:205:5
    llvm#11 0x55e2979ebc17 in erase llvm/include/llvm/ADT/ilist.h:209:39
    llvm#12 0x55e2979ebc17 in mlir::Block::erase() mlir/lib/IR/Block.cpp:67:28
    llvm#13 0x55e297aef978 in mlir::RewriterBase::eraseBlock(mlir::Block*) mlir/lib/IR/PatternMatch.cpp:245:10
    llvm#14 0x55e297af0563 in mlir::RewriterBase::inlineBlockBefore(mlir::Block*, mlir::Block*, llvm::ilist_iterator<llvm::ilist_detail::node_options<mlir::Operation, false, false, void, false, void>, false, false>, mlir::ValueRange) mlir/lib/IR/PatternMatch.cpp:331:3
    llvm#15 0x55e297af06d8 in mlir::RewriterBase::mergeBlocks(mlir::Block*, mlir::Block*, mlir::ValueRange) mlir/lib/IR/PatternMatch.cpp:341:3
    llvm#16 0x55e297036608 in mlir::scf::ForOp::replaceWithAdditionalYields(mlir::RewriterBase&, mlir::ValueRange, bool, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)> const&) mlir/lib/Dialect/SCF/IR/SCF.cpp:575:12
    llvm#17 0x55e2970673ca in mlir::detail::LoopLikeOpInterfaceInterfaceTraits::Model<mlir::scf::ForOp>::replaceWithAdditionalYields(mlir::detail::LoopLikeOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::RewriterBase&, mlir::ValueRange, bool, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)> const&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Interfaces/LoopLikeInterface.h.inc:658:56
    llvm#18 0x55e2978d5feb in replaceWithAdditionalYields blaze-out/k8-opt-asan/bin/mlir/include/mlir/Interfaces/LoopLikeInterface.cpp.inc:105:14
    llvm#19 0x55e2978d5feb in mlir::createFused(mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface, mlir::RewriterBase&, std::__u::function<llvm::SmallVector<mlir::Value, 6u> (mlir::OpBuilder&, mlir::Location, llvm::ArrayRef<mlir::BlockArgument>)>, llvm::function_ref<void (mlir::RewriterBase&, mlir::LoopLikeOpInterface, mlir::LoopLikeOpInterface&, mlir::IRMapping)>) mlir/lib/Interfaces/LoopLikeInterface.cpp:135:14
    llvm#20 0x55e2952a614b in mlir::fuseIndependentSiblingForLoops(mlir::scf::ForOp, mlir::scf::ForOp, mlir::RewriterBase&) mlir/lib/Dialect/SCF/Utils/Utils.cpp:1398:43
    llvm#21 0x55e291480c6f in mlir::transform::LoopFuseSiblingOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp:482:17
    llvm#22 0x55e29149ed5e in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::LoopFuseSiblingOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56
    llvm#23 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14
    llvm#24 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48
    llvm#25 0x55e294646a8d in applySequenceBlock(mlir::Block&, mlir::transform::FailurePropagationMode, mlir::transform::TransformState&, mlir::transform::TransformResults&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:1788:15
    llvm#26 0x55e29464f927 in mlir::transform::NamedSequenceOp::apply(mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) mlir/lib/Dialect/Transform/IR/TransformOps.cpp:2155:10
    llvm#27 0x55e2945d28ee in mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Model<mlir::transform::NamedSequenceOp>::apply(mlir::transform::detail::TransformOpInterfaceInterfaceTraits::Concept const*, mlir::Operation*, mlir::transform::TransformRewriter&, mlir::transform::TransformResults&, mlir::transform::TransformState&) blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.h.inc:477:56
    llvm#28 0x55e297494a60 in apply blaze-out/k8-opt-asan/bin/mlir/include/mlir/Dialect/Transform/Interfaces/TransformInterfaces.cpp.inc:61:14
    llvm#29 0x55e297494a60 in mlir::transform::TransformState::applyTransform(mlir::transform::TransformOpInterface) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:953:48
    llvm#30 0x55e2974a5fe2 in mlir::transform::applyTransforms(mlir::Operation*, mlir::transform::TransformOpInterface, mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>> const&, mlir::transform::TransformOptions const&, bool) mlir/lib/Dialect/Transform/Interfaces/TransformInterfaces.cpp:2016:16
    llvm#31 0x55e2945888d7 in mlir::transform::applyTransformNamedSequence(mlir::RaggedArray<llvm::PointerUnion<mlir::Operation*, mlir::Attribute, mlir::Value>>, mlir::transform::TransformOpInterface, mlir::ModuleOp, mlir::transform::TransformOptions const&) mlir/lib/Dialect/Transform/Transforms/TransformInterpreterUtils.cpp:234:10
    llvm#32 0x55e294582446 in (anonymous namespace)::InterpreterPass::runOnOperation() mlir/lib/Dialect/Transform/Transforms/InterpreterPass.cpp:147:16
    llvm#33 0x55e2978e93c6 in operator() mlir/lib/Pass/Pass.cpp:527:17
    llvm#34 0x55e2978e93c6 in void llvm::function_ref<void ()>::callback_fn<mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int)::$_1>(long) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#35 0x55e2978e207a in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#36 0x55e2978e207a in executeAction<mlir::PassExecutionAction, mlir::Pass &> mlir/include/mlir/IR/MLIRContext.h:275:7
    llvm#37 0x55e2978e207a in mlir::detail::OpToOpPassAdaptor::run(mlir::Pass*, mlir::Operation*, mlir::AnalysisManager, bool, unsigned int) mlir/lib/Pass/Pass.cpp:521:21
    llvm#38 0x55e2978e5fbf in runPipeline mlir/lib/Pass/Pass.cpp:593:16
    llvm#39 0x55e2978e5fbf in mlir::PassManager::runPasses(mlir::Operation*, mlir::AnalysisManager) mlir/lib/Pass/Pass.cpp:904:10
    llvm#40 0x55e2978e5b65 in mlir::PassManager::run(mlir::Operation*) mlir/lib/Pass/Pass.cpp:884:60
    llvm#41 0x55e291ebb460 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:408:17
    llvm#42 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9
    llvm#43 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12
    llvm#44 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#45 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#46 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16
    llvm#47 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3
    llvm#48 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3
    llvm#49 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3
    llvm#50 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10
    llvm#51 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14

previously allocated by thread T0 here:
    #0 0x55e29130ab5d in operator new(unsigned long) compiler-rt/lib/asan/asan_new_delete.cpp:86:3
    #1 0x55e2979ed5d4 in __libcpp_operator_new<unsigned long>
    llvm#2 0x55e2979ed5d4 in __libcpp_allocate
    llvm#3 0x55e2979ed5d4 in allocate
    llvm#4 0x55e2979ed5d4 in __allocate_at_least<std::__u::allocator<mlir::BlockArgument> >
    llvm#5 0x55e2979ed5d4 in __split_buffer
    llvm#6 0x55e2979ed5d4 in mlir::BlockArgument* std::__u::vector<mlir::BlockArgument, std::__u::allocator<mlir::BlockArgument>>::__push_back_slow_path<mlir::BlockArgument const&>(mlir::BlockArgument const&)
    llvm#7 0x55e2979ec0f2 in push_back
    llvm#8 0x55e2979ec0f2 in mlir::Block::addArgument(mlir::Type, mlir::Location) mlir/lib/IR/Block.cpp:154:13
    llvm#9 0x55e29796e457 in parseRegionBody mlir/lib/AsmParser/Parser.cpp:2172:34
    llvm#10 0x55e29796e457 in (anonymous namespace)::OperationParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:2121:7
    llvm#11 0x55e29796b25e in (anonymous namespace)::CustomOpAsmParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1785:16
    llvm#12 0x55e297035742 in mlir::scf::ForOp::parse(mlir::OpAsmParser&, mlir::OperationState&) mlir/lib/Dialect/SCF/IR/SCF.cpp:521:14
    llvm#13 0x55e291322c18 in llvm::ParseResult llvm::detail::UniqueFunctionBase<llvm::ParseResult, mlir::OpAsmParser&, mlir::OperationState&>::CallImpl<llvm::ParseResult (*)(mlir::OpAsmParser&, mlir::OperationState&)>(void*, mlir::OpAsmParser&, mlir::OperationState&) llvm/include/llvm/ADT/FunctionExtras.h:220:12
    llvm#14 0x55e29795bea3 in operator() llvm/include/llvm/ADT/FunctionExtras.h:384:12
    llvm#15 0x55e29795bea3 in callback_fn<llvm::unique_function<llvm::ParseResult (mlir::OpAsmParser &, mlir::OperationState &)> > llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#16 0x55e29795bea3 in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#17 0x55e29795bea3 in parseOperation mlir/lib/AsmParser/Parser.cpp:1521:9
    llvm#18 0x55e29795bea3 in parseCustomOperation mlir/lib/AsmParser/Parser.cpp:2017:19
    llvm#19 0x55e29795bea3 in (anonymous namespace)::OperationParser::parseOperation() mlir/lib/AsmParser/Parser.cpp:1174:10
    llvm#20 0x55e297971d20 in parseBlockBody mlir/lib/AsmParser/Parser.cpp:2296:9
    llvm#21 0x55e297971d20 in (anonymous namespace)::OperationParser::parseBlock(mlir::Block*&) mlir/lib/AsmParser/Parser.cpp:2226:12
    llvm#22 0x55e29796e4f5 in parseRegionBody mlir/lib/AsmParser/Parser.cpp:2184:7
    llvm#23 0x55e29796e4f5 in (anonymous namespace)::OperationParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:2121:7
    llvm#24 0x55e29796b25e in (anonymous namespace)::CustomOpAsmParser::parseRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1785:16
    llvm#25 0x55e29796b2cf in (anonymous namespace)::CustomOpAsmParser::parseOptionalRegion(mlir::Region&, llvm::ArrayRef<mlir::OpAsmParser::Argument>, bool) mlir/lib/AsmParser/Parser.cpp:1796:12
    llvm#26 0x55e2978d89ff in mlir::function_interface_impl::parseFunctionOp(mlir::OpAsmParser&, mlir::OperationState&, bool, mlir::StringAttr, llvm::function_ref<mlir::Type (mlir::Builder&, llvm::ArrayRef<mlir::Type>, llvm::ArrayRef<mlir::Type>, mlir::function_interface_impl::VariadicFlag, std::__u::basic_string<char, std::__u::char_traits<char>, std::__u::allocator<char>>&)>, mlir::StringAttr, mlir::StringAttr) mlir/lib/Interfaces/FunctionImplementation.cpp:232:14
    llvm#27 0x55e2969ba41d in mlir::func::FuncOp::parse(mlir::OpAsmParser&, mlir::OperationState&) mlir/lib/Dialect/Func/IR/FuncOps.cpp:203:10
    llvm#28 0x55e291322c18 in llvm::ParseResult llvm::detail::UniqueFunctionBase<llvm::ParseResult, mlir::OpAsmParser&, mlir::OperationState&>::CallImpl<llvm::ParseResult (*)(mlir::OpAsmParser&, mlir::OperationState&)>(void*, mlir::OpAsmParser&, mlir::OperationState&) llvm/include/llvm/ADT/FunctionExtras.h:220:12
    llvm#29 0x55e29795bea3 in operator() llvm/include/llvm/ADT/FunctionExtras.h:384:12
    llvm#30 0x55e29795bea3 in callback_fn<llvm::unique_function<llvm::ParseResult (mlir::OpAsmParser &, mlir::OperationState &)> > llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#31 0x55e29795bea3 in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#32 0x55e29795bea3 in parseOperation mlir/lib/AsmParser/Parser.cpp:1521:9
    llvm#33 0x55e29795bea3 in parseCustomOperation mlir/lib/AsmParser/Parser.cpp:2017:19
    llvm#34 0x55e29795bea3 in (anonymous namespace)::OperationParser::parseOperation() mlir/lib/AsmParser/Parser.cpp:1174:10
    llvm#35 0x55e297959b78 in parse mlir/lib/AsmParser/Parser.cpp:2725:20
    llvm#36 0x55e297959b78 in mlir::parseAsmSourceFile(llvm::SourceMgr const&, mlir::Block*, mlir::ParserConfig const&, mlir::AsmParserState*, mlir::AsmParserCodeCompleteContext*) mlir/lib/AsmParser/Parser.cpp:2785:41
    llvm#37 0x55e29790d5c2 in mlir::parseSourceFile(std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::Block*, mlir::ParserConfig const&, mlir::LocationAttr*) mlir/lib/Parser/Parser.cpp:46:10
    llvm#38 0x55e291ebbfe2 in parseSourceFile<mlir::ModuleOp, const std::__u::shared_ptr<llvm::SourceMgr> &> mlir/include/mlir/Parser/Parser.h:159:14
    llvm#39 0x55e291ebbfe2 in parseSourceFile<mlir::ModuleOp> mlir/include/mlir/Parser/Parser.h:189:10
    llvm#40 0x55e291ebbfe2 in mlir::parseSourceFileForTool(std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::ParserConfig const&, bool) mlir/include/mlir/Tools/ParseUtilities.h:31:12
    llvm#41 0x55e291ebb263 in performActions(llvm::raw_ostream&, std::__u::shared_ptr<llvm::SourceMgr> const&, mlir::MLIRContext*, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:383:33
    llvm#42 0x55e291ebabd9 in processBuffer mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:481:9
    llvm#43 0x55e291ebabd9 in operator() mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:548:12
    llvm#44 0x55e291ebabd9 in llvm::LogicalResult llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>::callback_fn<mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&)::$_0>(long, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&) llvm/include/llvm/ADT/STLFunctionalExtras.h:45:12
    llvm#45 0x55e297b1cffe in operator() llvm/include/llvm/ADT/STLFunctionalExtras.h:68:12
    llvm#46 0x55e297b1cffe in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef)::$_0::operator()(llvm::StringRef) const mlir/lib/Support/ToolUtilities.cpp:86:16
    llvm#47 0x55e297b1c9c5 in interleave<const llvm::StringRef *, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), (lambda at llvm/include/llvm/ADT/STLExtras.h:2147:49), void> llvm/include/llvm/ADT/STLExtras.h:2125:3
    llvm#48 0x55e297b1c9c5 in interleave<llvm::SmallVector<llvm::StringRef, 8U>, (lambda at mlir/lib/Support/ToolUtilities.cpp:79:23), llvm::raw_ostream, llvm::StringRef> llvm/include/llvm/ADT/STLExtras.h:2147:3
    llvm#49 0x55e297b1c9c5 in mlir::splitAndProcessBuffer(std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::function_ref<llvm::LogicalResult (std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, llvm::raw_ostream&)>, llvm::raw_ostream&, llvm::StringRef, llvm::StringRef) mlir/lib/Support/ToolUtilities.cpp:89:3
    llvm#50 0x55e291eb0cf0 in mlir::MlirOptMain(llvm::raw_ostream&, std::__u::unique_ptr<llvm::MemoryBuffer, std::__u::default_delete<llvm::MemoryBuffer>>, mlir::DialectRegistry&, mlir::MlirOptMainConfig const&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:551:10
    llvm#51 0x55e291eb115c in mlir::MlirOptMain(int, char**, llvm::StringRef, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:589:14
    llvm#52 0x55e291eb15f8 in mlir::MlirOptMain(int, char**, llvm::StringRef, mlir::DialectRegistry&) mlir/lib/Tools/mlir-opt/MlirOptMain.cpp:605:10
    llvm#53 0x55e29130d1be in main mlir/tools/mlir-opt/mlir-opt.cpp:311:33
    llvm#54 0x7fbcf3fff3d3 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x613d3) (BuildId: 9a996398ce14a94560b0c642eb4f6e94)
    llvm#55 0x55e2912365a9 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120

SUMMARY: AddressSanitizer: heap-use-after-free mlir/include/mlir/IR/IRMapping.h:40:11 in map<llvm::MutableArrayRef<mlir::BlockArgument> &, llvm::MutableArrayRef<mlir::BlockArgument>, nullptr>
Shadow bytes around the buggy address:
  0x502000006a00: fa fa 00 fa fa fa 00 00 fa fa 00 fa fa fa 00 fa
  0x502000006a80: fa fa 00 fa fa fa 00 00 fa fa 00 00 fa fa 00 00
  0x502000006b00: fa fa 00 00 fa fa 00 00 fa fa 00 fa fa fa 00 fa
  0x502000006b80: fa fa 00 fa fa fa 00 fa fa fa 00 00 fa fa 00 00
  0x502000006c00: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa fd fa
=>0x502000006c80: fa fa fd fa fa fa fd fd fa fa fd[fd]fa fa fd fd
  0x502000006d00: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
  0x502000006d80: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
  0x502000006e00: fa fa 00 fa fa fa 00 fa fa fa 00 00 fa fa 00 fa
  0x502000006e80: fa fa 00 fa fa fa 00 00 fa fa 00 fa fa fa 00 fa
  0x502000006f00: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==4320==ABORTING
Discookie pushed a commit that referenced this pull request Aug 15, 2024
This test is currently flaky on a local Windows amd64 build. The reason
is that it relies on the order of `process.threads` but this order is
nondeterministic:

If we print lldb's inputs and outputs while running, we can see that the
breakpoints are always being set correctly, and always being hit:

```sh
runCmd: breakpoint set -f "main.c" -l 2
output: Breakpoint 1: where = a.out`func_inner + 1 at main.c:2:9, address = 0x0000000140001001

runCmd: breakpoint set -f "main.c" -l 7
output: Breakpoint 2: where = a.out`main + 17 at main.c:7:5, address = 0x0000000140001021

runCmd: run
output: Process 52328 launched: 'C:\workspace\llvm-project\llvm\build\lldb-test-build.noindex\functionalities\unwind\zeroth_frame\TestZerothFrame.test_dwarf\a.out' (x86_64)
Process 52328 stopped
* thread #1, stop reason = breakpoint 1.1
    frame #0: 0x00007ff68f6b1001 a.out`func_inner at main.c:2:9
   1    void func_inner() {
-> 2        int a = 1;  // Set breakpoint 1 here
                ^
   3    }
   4
   5    int main() {
   6        func_inner();
   7        return 0; // Set breakpoint 2 here
```

However, sometimes the backtrace printed in this test shows that the
process is stopped inside NtWaitForWorkViaWorkerFactory from
`ntdll.dll`:

```sh
Backtrace at the first breakpoint:
frame #0: 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
frame #1: 0x00007ffecc74585e ntdll.dll`RtlClearThreadWorkOnBehalfTicket + 862
frame llvm#2: 0x00007ffecc3e257d kernel32.dll`BaseThreadInitThunk + 29
frame llvm#3: 0x00007ffecc76af28 ntdll.dll`RtlUserThreadStart + 40
```

When this happens, the test fails with an assertion error that the
stopped thread's zeroth frame's current line number does not match the
expected line number. This is because the test is looking at the wrong
thread: `process.threads[0]`.

If we print the list of threads each time the test is run, we notice
that threads are sometimes in a different order, within
`process.threads`:

```sh
Thread 0: thread llvm#4: tid = 0x9c38, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
Thread 1: thread llvm#2: tid = 0xa950, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
Thread 2: thread #1: tid = 0xab18, 0x00007ff64bc81001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1
Thread 3: thread llvm#3: tid = 0xc514, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20

Thread 0: thread llvm#3: tid = 0x018c, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
Thread 1: thread #1: tid = 0x85c8, 0x00007ff7130c1001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1
Thread 2: thread llvm#2: tid = 0xf344, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
Thread 3: thread llvm#4: tid = 0x6a50, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20
```

Use `self.thread()` to consistently select the correct thread, instead.

Co-authored-by: kendal <[email protected]>
Discookie pushed a commit that referenced this pull request Aug 15, 2024
…izations of function templates to USRGenerator (llvm#98027)

Given the following:
```
template<typename T>
struct A
{
    void f(int); // #1
    
    template<typename U>
    void f(U); // llvm#2
    
    template<>
    void f<int>(int); // llvm#3
};
```
Clang will generate the same USR for `#1` and `llvm#2`. This patch fixes the
issue by including the template arguments of dependent class scope
explicit specializations in their USRs.
Discookie pushed a commit that referenced this pull request Aug 15, 2024
This patch adds a frame recognizer for Clang's
`__builtin_verbose_trap`, which behaves like a
`__builtin_trap`, but emits a failure-reason string into debug-info in
order for debuggers to display
it to a user.

The frame recognizer triggers when we encounter
a frame with a function name that begins with
`__clang_trap_msg`, which is the magic prefix
Clang emits into debug-info for verbose traps.
Once such frame is encountered we display the
frame function name as the `Stop Reason` and display that frame to the
user.

Example output:
```
(lldb) run
warning: a.out was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 35942 launched: 'a.out' (arm64)
Process 35942 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented
    frame #1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt]
   1    struct Dummy {
   2      void func() {
-> 3        __builtin_verbose_trap("Misc.", "Function is not implemented");
   4      }
   5    };
   6
   7    int main() {
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Misc.: Function is not implemented
    frame #0: 0x0000000100003fa4 a.out`main [inlined] __clang_trap_msg$Misc.$Function is not implemented$ at verbose_trap.cpp:0 [opt]
  * frame #1: 0x0000000100003fa4 a.out`main [inlined] Dummy::func(this=<unavailable>) at verbose_trap.cpp:3:5 [opt]
    frame llvm#2: 0x0000000100003fa4 a.out`main at verbose_trap.cpp:8:13 [opt]
    frame llvm#3: 0x0000000189d518b4 dyld`start + 1988
```
Discookie pushed a commit that referenced this pull request Sep 2, 2024
…lvm#104148)

`hasOperands` does not always execute matchers in the order they are
written. This can cause issue in code using bindings when one operand
matcher is relying on a binding set by the other. With this change, the
first matcher present in the code is always executed first and any
binding it sets are available to the second matcher.

Simple example with current version (1 match) and new version (2
matches):
```bash
> cat tmp.cpp
int a = 13;
int b = ((int) a) - a;
int c = a - ((int) a);

> clang-query tmp.cpp
clang-query> set traversal IgnoreUnlessSpelledInSource
clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d"))))))

Match #1:

tmp.cpp:1:1: note: "d" binds here
int a = 13;
^~~~~~~~~~
tmp.cpp:2:9: note: "root" binds here
int b = ((int)a) - a;
        ^~~~~~~~~~~~
1 match.

> ./build/bin/clang-query tmp.cpp
clang-query> set traversal IgnoreUnlessSpelledInSource
clang-query> m binaryOperator(hasOperands(cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))), declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d"))))))

Match #1:

tmp.cpp:1:1: note: "d" binds here
    1 | int a = 13;
      | ^~~~~~~~~~
tmp.cpp:2:9: note: "root" binds here
    2 | int b = ((int)a) - a;
      |         ^~~~~~~~~~~~

Match llvm#2:

tmp.cpp:1:1: note: "d" binds here
    1 | int a = 13;
      | ^~~~~~~~~~
tmp.cpp:3:9: note: "root" binds here
    3 | int c = a - ((int)a);
      |         ^~~~~~~~~~~~
2 matches.
```

If this should be documented or regression tested anywhere please let me
know where.
Discookie pushed a commit that referenced this pull request Sep 2, 2024
…104523)

Compilers and language runtimes often use helper functions that are
fundamentally uninteresting when debugging anything but the
compiler/runtime itself. This patch introduces a user-extensible
mechanism that allows for these frames to be hidden from backtraces and
automatically skipped over when navigating the stack with `up` and
`down`.

This does not affect the numbering of frames, so `f <N>` will still
provide access to the hidden frames. The `bt` output will also print a
hint that frames have been hidden.

My primary motivation for this feature is to hide thunks in the Swift
programming language, but I'm including an example recognizer for
`std::function::operator()` that I wished for myself many times while
debugging LLDB.

rdar://126629381


Example output. (Yes, my proof-of-concept recognizer could hide even
more frames if we had a method that returned the function name without
the return type or I used something that isn't based off regex, but it's
really only meant as an example).

before:
```
(lldb) thread backtrace --filtered=false
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10
    frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25
    frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12
    frame llvm#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12
    frame llvm#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10
    frame llvm#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12
    frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10
    frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10
    frame llvm#8: 0x0000000183cdf154 dyld`start + 2476
(lldb) 
```

after

```
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10
    frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25
    frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12
    frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10
    frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10
    frame llvm#8: 0x0000000183cdf154 dyld`start + 2476
Note: Some frames were hidden by frame recognizers
```
Discookie pushed a commit that referenced this pull request Sep 2, 2024
`JITDylibSearchOrderResolver` local variable can be destroyed before
completion of all callbacks. Capture it together with `Deps` in
`OnEmitted` callback.

Original error:

```
==2035==ERROR: AddressSanitizer: stack-use-after-return on address 0x7bebfa155b70 at pc 0x7ff2a9a88b4a bp 0x7bec08d51980 sp 0x7bec08d51978
READ of size 8 at 0x7bebfa155b70 thread T87 (tf_xla-cpu-llvm)
    #0 0x7ff2a9a88b49 in operator() llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:58
    #1 0x7ff2a9a88b49 in __invoke<(lambda at llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:9) &, const llvm::DenseMap<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib *, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > &> libcxx/include/__type_traits/invoke.h:149:25
    llvm#2 0x7ff2a9a88b49 in __call<(lambda at llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp:55:9) &, const llvm::DenseMap<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> >, llvm::DenseMapInfo<llvm::orc::JITDylib *, void>, llvm::detail::DenseMapPair<llvm::orc::JITDylib *, llvm::DenseSet<llvm::orc::SymbolStringPtr, llvm::DenseMapInfo<llvm::orc::SymbolStringPtr, void> > > > &> libcxx/include/__type_traits/invoke.h:224:5
    llvm#3 0x7ff2a9a88b49 in operator() libcxx/include/__functional/function.h:210:12
    llvm#4 0x7ff2a9a88b49 in void std::__u::__function::__policy_invoker<void (llvm::DenseMap<llvm::orc::JITDylib*, llvm::DenseSet<llvm::orc::SymbolStringPtr,
```
Discookie pushed a commit that referenced this pull request Sep 26, 2024
…ap (llvm#108825)

This attempts to improve user-experience when LLDB stops on a
verbose_trap. Currently if a `__builtin_verbose_trap` triggers, we
display the first frame above the call to the verbose_trap. So in the
newly added test case, we would've previously stopped here:
```
(lldb) run
Process 28095 launched: '/Users/michaelbuch/a.out' (arm64)
Process 28095 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access
    frame #1: 0x0000000100003f5c a.out`std::__1::vector<int>::operator[](this=0x000000016fdfebef size=0, (null)=10) at verbose_trap.cpp:6:9
   3    template <typename T>
   4    struct vector {
   5        void operator[](unsigned) {
-> 6            __builtin_verbose_trap("Bounds error", "out-of-bounds access");
   7        }
   8    };
```

After this patch, we would stop in the first non-`std` frame:
```
(lldb) run
Process 27843 launched: '/Users/michaelbuch/a.out' (arm64)
Process 27843 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access
    frame llvm#2: 0x0000000100003f44 a.out`g() at verbose_trap.cpp:14:5
   11  
   12   void g() {
   13       std::vector<int> v;
-> 14       v[10];
   15   }
   16  
```

rdar://134490328
Discookie pushed a commit that referenced this pull request Sep 26, 2024
Random testing found that the Z3 wrapper does not support UnarySymExpr,
which was added recently and not included in the original Z3 wrapper.
For now, just avoid submitting expressions to Z3 to avoid compiler
crashes.

Some crash context ...

clang -cc1 -analyze -analyzer-checker=core z3-unarysymexpr.c
-analyzer-constraints=z3

Unsupported expression to reason about!
UNREACHABLE executed at
clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h:297!

Stack dump:
3. <root>/clang/test/Analysis/z3-unarysymexpr.c:13:7: Error evaluating
branch #0 <addr> llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) #1
<addr> llvm::sys::RunSignalHandlers() llvm#8 <addr>
clang::ento::SimpleConstraintManager::assumeAux(
llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>,
clang::ento::NonLoc, bool) llvm#9 <addr>
clang::ento::SimpleConstraintManager::assume(
llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>,
clang::ento::NonLoc, bool)

Co-authored-by: einvbri <[email protected]>
Discookie pushed a commit that referenced this pull request Sep 26, 2024
…llvm#94981)

This extends default argument deduction to cover class templates as
well, applying only to partial ordering, adding to the provisional
wording introduced in llvm#89807.

This solves some ambuguity introduced in P0522 regarding how template
template parameters are partially ordered, and should reduce the
negative impact of enabling `-frelaxed-template-template-args` by
default.

Given the following example:
```C++
template <class T1, class T2 = float> struct A;
template <class T3> struct B;

template <template <class T4> class TT1, class T5> struct B<TT1<T5>>;   // #1
template <class T6, class T7>                      struct B<A<T6, T7>>; // llvm#2

template struct B<A<int>>;
```
Prior to P0522, `llvm#2` was picked. Afterwards, this became ambiguous. This
patch restores the pre-P0522 behavior, `llvm#2` is picked again.
Discookie pushed a commit that referenced this pull request Oct 16, 2024
…ext is not fully initialized (llvm#110481)

As this comment around target initialization implies:
```
  // This can be NULL if we don't know anything about the architecture or if
  // the target for an architecture isn't enabled in the llvm/clang that we
  // built
```

There are cases where we might fail to call `InitBuiltinTypes` when
creating the backing `ASTContext` for a `TypeSystemClang`. If that
happens, the builtins `QualType`s, e.g., `VoidPtrTy`/`IntTy`/etc., are
not initialized and dereferencing them as we do in
`GetBuiltinTypeForEncodingAndBitSize` (and other places) will lead to
nullptr-dereferences. Example backtrace:
```
(lldb) run
Assertion failed: (!isNull() && "Cannot retrieve a NULL type pointer"), function getCommonPtr, file Type.h, line 958.
Process 2680 stopped
* thread llvm#15, name = '<lldb.process.internal-state(pid=2712)>', stop reason = hit program assert
    frame llvm#4: 0x000000010cdf3cdc liblldb.20.0.0git.dylib`DWARFASTParserClang::ExtractIntFromFormValue(lldb_private::CompilerType const&, lldb_private::plugin::dwarf::DWARFFormValue const&) const (.cold.1) + 
liblldb.20.0.0git.dylib`DWARFASTParserClang::ParseObjCMethod(lldb_private::ObjCLanguage::MethodName const&, lldb_private::plugin::dwarf::DWARFDIE const&, lldb_private::CompilerType, ParsedDWARFTypeAttributes
, bool) (.cold.1):
->  0x10cdf3cdc <+0>:  stp    x29, x30, [sp, #-0x10]!
    0x10cdf3ce0 <+4>:  mov    x29, sp
    0x10cdf3ce4 <+8>:  adrp   x0, 545
    0x10cdf3ce8 <+12>: add    x0, x0, #0xa25 ; "ParseObjCMethod"
Target 0: (lldb) stopped.
(lldb) bt
* thread llvm#15, name = '<lldb.process.internal-state(pid=2712)>', stop reason = hit program assert
    frame #0: 0x0000000180d08600 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x0000000180d40f50 libsystem_pthread.dylib`pthread_kill + 288
    frame llvm#2: 0x0000000180c4d908 libsystem_c.dylib`abort + 128
    frame llvm#3: 0x0000000180c4cc1c libsystem_c.dylib`__assert_rtn + 284
  * frame llvm#4: 0x000000010cdf3cdc liblldb.20.0.0git.dylib`DWARFASTParserClang::ExtractIntFromFormValue(lldb_private::CompilerType const&, lldb_private::plugin::dwarf::DWARFFormValue const&) const (.cold.1) + 
    frame llvm#5: 0x0000000109d30acc liblldb.20.0.0git.dylib`lldb_private::TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding, unsigned long) + 1188
    frame llvm#6: 0x0000000109aaaed4 liblldb.20.0.0git.dylib`DynamicLoaderMacOS::NotifyBreakpointHit(void*, lldb_private::StoppointCallbackContext*, unsigned long long, unsigned long long) + 384
```

This patch adds a one-time user-visible warning for when we fail to
initialize the AST to indicate that initialization went wrong for the
given target. Additionally, we add checks for whether one of the
`ASTContext` `QualType`s is invalid before dereferencing any builtin
types.

The warning would look as follows:
```
(lldb) target create "a.out"
Current executable set to 'a.out' (arm64).
(lldb) b main
warning: Failed to initialize builtin ASTContext types for target 'some-unknown-triple'. Printing variables may behave unexpectedly.
Breakpoint 1: where = a.out`main + 8 at stepping.cpp:5:14, address = 0x0000000100003f90
```

rdar://134869779
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants