https://github.com/steakhal updated 
https://github.com/llvm/llvm-project/pull/173186

From c7a8ebeab0e2abbf6beca457247ed0080860afef Mon Sep 17 00:00:00 2001
From: Laurent Bonnans <[email protected]>
Date: Fri, 19 Dec 2025 14:54:21 +0100
Subject: [PATCH 1/6] [analyzer] Support pack indexing expressions

Analyzer used to crash when visiting the unsubstituted DeclRefExpr
nodes here. Instead we skip them and process them in a dedicated pack
indexing transfer function.
---
 .../Core/PathSensitive/ExprEngine.h           |  4 ++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  | 41 ++++++++++++++++++-
 clang/test/Analysis/pack_indexing.cpp         | 36 ++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Analysis/pack_indexing.cpp

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index d184986cda15d..2d96d668d9f7e 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -506,6 +506,10 @@ class ExprEngine {
   void VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
                      ExplodedNodeSet &Dst);
 
+  /// VisitPackIndexingExpr - Transfer function logic for C++26 pack indexing
+  void VisitPackIndexingExpr(const PackIndexingExpr *E, ExplodedNode *Pred,
+                             ExplodedNodeSet &Dst);
+
   /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose
   void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R,
                         ExplodedNode *Pred, ExplodedNodeSet &Dst);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index c8dc5b6e81b16..573580fedaa25 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1740,7 +1740,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::RecoveryExprClass:
     case Stmt::CXXNoexceptExprClass:
     case Stmt::PackExpansionExprClass:
-    case Stmt::PackIndexingExprClass:
     case Stmt::SubstNonTypeTemplateParmPackExprClass:
     case Stmt::FunctionParmPackExprClass:
     case Stmt::CoroutineBodyStmtClass:
@@ -2292,6 +2291,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       Bldr.addNodes(Dst);
       break;
 
+    case Stmt::PackIndexingExprClass: {
+      Bldr.takeNodes(Pred);
+      VisitPackIndexingExpr(cast<PackIndexingExpr>(S), Pred, Dst);
+      Bldr.addNodes(Dst);
+      break;
+    }
+
     case Stmt::ImplicitCastExprClass:
     case Stmt::CStyleCastExprClass:
     case Stmt::CXXStaticCastExprClass:
@@ -3296,6 +3302,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
 
     SVal V = UnknownVal();
 
+    // For pack indexing expressions. Binding is not available here but through
+    // the expanded expressions in the PackIndexingExpr node.
+    if (BD->isParameterPack()) {
+      // FIXME: We should meaningfully implement this.
+      return;
+    }
+
     // Handle binding to data members
     if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) {
       const auto *Field = cast<FieldDecl>(ME->getMemberDecl());
@@ -3346,6 +3359,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
     return;
   }
 
+  if (const auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(D)) {
+    // FIXME: We should meaningfully implement this.
+    (void)NTTPD;
+    return;
+  }
+
   llvm_unreachable("Support for this Decl not implemented.");
 }
 
@@ -3447,6 +3466,26 @@ void ExprEngine::VisitArrayInitLoopExpr(const 
ArrayInitLoopExpr *Ex,
   getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
 }
 
+void ExprEngine::VisitPackIndexingExpr(const PackIndexingExpr *E,
+                                       ExplodedNode *Pred,
+                                       ExplodedNodeSet &Dst) {
+  assert(E->isFullySubstituted() && "unsubstituted pack indexing expression");
+
+  if (const auto *DE = dyn_cast<DeclRefExpr>(E->getSelectedExpr())) {
+    VisitCommonDeclRefExpr(E, DE->getDecl(), Pred, Dst);
+  } else if (const auto *SNTTPE =
+                 dyn_cast<SubstNonTypeTemplateParmExpr>(E->getSelectedExpr())) 
{
+    (void)SNTTPE;
+    // FIXME: handle this case
+    StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+    const ExplodedNode *node = Bldr.generateSink(E, Pred, Pred->getState());
+    Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+  } else {
+    llvm_unreachable(
+        "Unexpected selected expression in pack indexing expression");
+  }
+}
+
 /// VisitArraySubscriptExpr - Transfer function for array accesses
 void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
                                              ExplodedNode *Pred,
diff --git a/clang/test/Analysis/pack_indexing.cpp 
b/clang/test/Analysis/pack_indexing.cpp
new file mode 100644
index 0000000000000..6217248f48771
--- /dev/null
+++ b/clang/test/Analysis/pack_indexing.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -std=c++26 -verify %s
+
+void clang_analyzer_eval(bool);
+
+template <class T>
+constexpr decltype(auto) get0(const T& val) noexcept {
+    auto& [...members] = val;
+    auto&& r = members...[0]; // no-crash
+    return r;
+}
+
+struct A {
+    int a;
+};
+
+void no_crash_negative() {
+    const int& x = get0(A{1});
+    clang_analyzer_eval(x == 1);
+}
+
+void uninitialized() {
+    A a;
+    const int& x = get0(a);
+    clang_analyzer_eval(x == 0); // expected-warning{{The left operand of '==' 
is a garbage value}}
+}
+
+template <int I, auto...Ts>
+int index_template_pack()
+{
+  return Ts...[I]; // no-crash
+}
+
+void template_pack_no_crash()
+{
+  int r = index_template_pack<2, 0, 1, 42>();
+}

From 676b6965d61d6958251e15334060cdbb8dbe6267 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Wed, 25 Feb 2026 10:27:41 +0000
Subject: [PATCH 2/6] Sema fixes

---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 38 +++++++++++++++++++
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  8 ++++
 2 files changed, 46 insertions(+)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e74c41517ecbf..c57432c5e93a2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1685,6 +1685,40 @@ 
TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
 }
 
 Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
+  // Note: Here we "instantiate" a BindingDecl.
+  //
+  // Currently it basically creates a new one with the same internal content.
+  // So, the address of the BindingDecl will be different in the template
+  // specialization, but the content would be the same. This also means
+  // that it will inherit the "dependent-type" of that original BindingDecl
+  // (because that was in the primary template, thus it was in dependent
+  // context) - but in a fully specialized template there shouldn't be anything
+  // with "dependent-type" (I think).
+  // https://github.com/llvm/llvm-project/issues/182691#issuecomment-3939539109
+
+  // llvm::errs() << "binding decl type:\n";
+  // D->getType()->dump();
+  // QualType Ty = D->getType(); // <-- this one
+
+  // TODO: How to instantiate the Decl type to get rid of the "dependent-type"?
+
+  // Some copypaste from other places to see if it would work. Spoiler: NO.
+#if 0
+  if (const auto *ExpTy = Ty->getAs<PackExpansionType>()) {
+    QualType Pattern = ExpTy->getPattern();
+    QualType T =
+        SubstType(Pattern, TemplateArgs, D->getLocation(), D->getDeclName());
+
+    TemplateInstantiator Instantiator(
+        *this, TemplateArgs, Loc, Entity,
+        /*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr);
+    QualType QT = Instantiator.TransformType(T);
+    if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
+      *IsIncompleteSubstitution = true;
+    return QT;
+  }
+#endif
+
   auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
                                     D->getIdentifier(), D->getType());
   NewBD->setReferenced(D->isReferenced());
@@ -1708,6 +1742,10 @@ Decl 
*TemplateDeclInstantiator::VisitDecompositionDecl(DecompositionDecl *D) {
     NewBindings.push_back(cast<BindingDecl>(VisitBindingDecl(OldBD)));
   }
   ArrayRef<BindingDecl*> NewBindingArray = NewBindings;
+  // llvm::errs() << "instantiating VisitDecompositionDecl\n";
+  // for (const BindingDecl *B : NewBindings) {
+  //   B->dumpColor();
+  // }
 
   auto *NewDD = cast_if_present<DecompositionDecl>(
       VisitVarDecl(D, /*InstantiatingVarTemplate=*/false, &NewBindingArray));
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp 
b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..5d66194d6c113 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1375,6 +1375,14 @@ ExprResult Sema::BuildPackIndexingExpr(Expr 
*PackExpression,
     }
   }
 
+  // Note: Without this, we would inject the pack-expression of the primary
+  // template to the template specialization - which is not great!
+  // https://github.com/llvm/llvm-project/issues/182691#issuecomment-3939486419
+  // With this fix, I think it should work now.
+  // TODO: Add tests and check everything!
+  if (FullySubstituted)
+    PackExpression = ExpandedExprs[Index.value()];
+
   return PackIndexingExpr::Create(getASTContext(), EllipsisLoc, RSquareLoc,
                                   PackExpression, IndexExpr, Index,
                                   ExpandedExprs, FullySubstituted);

From bc5033429d4ea3bc96fa15d521a6f3990056aa85 Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Wed, 25 Feb 2026 10:55:10 +0000
Subject: [PATCH 3/6] Patches for LiveVariables

---
 clang/lib/Analysis/LiveVariables.cpp | 79 ++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/clang/lib/Analysis/LiveVariables.cpp 
b/clang/lib/Analysis/LiveVariables.cpp
index 74b930bf26fb6..e5930c42efc40 100644
--- a/clang/lib/Analysis/LiveVariables.cpp
+++ b/clang/lib/Analysis/LiveVariables.cpp
@@ -393,6 +393,8 @@ void TransferFunctions::VisitBinaryOperator(BinaryOperator 
*B) {
           if (const auto *HV = BD->getHoldingVar())
             val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV);
 
+          // TODO: Handle pack-exprs here.
+
           val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD);
         }
       } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
@@ -413,11 +415,78 @@ void TransferFunctions::VisitBlockExpr(BlockExpr *BE) {
   }
 }
 
+// Note: Liveness analysis
+// (https://en.wikipedia.org/wiki/Live-variable_analysis) These traverse
+// function go element-by-element backwards in each CFG block and basically
+// tracks what is "alive".
+//  - A Decl is marked alive if it's "used". (i.e. remember that the variable
+//    shouldn't raise an -Wunused warning)
+//  - A Decl is "killed" when it's assigned or initialized. (i.e. remember that
+//    whatever the previous value of this variable was, it does not matter,
+//    because here we just overwritten it)
+
+// Example: https://godbolt.org/z/PnGvsEeYa
+// \code
+//   template <class = void>
+//   int templated_fn() {
+//       struct S { int a; long b; };
+//       auto [... members] = S{12, 5};
+//       return members...[1];
+//   }
+// \endcode
+// Is has an AST of the template specialization (instantiation) would be this:
+// clang-format off
+// \code
+//   |-DeclStmt 0x1f25d6a0 <line:15:5, col:34>
+//   | `-DecompositionDecl 0x1f25d118 <col:5, col:33> col:10 used 'S' cinit
+//   |   |-CXXFunctionalCastExpr 0x1f25d3c0 <col:26, col:33> 'S' functional 
cast to S <NoOp>
+//   |   | `-InitListExpr 0x1f25d218 <col:27, col:33> 'S'
+//   |   |   |-IntegerLiteral 0x1f237bb0 <col:28> 'int' 12
+//   |   |   `-ImplicitCastExpr 0x1f25d268 <col:32> 'long' <IntegralCast>
+//   |   |     `-IntegerLiteral 0x1f237bd0 <col:32> 'int' 5
+//   |   `-BindingDecl 0x1f25d0c8 <col:15> col:15 referenced members 
'<dependent type>...'   <-- Why is it still "<dependent-type>"? It should be 
"int".
+//   |     `-FunctionParmPackExpr 0x1f25d5d0 <col:15> '<dependent type>...' 
lvalue
+//   `-ReturnStmt 0x1f25d878 <line:20:5, col:24>
+//     `-ImplicitCastExpr 0x1f25d780 <col:12, col:24> 'int' <IntegralCast>
+//       `-ImplicitCastExpr 0x1f25d768 <col:12, col:24> 'long' <LValueToRValue>
+//         `-PackIndexingExpr 0x1f25d730 <col:12, col:24> 'long' lvalue
+//           |-DeclRefExpr 0x1f237cb8 <col:12> '<dependent type>' lvalue 
Binding 0x1f237a98 'members' '<dependent type>...' <-- Refers to the 
BindingDecl inside the primary template, this is BAD!
+//           `-ConstantExpr 0x1f25d710 <col:23> '__size_t':'unsigned long'     
                                                 This should have the address 
of "0x1f25d0c8", and point to the BindingDecl inside the specialization.
+//             |-value: Int 1                                                  
                                                 But even then, the type of 
that Decl would be wrong.
+//             `-ImplicitCastExpr 0x1f25d6f8 <col:23> '__size_t':'unsigned 
long' <IntegralCast>
+//               `-IntegerLiteral 0x1f237cd8 <col:23> 'int' 1
+// \code
+// clang-format on
+
+// Note: DeclRefExpr represents a "use".
 void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) {
   const Decl* D = DR->getDecl();
   bool InAssignment = LV.inAssignment.contains(DR);
   if (const auto *BD = dyn_cast<BindingDecl>(D)) {
     if (!InAssignment) {
+      llvm::errs() << "visiting decl ref expr:\n";
+      DR->dump();
+      if (BD->isParameterPack()) {
+        llvm::errs() << "Visited decl ref expr of a parm pack expr:\n";
+        BD->getBinding()->dump();
+        // The binding will be basically the field access:
+        // "members...[1]" is basically a desugared "members.b", which is
+        // MemberExpr of a DeclRefExpr.
+        if (const auto *Mem = dyn_cast<MemberExpr>(BD->getBinding())) {
+          if (const auto *DRE = dyn_cast<DeclRefExpr>(Mem->getBase())) {
+            // This thing should refer to a DecompositionDecl
+            // "auto [...members]". Which is basically the hidden variable.
+            if (const auto *Decomp =
+                    dyn_cast<DecompositionDecl>(DRE->getDecl())) {
+              llvm::errs() << "Making it alive: " << Decomp << "\n";
+              // This should refer to the DecompositionDecl of the template
+              // specialization.
+              val.liveDecls = LV.DSetFact.add(val.liveDecls, Decomp);
+            }
+          }
+        }
+      }
+
       if (const auto *HV = BD->getHoldingVar())
         val.liveDecls = LV.DSetFact.add(val.liveDecls, HV);
 
@@ -429,6 +498,7 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) {
   }
 }
 
+// Note: DeclStmt represents a "kill".
 void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
   for (const auto *DI : DS->decls()) {
     if (const auto *DD = dyn_cast<DecompositionDecl>(DI)) {
@@ -436,6 +506,15 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
         if (const auto *HV = BD->getHoldingVar())
           val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV);
 
+        // For DecompositionDecls of packs we should jsut kill that decl.
+        // This decl must have the same address that the DeclRefExpr had!
+        // Otherwise it wouldn't do the right thing.
+        if (BD->isParameterPack()) {
+          llvm::errs() << "Making it dead: " << DD << "\n";
+          auto prev = val.liveDecls;
+          val.liveDecls = LV.DSetFact.remove(val.liveDecls, DD);
+        }
+
         val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD);
       }
 

From a501f09cd6fcc5594a904f8d6d434f8575f1f6aa Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Wed, 25 Feb 2026 11:02:30 +0000
Subject: [PATCH 4/6] Patches for ExprEngine::VisitCommonDeclRefExpr and
 VisitPackIndexingExpr

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 24 +++++++-------------
 1 file changed, 8 insertions(+), 16 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 4cc743a413ecb..1b52598a45911 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3300,10 +3300,11 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, 
const NamedDecl *D,
 
     SVal V = UnknownVal();
 
-    // For pack indexing expressions. Binding is not available here but through
-    // the expanded expressions in the PackIndexingExpr node.
     if (BD->isParameterPack()) {
-      // FIXME: We should meaningfully implement this.
+      // Just bind the lvalue of the decomp decl to the expr.
+      V = state->getLValue(DD, LCtx);
+      Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
+                        ProgramPoint::PostLValueKind);
       return;
     }
 
@@ -3469,19 +3470,10 @@ void ExprEngine::VisitPackIndexingExpr(const 
PackIndexingExpr *E,
                                        ExplodedNodeSet &Dst) {
   assert(E->isFullySubstituted() && "unsubstituted pack indexing expression");
 
-  if (const auto *DE = dyn_cast<DeclRefExpr>(E->getSelectedExpr())) {
-    VisitCommonDeclRefExpr(E, DE->getDecl(), Pred, Dst);
-  } else if (const auto *SNTTPE =
-                 dyn_cast<SubstNonTypeTemplateParmExpr>(E->getSelectedExpr())) 
{
-    (void)SNTTPE;
-    // FIXME: handle this case
-    StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
-    const ExplodedNode *node = Bldr.generateSink(E, Pred, Pred->getState());
-    Engine.addAbortedBlock(node, currBldrCtx->getBlock());
-  } else {
-    llvm_unreachable(
-        "Unexpected selected expression in pack indexing expression");
-  }
+  NodeBuilder Builder(Pred, Dst, *currBldrCtx);
+  Builder.takeNodes(Pred);
+  VisitCommonDeclRefExpr(E, E->getPackDecl(), Pred, Dst);
+  Builder.addNodes(Dst);
 }
 
 /// VisitArraySubscriptExpr - Transfer function for array accesses

From a47bd9d108be2a8492b55ae49c739fa6c02c2cee Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Wed, 25 Feb 2026 11:03:33 +0000
Subject: [PATCH 5/6] Debug prints for debugging liveness and the Store

---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 6ec66298e8c45..020818a5258a1 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -3082,13 +3082,22 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() {
 StoreRef RegionStoreManager::removeDeadBindings(Store store,
                                                 const StackFrameContext *LCtx,
                                                 SymbolReaper& SymReaper) {
+  // static int counter = 0;
+  // ++counter;
+  // if (counter == 7) {
+  //   counter = 100;
+  // }
+  // llvm::errs() << "\nremoveDeadBindings\n";
   RegionBindingsRef B = getRegionBindings(store);
   RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
   W.GenerateClusters();
 
+  // llvm::errs() << "SymbolReaper root (live) regions:\n";
+
   // Enqueue the region roots onto the worklist.
   for (const MemRegion *Reg : SymReaper.regions()) {
     W.AddToWorkList(Reg);
+    // llvm::errs() << "  " << Reg << "\n";
   }
 
   do W.RunWorkList(); while (W.UpdatePostponed());
@@ -3099,8 +3108,11 @@ StoreRef RegionStoreManager::removeDeadBindings(Store 
store,
   for (const MemRegion *Base : llvm::make_first_range(B)) {
     // If the cluster has been visited, we know the region has been marked.
     // Otherwise, remove the dead entry.
-    if (!W.isVisited(Base))
+    if (!W.isVisited(Base)) {
       B = B.removeCluster(Base);
+      // llvm::errs() << "Base was not visited, so removing it: " << Base <<
+      // "\n";
+    }
   }
 
   return StoreRef(B.asStore(), *this);

From d3a53b41b2fa66b58c750f8cddc9a2f83bd03cea Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Wed, 25 Feb 2026 11:50:00 +0000
Subject: [PATCH 6/6] Fix the tests and ignore NTTP packs

---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 13 ++++++++---
 clang/test/Analysis/pack_indexing.cpp        | 23 ++++++++++++--------
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1b52598a45911..0239a565ae06d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3470,10 +3470,17 @@ void ExprEngine::VisitPackIndexingExpr(const 
PackIndexingExpr *E,
                                        ExplodedNodeSet &Dst) {
   assert(E->isFullySubstituted() && "unsubstituted pack indexing expression");
 
-  NodeBuilder Builder(Pred, Dst, *currBldrCtx);
-  Builder.takeNodes(Pred);
+  // For now, just bind Unknown for indexing NTTP pack exprs.
+  // TODO: Properly implement this.
+  if (isa<SubstNonTypeTemplateParmExpr>(E->getPackIdExpression())) {
+    NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+    ProgramStateRef State = Pred->getState();
+    const auto *LCtx = Pred->getLocationContext();
+    Bldr.generateNode(E, Pred, State->BindExpr(E, LCtx, UnknownVal()));
+    return;
+  }
+
   VisitCommonDeclRefExpr(E, E->getPackDecl(), Pred, Dst);
-  Builder.addNodes(Dst);
 }
 
 /// VisitArraySubscriptExpr - Transfer function for array accesses
diff --git a/clang/test/Analysis/pack_indexing.cpp 
b/clang/test/Analysis/pack_indexing.cpp
index 6217248f48771..358344eb19c20 100644
--- a/clang/test/Analysis/pack_indexing.cpp
+++ b/clang/test/Analysis/pack_indexing.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core -std=c++26 -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection 
-std=c++26 -verify %s
 
-void clang_analyzer_eval(bool);
+template <class T> void clang_analyzer_dump(T);
 
 template <class T>
 constexpr decltype(auto) get0(const T& val) noexcept {
@@ -15,22 +15,27 @@ struct A {
 
 void no_crash_negative() {
     const int& x = get0(A{1});
-    clang_analyzer_eval(x == 1);
+    clang_analyzer_dump(x); // expected-warning {{1 S32b}}
 }
 
 void uninitialized() {
     A a;
     const int& x = get0(a);
-    clang_analyzer_eval(x == 0); // expected-warning{{The left operand of '==' 
is a garbage value}}
+    clang_analyzer_dump(x); // expected-warning {{1st function call argument 
is an uninitialized value}}
+}
+
+void initialized() {
+    A a;
+    a.a = 4;
+    const int& x = get0(a);
+    clang_analyzer_dump(x); // expected-warning {{4 S32b}}
 }
 
 template <int I, auto...Ts>
-int index_template_pack()
-{
+int index_template_pack() {
   return Ts...[I]; // no-crash
 }
 
-void template_pack_no_crash()
-{
-  int r = index_template_pack<2, 0, 1, 42>();
+void template_pack_no_crash() {
+  (void)index_template_pack<2, 0, 1, 42>();
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to