llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
