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
