[clang] [LifetimeSafety] Overhaul CFG and analysis to also work with trivially destructed temporary objects (PR #177985)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 3 06:48:59 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-temporal-safety
@llvm/pr-subscribers-clang-analysis
Author: Abhinav Pradeep (AbhinavPradeep)
<details>
<summary>Changes</summary>
So far:
1. Added `CFGFullExprCleanup` which has a pointer to `BumpVector<const MaterializeTemporaryExpr *>` to track all MTE that **might** (in the sense that we take union on branches) be spawned by an `ExprWithCleanups`
2. Modified logic in `CFGBuilder` to appropriately insert this marker. It inserts the marker primarily via `CFGBuilder::VisitExprWithCleanups`, and also `CFGBuilder::addInitializer` and `CFGBuilder::VisitDeclSubExpr` as these bypass visiting the `ExprWithCleanups`. The bump vector is allocated appropriately using the bump allocator of the CFG to respect its lifetime rules.
3. Visiting to track the temporaries is done in `CFGBuilder::VisitForTemporaryDtors`. We introduce a flag to `TempDtorContext` so as to enable tracking only when necessary. The encountered non-lifetime extended MTE are stored in a small vector member of `TempDtorContext`.
---
Patch is 69.43 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/177985.diff
16 Files Affected:
- (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h (+1-1)
- (modified) clang/include/clang/Analysis/CFG.h (+32)
- (modified) clang/lib/Analysis/CFG.cpp (+66-6)
- (modified) clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp (+19-31)
- (modified) clang/lib/Analysis/PathDiagnostic.cpp (+1)
- (modified) clang/lib/StaticAnalyzer/Core/CoreEngine.cpp (+6)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+1)
- (modified) clang/lib/StaticAnalyzer/Core/SymbolManager.cpp (+2)
- (modified) clang/test/Analysis/auto-obj-dtors-cfg-output.cpp (+88-84)
- (modified) clang/test/Analysis/cfg-rich-constructors.cpp (+91-64)
- (modified) clang/test/Analysis/cfg-rich-constructors.mm (+5-3)
- (modified) clang/test/Analysis/cfg.cpp (+1)
- (modified) clang/test/Analysis/missing-bind-temporary.cpp (+9-6)
- (modified) clang/test/Analysis/temp-obj-dtors-cfg-output.cpp (+99-67)
- (modified) clang/test/Sema/warn-lifetime-analysis-nocfg.cpp (+11-10)
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+6-5)
``````````diff
diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index a47505ee9f159..271e76d35a730 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -60,7 +60,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);
- void handleTemporaryDtor(const CFGTemporaryDtor &TemporaryDtor);
+ void handleFullExprCleanup(const CFGFullExprCleanup &FullExprCleanup);
void handleGSLPointerConstruction(const CXXConstructExpr *CCE);
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h
index a4bafd4927df0..c021ffcf85785 100644
--- a/clang/include/clang/Analysis/CFG.h
+++ b/clang/include/clang/Analysis/CFG.h
@@ -62,6 +62,7 @@ class CFGElement {
NewAllocator,
LifetimeEnds,
LoopExit,
+ FullExprCleanup,
// stmt kind
Statement,
Constructor,
@@ -313,6 +314,32 @@ class CFGLifetimeEnds : public CFGElement {
}
};
+class CFGFullExprCleanup : public CFGElement {
+ using MTEVecTy = BumpVector<const MaterializeTemporaryExpr *>;
+
+public:
+ explicit CFGFullExprCleanup(const MTEVecTy *vec)
+ : CFGElement(FullExprCleanup, vec, nullptr) {}
+
+ ArrayRef<const MaterializeTemporaryExpr *> getExpiringMTEs() const {
+ const MTEVecTy *ExpiringMTEs =
+ static_cast<const MTEVecTy *>(Data1.getPointer());
+ if (!ExpiringMTEs)
+ return {};
+ return ArrayRef<const MaterializeTemporaryExpr *>(ExpiringMTEs->begin(),
+ ExpiringMTEs->end());
+ }
+
+private:
+ friend class CFGElement;
+
+ CFGFullExprCleanup() = default;
+
+ static bool isKind(const CFGElement &elem) {
+ return elem.getKind() == FullExprCleanup;
+ }
+};
+
/// Represents beginning of a scope implicitly generated
/// by the compiler on encountering a CompoundStmt
class CFGScopeBegin : public CFGElement {
@@ -1183,6 +1210,11 @@ class CFGBlock {
Elements.push_back(CFGLifetimeEnds(VD, S), C);
}
+ void appendFullExprCleanup(BumpVector<const MaterializeTemporaryExpr *> *BV,
+ BumpVectorContext &C) {
+ Elements.push_back(CFGFullExprCleanup(BV), C);
+ }
+
void appendLoopExit(const Stmt *LoopStmt, BumpVectorContext &C) {
Elements.push_back(CFGLoopExit(LoopStmt), C);
}
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index a9c7baa00543c..ca47c644228f4 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -55,6 +55,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <cstddef>
#include <memory>
#include <optional>
#include <string>
@@ -698,7 +699,9 @@ class CFGBuilder {
TempDtorContext() = default;
TempDtorContext(TryResult KnownExecuted)
: IsConditional(true), KnownExecuted(KnownExecuted) {}
-
+ TempDtorContext(TryResult KnownExecuted, bool TrackExpiringMTEs)
+ : IsConditional(true), TrackExpiringMTEs(TrackExpiringMTEs),
+ KnownExecuted(KnownExecuted) {}
/// Returns whether we need to start a new branch for a temporary destructor
/// call. This is the case when the temporary destructor is
/// conditionally executed, and it is the first one we encounter while
@@ -716,7 +719,16 @@ class CFGBuilder {
TerminatorExpr = E;
}
+ void track(const MaterializeTemporaryExpr *MTE) {
+ // Must only be invoked when TrackMTE is true
+ assert(TrackExpiringMTEs);
+ if (MTE)
+ CollectedMTEs.push_back(MTE);
+ }
+
const bool IsConditional = false;
+ bool TrackExpiringMTEs = false;
+ SmallVector<const MaterializeTemporaryExpr *, 5> CollectedMTEs;
const TryResult KnownExecuted = true;
CFGBlock *Succ = nullptr;
CXXBindTemporaryExpr *TerminatorExpr = nullptr;
@@ -803,6 +815,7 @@ class CFGBuilder {
void addScopeChangesHandling(LocalScope::const_iterator SrcPos,
LocalScope::const_iterator DstPos,
Stmt *S);
+ void addFullExprCleanupMarker(TempDtorContext &Context);
CFGBlock *createScopeChangesHandlingBlock(LocalScope::const_iterator SrcPos,
CFGBlock *SrcBlk,
LocalScope::const_iterator DstPost,
@@ -1816,8 +1829,11 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
/*ExternallyDestructed=*/false, Context);
+
+ addFullExprCleanupMarker(Context);
}
}
@@ -2065,6 +2081,23 @@ void CFGBuilder::addScopeChangesHandling(LocalScope::const_iterator SrcPos,
addAutomaticObjHandling(SrcPos, BasePos, S);
}
+void CFGBuilder::addFullExprCleanupMarker(TempDtorContext &Context) {
+ assert(Context.TrackExpiringMTEs);
+
+ using MTEVecTy = BumpVector<const MaterializeTemporaryExpr *>;
+ MTEVecTy *ExpiringMTEs = nullptr;
+ BumpVectorContext &BVC = cfg->getBumpVectorContext();
+
+ size_t NumCollected = Context.CollectedMTEs.size();
+ if (NumCollected > 0) {
+ autoCreateBlock();
+ ExpiringMTEs = new (cfg->getAllocator()) MTEVecTy(BVC, NumCollected);
+ for (const MaterializeTemporaryExpr *MTE : Context.CollectedMTEs)
+ ExpiringMTEs->push_back(MTE, BVC);
+ Block->appendFullExprCleanup(ExpiringMTEs, BVC);
+ }
+}
+
/// createScopeChangesHandlingBlock - Creates a block with cfgElements
/// corresponding to changing the scope from the source scope of the GotoStmt,
/// to destination scope. Add destructor, lifetime and cfgScopeEnd
@@ -3113,8 +3146,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
/*ExternallyDestructed=*/true, Context);
+
+ addFullExprCleanupMarker(Context);
}
}
@@ -4924,13 +4960,17 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
}
CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
- AddStmtChoice asc, bool ExternallyDestructed) {
+ AddStmtChoice asc,
+ bool ExternallyDestructed) {
if (BuildOpts.AddTemporaryDtors) {
// If adding implicit destructors visit the full expression for adding
// destructors of temporaries.
TempDtorContext Context;
+ Context.TrackExpiringMTEs = true;
VisitForTemporaryDtors(E->getSubExpr(), ExternallyDestructed, Context);
+ addFullExprCleanupMarker(Context);
+
// Full expression has to be added as CFGStmt so it will be sequenced
// before destructors of it's temporaries.
asc = asc.withAlwaysAdd(true);
@@ -5117,6 +5157,8 @@ CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
case Stmt::MaterializeTemporaryExprClass: {
const MaterializeTemporaryExpr* MTE = cast<MaterializeTemporaryExpr>(E);
ExternallyDestructed = (MTE->getStorageDuration() != SD_FullExpression);
+ if (Context.TrackExpiringMTEs && !ExternallyDestructed)
+ Context.track(MTE);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
// Find the expression whose lifetime needs to be extended.
@@ -5208,10 +5250,14 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
// executed, thus we add a branch node that depends on the temporary
// constructor call.
TempDtorContext RHSContext(
- bothKnownTrue(Context.KnownExecuted, RHSExecuted));
+ bothKnownTrue(Context.KnownExecuted, RHSExecuted),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getRHS(), false, RHSContext);
InsertTempDtorDecisionBlock(RHSContext);
+ if (Context.TrackExpiringMTEs)
+ Context.CollectedMTEs.append(RHSContext.CollectedMTEs);
+
return Block;
}
@@ -5290,14 +5336,15 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
if (NegatedVal.isKnown()) NegatedVal.negate();
TempDtorContext TrueContext(
- bothKnownTrue(Context.KnownExecuted, ConditionVal));
+ bothKnownTrue(Context.KnownExecuted, ConditionVal),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getTrueExpr(), ExternallyDestructed, TrueContext);
CFGBlock *TrueBlock = Block;
Block = ConditionBlock;
Succ = ConditionSucc;
- TempDtorContext FalseContext(
- bothKnownTrue(Context.KnownExecuted, NegatedVal));
+ TempDtorContext FalseContext(bothKnownTrue(Context.KnownExecuted, NegatedVal),
+ Context.TrackExpiringMTEs);
VisitForTemporaryDtors(E->getFalseExpr(), ExternallyDestructed, FalseContext);
if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
@@ -5308,6 +5355,11 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
} else {
InsertTempDtorDecisionBlock(FalseContext);
}
+ if (Context.TrackExpiringMTEs) {
+ Context.CollectedMTEs.append(TrueContext.CollectedMTEs);
+ Context.CollectedMTEs.append(FalseContext.CollectedMTEs);
+ }
+
return Block;
}
@@ -5425,6 +5477,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
case CFGElement::CXXRecordTypedCall:
case CFGElement::ScopeBegin:
case CFGElement::ScopeEnd:
+ case CFGElement::FullExprCleanup:
case CFGElement::CleanupFunction:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
@@ -5989,6 +6042,13 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
OS << " (Lifetime ends)";
break;
+ case CFGElement::Kind::FullExprCleanup:
+ OS << "(FullExprCleanup collected "
+ << std::to_string(
+ E.castAs<CFGFullExprCleanup>().getExpiringMTEs().size())
+ << " MTEs)";
+ break;
+
case CFGElement::Kind::LoopExit:
OS << E.castAs<CFGLoopExit>().getLoopStmt()->getStmtClassName()
<< " (LoopExit)";
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index c982b255d54ea..8f88648aa7c33 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -9,11 +9,15 @@
#include <cassert>
#include <string>
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/OperationKinds.h"
#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TimeProfiler.h"
@@ -80,17 +84,6 @@ static const PathLoan *createLoan(FactManager &FactMgr,
return FactMgr.getLoanMgr().createLoan<PathLoan>(Path, MTE);
}
-/// Try to find a CXXBindTemporaryExpr that descends from MTE, stripping away
-/// any implicit casts.
-/// \param MTE MaterializeTemporaryExpr whose descendants we are interested in.
-/// \return Pointer to descendant CXXBindTemporaryExpr or nullptr when not
-/// found.
-static const CXXBindTemporaryExpr *
-getChildBinding(const MaterializeTemporaryExpr *MTE) {
- const Expr *Child = MTE->getSubExpr()->IgnoreImpCasts();
- return dyn_cast<CXXBindTemporaryExpr>(Child);
-}
-
void FactsGenerator::run() {
llvm::TimeTraceScope TimeProfile("FactGenerator");
const CFG &Cfg = *AC.getCFG();
@@ -110,9 +103,9 @@ void FactsGenerator::run() {
else if (std::optional<CFGLifetimeEnds> LifetimeEnds =
Element.getAs<CFGLifetimeEnds>())
handleLifetimeEnds(*LifetimeEnds);
- else if (std::optional<CFGTemporaryDtor> TemporaryDtor =
- Element.getAs<CFGTemporaryDtor>())
- handleTemporaryDtor(*TemporaryDtor);
+ else if (std::optional<CFGFullExprCleanup> FullExprCleanup =
+ Element.getAs<CFGFullExprCleanup>())
+ handleFullExprCleanup(*FullExprCleanup);
}
CurrentBlockFacts.append(EscapesInCurrentBlock.begin(),
EscapesInCurrentBlock.end());
@@ -407,18 +400,15 @@ void FactsGenerator::VisitMaterializeTemporaryExpr(
MTEList->getLength() == (SubExprList->getLength() + 1)) &&
"MTE top level origin should contain a loan to the MTE itself");
MTEList = getRValueOrigins(MTE, MTEList);
- if (getChildBinding(MTE)) {
- // Issue a loan to MTE for the storage location represented by MTE.
- const Loan *L = createLoan(FactMgr, MTE);
- OriginList *List = getOriginsList(*MTE);
- CurrentBlockFacts.push_back(
- FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
- }
+ // Issue a loan to MTE for the storage location represented by MTE.
+ const Loan *L = createLoan(FactMgr, MTE);
+ OriginList *List = getOriginsList(*MTE);
+ CurrentBlockFacts.push_back(
+ FactMgr.createFact<IssueFact>(L->getID(), List->getOuterOriginID()));
flow(MTEList, SubExprList, /*Kill=*/true);
}
void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
- /// TODO: Handle loans to temporaries.
const VarDecl *LifetimeEndsVD = LifetimeEnds.getVarDecl();
if (!LifetimeEndsVD)
return;
@@ -441,12 +431,10 @@ void FactsGenerator::handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds) {
}
}
-void FactsGenerator::handleTemporaryDtor(
- const CFGTemporaryDtor &TemporaryDtor) {
- const CXXBindTemporaryExpr *ExpiringBTE =
- TemporaryDtor.getBindTemporaryExpr();
- if (!ExpiringBTE)
- return;
+void FactsGenerator::handleFullExprCleanup(
+ const CFGFullExprCleanup &FullExprCleanup) {
+ ArrayRef<const MaterializeTemporaryExpr *> CollectedMTEs =
+ FullExprCleanup.getExpiringMTEs();
// Iterate through all loans to see if any expire.
for (const auto *Loan : FactMgr.getLoanMgr().getLoans()) {
if (const auto *PL = dyn_cast<PathLoan>(Loan)) {
@@ -456,9 +444,9 @@ void FactsGenerator::handleTemporaryDtor(
const MaterializeTemporaryExpr *Path = AP.getAsMaterializeTemporaryExpr();
if (!Path)
continue;
- if (ExpiringBTE == getChildBinding(Path)) {
- CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(
- PL->getID(), TemporaryDtor.getBindTemporaryExpr()->getEndLoc()));
+ if (is_contained(CollectedMTEs, Path)) {
+ CurrentBlockFacts.push_back(
+ FactMgr.createFact<ExpireFact>(PL->getID(), Path->getEndLoc()));
}
}
}
diff --git a/clang/lib/Analysis/PathDiagnostic.cpp b/clang/lib/Analysis/PathDiagnostic.cpp
index e42731b93bfb2..5e387e086a5a7 100644
--- a/clang/lib/Analysis/PathDiagnostic.cpp
+++ b/clang/lib/Analysis/PathDiagnostic.cpp
@@ -564,6 +564,7 @@ getLocationForCaller(const StackFrameContext *SFC,
case CFGElement::CleanupFunction:
llvm_unreachable("not yet implemented!");
case CFGElement::LifetimeEnds:
+ case CFGElement::FullExprCleanup:
case CFGElement::LoopExit:
llvm_unreachable("CFGElement kind should not be on callsite!");
}
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 95a843ee87571..49835a0224171 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -524,6 +524,12 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
assert(B);
assert(!B->empty());
+ // We no-op by skipping any FullExprCleanup
+ while (StmtIdx < B->size() &&
+ (*B)[StmtIdx].getKind() == CFGElement::FullExprCleanup) {
+ StmtIdx++;
+ }
+
if (StmtIdx == B->size())
HandleBlockExit(B, Pred);
else {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a6a96b594fe85..da176ebd20bb4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -995,6 +995,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
return;
case CFGElement::LifetimeEnds:
case CFGElement::CleanupFunction:
+ case CFGElement::FullExprCleanup:
case CFGElement::ScopeBegin:
case CFGElement::ScopeEnd:
return;
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index d03f47fa301e6..35fbe4d60c3f2 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -115,6 +115,8 @@ const Stmt *SymbolConjured::getStmt() const {
return Elem->castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
case CFGElement::CleanupFunction:
return nullptr;
+ case CFGElement::FullExprCleanup:
+ return nullptr;
}
return nullptr;
}
diff --git a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
index 96b9a5508cc08..4790469850107 100644
--- a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -188,50 +188,52 @@ void test_aggregate_lifetime_extension() {
// CXX11-NEXT: 14: {[B1.13]}
// Double curly braces trigger regexps, escape as per FileCheck manual.
// CXX11-NEXT: 15: C c = {{[{][{]}}A(), A(){{[}][}]}};
-// CXX11-NEXT: 16: ~A() (Temporary object destructor)
+// CXX11-NEXT: 16: (FullExprCleanup collected 2 MTEs)
// CXX11-NEXT: 17: ~A() (Temporary object destructor)
-// CXX11-WARNINGS-NEXT: 18: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 18: A() (CXXConstructExpr, [B1.19], [B1.21], A)
-// CXX11-NEXT: 19: [B1.18] (BindTemporary)
-// CXX11-NEXT: 20: [B1.19] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 21: [B1.20]
-// CXX11-NEXT: 22: [B1.21] (CXXConstructExpr, const A)
-// CXX11-WARNINGS-NEXT: 23: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 23: A() (CXXConstructExpr, [B1.24], [B1.26], A)
-// CXX11-NEXT: 24: [B1.23] (BindTemporary)
-// CXX11-NEXT: 25: [B1.24] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 26: [B1.25]
-// CXX11-NEXT: 27: [B1.26] (CXXConstructExpr, const A)
+// CXX11-NEXT: 18: ~A() (Temporary object destructor)
+// CXX11-WARNINGS-NEXT: 19: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 19: A() (CXXConstructExpr, [B1.20], [B1.22], A)
+// CXX11-NEXT: 20: [B1.19] (BindTemporary)
+// CXX11-NEXT: 21: [B1.20] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 22: [B1.21]
+// CXX11-NEXT: 23: [B1.22] (CXXConstructExpr, const A)
+// CXX11-WARNINGS-NEXT: 24: A() (CXXConstructExpr, A)
+// CXX11-ANALYZER-NEXT: 24: A() (CXXConstructExpr, [B1.25], [B1.27], A)
+// CXX11-NEXT: 25: [B1.24] (BindTemporary)
+// CXX11-NEXT: 26: [B1.25] (ImplicitCastExpr, NoOp, const A)
+// CXX11-NEXT: 27: [B1.26]
+// CXX11-NEXT: 28: [B1.27] (CXXConstructExpr, const A)
// FIXME: Why does it look as if the initializer list consumes uncopied objects?
-// CXX11-NEXT: 28: {[B1.19], [B1.24]}
-// CXX11-NEXT: 29: [B1.28] (BindTemporary)
-// CXX11-NEXT: 30: [B1.29]
-// CXX11-NEXT: 31: {[B1.30]}
-// CXX11-WARNINGS-NEXT: 32: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 32: A() (CXXConstructExpr, [B1.33], [B1.35], A)
-// CXX11-NEXT: 33: [B1.32] (BindTemporary)
-// CXX11-NEXT: 34: [B1.33] (ImplicitCastExpr, NoOp, const A)
-// CXX11-NEXT: 35: [B1.34]
-// CXX11-NEXT: 36: [B1.35] (CXXConstructExpr, const A)
-// CXX11-WARNINGS-NEXT: 37: A() (CXXConstructExpr, A)
-// CXX11-ANALYZER-NEXT: 37: A() (CXXConstructExpr, [B1.38], [B1.40], A)
-// CXX11-NEXT: 38: [B1.37] (BindTemporary)
-// CXX11-NEXT: 39: [B1.38] (ImplicitCastExpr, NoOp, const ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/177985
More information about the cfe-commits
mailing list