<div dir="ltr">We've started seeing assertion failures after this commit.<div>assert.h assertion failed at llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp:485 in static clang::ento::ProgramStateRef clang::ento::ExprEngine::elideDestructor(clang::ento::ProgramStateRef, const clang::CXXBindTemporaryExpr *, const clang::LocationContext *): !State->contains<ElidedDestructors>(I)<br></div><div><br></div><div><br></div><div>The stack trace is<div><div>clang::ento::ExprEngine::elideDestructor</div><div>clang::ento::ExprEngine::prepareForObjectConstruction</div><div>clang::ento::ExprEngine::prepareForObjectConstruction</div><div>clang::ento::ExprEngine::VisitCXXConstructExpr</div><div>clang::ento::ExprEngine::Visit</div><div>clang::ento::ExprEngine::ProcessStmt</div><div>clang::ento::ExprEngine::processCFGElement</div><div>clang::ento::CoreEngine::HandlePostStmt</div><div>clang::ento::CoreEngine::dispatchWorkItem</div><div>clang::ento::CoreEngine::ExecuteWorkList</div><div>clang::ento::ExprEngine::ExecuteWorkList</div><div>::AnalysisConsumer::ActionExprEngine</div><div>::AnalysisConsumer::HandleCode</div><div>::AnalysisConsumer::HandleDeclsCallGraph</div><div>::AnalysisConsumer::runAnalysisOnTranslationUnit</div><div>::AnalysisConsumer::HandleTranslationUnit</div></div></div><div><br></div><div>I haven't come up with a test case yet.</div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, Jun 28, 2018 at 2:34 AM Artem Dergachev via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: dergachev<br>
Date: Wed Jun 27 17:30:18 2018<br>
New Revision: 335800<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=335800&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=335800&view=rev</a><br>
Log:<br>
[analyzer] Add support for pre-C++17 copy elision.<br>
<br>
r335795 adds copy elision information to CFG. This commit allows static analyzer<br>
to elide elidable copy constructors by constructing the objects that were<br>
previously subject to elidable copy directly in the target region of the copy.<br>
<br>
The chain of elided constructors may potentially be indefinitely long. This<br>
only happens when the object is being returned from a function which in turn is<br>
returned from another function, etc.<br>
<br>
NRVO is not supported yet.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D47671" rel="noreferrer" target="_blank">https://reviews.llvm.org/D47671</a><br>
<br>
Modified:<br>
  cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h<br>
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp<br>
  cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp<br>
  cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp<br>
  cfe/trunk/test/Analysis/gtest.cpp<br>
  cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp<br>
  cfe/trunk/test/Analysis/lifetime-extension.cpp<br>
  cfe/trunk/test/Analysis/temporaries.cpp<br>
<br>
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)<br>
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Wed Jun 27 17:30:18 2018<br>
@@ -780,9 +780,30 @@ private:<br>
    llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,<br>
    const LocationContext *LC);<br>
<br>
+Â /// If the given expression corresponds to a temporary that was used for<br>
+Â /// passing into an elidable copy/move constructor and that constructor<br>
+Â /// was actually elided, track that we also need to elide the destructor.<br>
+Â static ProgramStateRef elideDestructor(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC);<br>
+<br>
+Â /// Stop tracking the destructor that corresponds to an elided constructor.<br>
+Â static ProgramStateRef<br>
+Â cleanupElidedDestructor(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC);<br>
+<br>
+Â /// Returns true if the given expression corresponds to a temporary that<br>
+Â /// was constructed for passing into an elidable copy/move constructor<br>
+Â /// and that constructor was actually elided.<br>
+Â static bool isDestructorElided(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC);<br>
+<br>
  /// Check if all objects under construction have been fully constructed<br>
  /// for the given context range (including FromLC, not including ToLC).<br>
-Â /// This is useful for assertions.<br>
+Â /// This is useful for assertions. Also checks if elided destructors<br>
+Â /// were cleaned up.<br>
  static bool areAllObjectsFullyConstructed(ProgramStateRef State,<br>
                       const LocationContext *FromLC,<br>
                       const LocationContext *ToLC);<br>
<br>
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Wed Jun 27 17:30:18 2018<br>
@@ -139,7 +139,8 @@ public:<br>
   // encountered. This list may be expanded when new actions are implemented.<br>
   assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) ||<br>
      isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) ||<br>
-Â Â Â Â Â Â isa<MaterializeTemporaryExpr>(getStmt()));<br>
+Â Â Â Â Â Â isa<MaterializeTemporaryExpr>(getStmt()) ||<br>
+Â Â Â Â Â Â isa<CXXConstructExpr>(getStmt()));<br>
  }<br>
<br>
  const Stmt *getStmt() const {<br>
@@ -183,6 +184,14 @@ typedef llvm::ImmutableMap<ConstructedOb<br>
 REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,<br>
                 ObjectsUnderConstructionMap)<br>
<br>
+// Additionally, track a set of destructors that correspond to elided<br>
+// constructors when copy elision occurs.<br>
+typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *><br>
+Â Â ElidedDestructorItem;<br>
+typedef llvm::ImmutableSet<ElidedDestructorItem><br>
+Â Â ElidedDestructorSet;<br>
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ElidedDestructorSet);<br>
<br>
 //===----------------------------------------------------------------------===//<br>
 // Engine construction and deletion.<br>
@@ -358,14 +367,12 @@ ExprEngine::createTemporaryRegionIfNeede<br>
  // a new temporary region out of thin air and copy the contents of the object<br>
  // (which are currently present in the Environment, because Init is an rvalue)<br>
  // into that region. This is not correct, but it is better than nothing.<br>
-Â bool FoundOriginalMaterializationRegion = false;<br>
  const TypedValueRegion *TR = nullptr;<br>
  if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {<br>
   if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {<br>
-Â Â Â FoundOriginalMaterializationRegion = true;<br>
-Â Â Â TR = cast<CXXTempObjectRegion>(V->getAsRegion());<br>
-Â Â Â assert(TR);<br>
    State = finishObjectConstruction(State, MT, LC);<br>
+Â Â Â State = State->BindExpr(Result, LC, *V);<br>
+Â Â Â return State;<br>
   } else {<br>
    StorageDuration SD = MT->getStorageDuration();<br>
    // If this object is bound to a reference with static storage duration, we<br>
@@ -402,35 +409,33 @@ ExprEngine::createTemporaryRegionIfNeede<br>
   }<br>
  }<br>
<br>
-Â if (!FoundOriginalMaterializationRegion) {<br>
-Â Â // What remains is to copy the value of the object to the new region.<br>
-Â Â // FIXME: In other words, what we should always do is copy value of the<br>
-Â Â // Init expression (which corresponds to the bigger object) to the whole<br>
-Â Â // temporary region TR. However, this value is often no longer present<br>
-Â Â // in the Environment. If it has disappeared, we instead invalidate TR.<br>
-Â Â // Still, what we can do is assign the value of expression Ex (which<br>
-Â Â // corresponds to the sub-object) to the TR's sub-region Reg. At least,<br>
-Â Â // values inside Reg would be correct.<br>
-Â Â SVal InitVal = State->getSVal(Init, LC);<br>
-Â Â if (InitVal.isUnknown()) {<br>
-Â Â Â InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),<br>
-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â currBldrCtx->blockCount());<br>
-Â Â Â State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
-<br>
-Â Â Â // Then we'd need to take the value that certainly exists and bind it<br>
-Â Â Â // over.<br>
-Â Â Â if (InitValWithAdjustments.isUnknown()) {<br>
-Â Â Â Â // Try to recover some path sensitivity in case we couldn't<br>
-Â Â Â Â // compute the value.<br>
-Â Â Â Â InitValWithAdjustments = getSValBuilder().conjureSymbolVal(<br>
-Â Â Â Â Â Â Result, LC, InitWithAdjustments->getType(),<br>
-Â Â Â Â Â Â currBldrCtx->blockCount());<br>
-Â Â Â }<br>
-Â Â Â State =<br>
-Â Â Â Â Â State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);<br>
-Â Â } else {<br>
-Â Â Â State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
+Â // What remains is to copy the value of the object to the new region.<br>
+Â // FIXME: In other words, what we should always do is copy value of the<br>
+Â // Init expression (which corresponds to the bigger object) to the whole<br>
+Â // temporary region TR. However, this value is often no longer present<br>
+Â // in the Environment. If it has disappeared, we instead invalidate TR.<br>
+Â // Still, what we can do is assign the value of expression Ex (which<br>
+Â // corresponds to the sub-object) to the TR's sub-region Reg. At least,<br>
+Â // values inside Reg would be correct.<br>
+Â SVal InitVal = State->getSVal(Init, LC);<br>
+Â if (InitVal.isUnknown()) {<br>
+Â Â InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â currBldrCtx->blockCount());<br>
+Â Â State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
+<br>
+Â Â // Then we'd need to take the value that certainly exists and bind it<br>
+Â Â // over.<br>
+Â Â if (InitValWithAdjustments.isUnknown()) {<br>
+Â Â Â // Try to recover some path sensitivity in case we couldn't<br>
+Â Â Â // compute the value.<br>
+Â Â Â InitValWithAdjustments = getSValBuilder().conjureSymbolVal(<br>
+Â Â Â Â Â Result, LC, InitWithAdjustments->getType(),<br>
+Â Â Â Â Â currBldrCtx->blockCount());<br>
   }<br>
+Â Â State =<br>
+Â Â Â Â State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);<br>
+Â } else {<br>
+Â Â State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
  }<br>
<br>
  // The result expression would now point to the correct sub-region of the<br>
@@ -438,10 +443,8 @@ ExprEngine::createTemporaryRegionIfNeede<br>
  // correctly in case (Result == Init).<br>
  State = State->BindExpr(Result, LC, Reg);<br>
<br>
-Â if (!FoundOriginalMaterializationRegion) {<br>
-Â Â // Notify checkers once for two bindLoc()s.<br>
-Â Â State = processRegionChange(State, TR, LC);<br>
-Â }<br>
+Â // Notify checkers once for two bindLoc()s.<br>
+Â State = processRegionChange(State, TR, LC);<br>
<br>
  return State;<br>
 }<br>
@@ -475,6 +478,30 @@ ProgramStateRef ExprEngine::finishObject<br>
  return State->remove<ObjectsUnderConstruction>(Key);<br>
 }<br>
<br>
+ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC) {<br>
+Â ElidedDestructorItem I(BTE, LC);<br>
+Â assert(!State->contains<ElidedDestructors>(I));<br>
+Â return State->add<ElidedDestructors>(I);<br>
+}<br>
+<br>
+ProgramStateRef<br>
+ExprEngine::cleanupElidedDestructor(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC) {<br>
+Â ElidedDestructorItem I(BTE, LC);<br>
+Â assert(State->contains<ElidedDestructors>(I));<br>
+Â return State->remove<ElidedDestructors>(I);<br>
+}<br>
+<br>
+bool ExprEngine::isDestructorElided(ProgramStateRef State,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const CXXBindTemporaryExpr *BTE,<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â const LocationContext *LC) {<br>
+Â ElidedDestructorItem I(BTE, LC);<br>
+Â return State->contains<ElidedDestructors>(I);<br>
+}<br>
+<br>
 bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,<br>
                        const LocationContext *FromLC,<br>
                        const LocationContext *ToLC) {<br>
@@ -485,6 +512,10 @@ bool ExprEngine::areAllObjectsFullyConst<br>
    if (I.first.getLocationContext() == LC)<br>
     return false;<br>
<br>
+Â Â for (auto I: State->get<ElidedDestructors>())<br>
+Â Â Â if (I.second == LC)<br>
+Â Â Â Â return false;<br>
+<br>
   LC = LC->getParent();<br>
  }<br>
  return true;<br>
@@ -529,6 +560,14 @@ static void printObjectsUnderConstructio<br>
   Key.print(Out, nullptr, PP);<br>
   Out << " : " << Value << NL;<br>
  }<br>
+<br>
+Â for (auto I : State->get<ElidedDestructors>()) {<br>
+Â Â if (I.second != LC)<br>
+Â Â Â continue;<br>
+Â Â Out << '(' << I.second << ',' << (const void *)I.first << ") ";<br>
+Â Â I.first->printPretty(Out, nullptr, PP);<br>
+Â Â Out << " : (constructor elided)" << NL;<br>
+Â }<br>
 }<br>
<br>
 void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,<br>
@@ -1003,10 +1042,11 @@ void ExprEngine::ProcessMemberDtor(const<br>
 void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,<br>
                    ExplodedNode *Pred,<br>
                    ExplodedNodeSet &Dst) {<br>
-Â ExplodedNodeSet CleanDtorState;<br>
-Â StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);<br>
+Â const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr();<br>
  ProgramStateRef State = Pred->getState();<br>
+Â const LocationContext *LC = Pred->getLocationContext();<br>
  const MemRegion *MR = nullptr;<br>
+<br>
  if (Optional<SVal> V =<br>
      getObjectUnderConstruction(State, D.getBindTemporaryExpr(),<br>
                   Pred->getLocationContext())) {<br>
@@ -1017,6 +1057,21 @@ void ExprEngine::ProcessTemporaryDtor(co<br>
                   Pred->getLocationContext());<br>
   MR = V->getAsRegion();<br>
  }<br>
+<br>
+Â // If copy elision has occured, and the constructor corresponding to the<br>
+Â // destructor was elided, we need to skip the destructor as well.<br>
+Â if (isDestructorElided(State, BTE, LC)) {<br>
+Â Â State = cleanupElidedDestructor(State, BTE, LC);<br>
+Â Â NodeBuilder Bldr(Pred, Dst, *currBldrCtx);<br>
+Â Â PostImplicitCall PP(D.getDestructorDecl(getContext()),<br>
+Â Â Â Â Â Â Â Â Â Â Â Â D.getBindTemporaryExpr()->getLocStart(),<br>
+Â Â Â Â Â Â Â Â Â Â Â Â Pred->getLocationContext());<br>
+Â Â Bldr.generateNode(PP, State, Pred);<br>
+Â Â return;<br>
+Â }<br>
+<br>
+Â ExplodedNodeSet CleanDtorState;<br>
+Â StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);<br>
  StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);<br>
<br>
  QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType();<br>
@@ -2201,8 +2256,21 @@ void ExprEngine::processEndOfFunction(No<br>
      // it must be a separate problem.<br>
      assert(isa<CXXBindTemporaryExpr>(I.first.getStmt()));<br>
      State = State->remove<ObjectsUnderConstruction>(I.first);<br>
+Â Â Â Â Â // Also cleanup the elided destructor info.<br>
+Â Â Â Â Â ElidedDestructorItem Item(<br>
+Â Â Â Â Â Â Â cast<CXXBindTemporaryExpr>(I.first.getStmt()),<br>
+Â Â Â Â Â Â Â I.first.getLocationContext());<br>
+Â Â Â Â Â State = State->remove<ElidedDestructors>(Item);<br>
     }<br>
<br>
+Â Â Â // Also suppress the assertion for elided destructors when temporary<br>
+Â Â Â // destructors are not provided at all by the CFG, because there's no<br>
+Â Â Â // good place to clean them up.<br>
+Â Â Â if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG())<br>
+Â Â Â Â for (auto I : State->get<ElidedDestructors>())<br>
+Â Â Â Â Â if (I.second == LC)<br>
+Â Â Â Â Â Â State = State->remove<ElidedDestructors>(I);<br>
+<br>
    LC = LC->getParent();<br>
   }<br>
   if (State != Pred->getState()) {<br>
<br>
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Wed Jun 27 17:30:18 2018<br>
@@ -209,12 +209,37 @@ std::pair<ProgramStateRef, SVal> ExprEng<br>
    }<br>
    llvm_unreachable("Unhandled return value construction context!");<br>
   }<br>
-Â Â case ConstructionContext::ElidedTemporaryObjectKind:<br>
+Â Â case ConstructionContext::ElidedTemporaryObjectKind: {<br>
    assert(AMgr.getAnalyzerOptions().shouldElideConstructors());<br>
-Â Â Â // FALL-THROUGH<br>
+Â Â Â const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);<br>
+Â Â Â const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();<br>
+Â Â Â const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();<br>
+Â Â Â const CXXConstructExpr *CE = TCC->getConstructorAfterElision();<br>
+<br>
+Â Â Â // Support pre-C++17 copy elision. We'll have the elidable copy<br>
+Â Â Â // constructor in the AST and in the CFG, but we'll skip it<br>
+Â Â Â // and construct directly into the final object. This call<br>
+Â Â Â // also sets the CallOpts flags for us.<br>
+Â Â Â SVal V;<br>
+Â Â Â std::tie(State, V) = prepareForObjectConstruction(<br>
+Â Â Â Â Â CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);<br>
+<br>
+Â Â Â // Remember that we've elided the constructor.<br>
+Â Â Â State = addObjectUnderConstruction(State, CE, LCtx, V);<br>
+<br>
+Â Â Â // Remember that we've elided the destructor.<br>
+Â Â Â if (BTE)<br>
+Â Â Â Â State = elideDestructor(State, BTE, LCtx);<br>
+<br>
+Â Â Â // Instead of materialization, shamelessly return<br>
+Â Â Â // the final object destination.<br>
+Â Â Â if (MTE)<br>
+Â Â Â Â State = addObjectUnderConstruction(State, MTE, LCtx, V);<br>
+<br>
+Â Â Â return std::make_pair(State, V);<br>
+Â Â }<br>
   case ConstructionContext::SimpleTemporaryObjectKind: {<br>
-Â Â Â // TODO: Copy elision implementation goes here.<br>
-Â Â Â const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);<br>
+Â Â Â const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);<br>
    const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();<br>
    const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();<br>
    SVal V = UnknownVal();<br>
@@ -266,6 +291,20 @@ void ExprEngine::VisitCXXConstructExpr(c<br>
<br>
  SVal Target = UnknownVal();<br>
<br>
+Â if (Optional<SVal> ElidedTarget =<br>
+Â Â Â Â Â getObjectUnderConstruction(State, CE, LCtx)) {<br>
+Â Â // We've previously modeled an elidable constructor by pretending that it in<br>
+Â Â // fact constructs into the correct target. This constructor can therefore<br>
+Â Â // be skipped.<br>
+Â Â Target = *ElidedTarget;<br>
+Â Â StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);<br>
+Â Â State = finishObjectConstruction(State, CE, LCtx);<br>
+Â Â if (auto L = Target.getAs<Loc>())<br>
+Â Â Â State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));<br>
+Â Â Bldr.generateNode(CE, Pred, State);<br>
+Â Â return;<br>
+Â }<br>
+<br>
  // FIXME: Handle arrays, which run the same constructor for every element.<br>
  // For now, we just run the first constructor (which should still invalidate<br>
  // the entire array).<br>
<br>
Modified: cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp (original)<br>
+++ cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp Wed Jun 27 17:30:18 2018<br>
@@ -1,5 +1,17 @@<br>
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify %s<br>
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s<br>
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %s<br>
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %s<br>
+<br>
+// Copy elision always occurs in C++17, otherwise it's under<br>
+// an on-by-default flag.<br>
+#if __cplusplus >= 201703L<br>
+Â #define ELIDE 1<br>
+#else<br>
+Â #ifndef NO_ELIDE_FLAG<br>
+Â Â #define ELIDE 1<br>
+Â #endif<br>
+#endif<br>
<br>
 void clang_analyzer_eval(bool);<br>
<br>
@@ -39,9 +51,9 @@ public:<br>
  C() : t(T(4)) {<br>
   S s = {1, 2, 3};<br>
   t.s = s;<br>
-Â Â // FIXME: Should be TRUE in C++11 as well.<br>
+Â Â // FIXME: Should be TRUE regardless of copy elision.<br>
   clang_analyzer_eval(t.w == 4);<br>
-#if __cplusplus >= 201703L<br>
+#ifdef ELIDE<br>
   // expected-warning@-2{{TRUE}}<br>
 #else<br>
   // expected-warning@-4{{UNKNOWN}}<br>
@@ -149,7 +161,7 @@ void testMultipleReturns() {<br>
  AddressVector<ClassWithoutDestructor> v;<br>
  ClassWithoutDestructor c = make3(v);<br>
<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
  clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
  clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}<br>
 #else<br>
@@ -184,13 +196,13 @@ void testVariable() {<br>
   ClassWithDestructor c = ClassWithDestructor(v);<br>
   // Check if the last destructor is an automatic destructor.<br>
   // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
   clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}<br>
 #endif<br>
  }<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
  // 0. Construct the variable.<br>
  // 1. Destroy the variable.<br>
  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
@@ -218,13 +230,13 @@ void testCtorInitializer() {<br>
   TestCtorInitializer t(v);<br>
   // Check if the last destructor is an automatic destructor.<br>
   // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
   clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}<br>
 #endif<br>
  }<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
  // 0. Construct the member variable.<br>
  // 1. Destroy the member variable.<br>
  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
@@ -257,14 +269,14 @@ void testMultipleReturnsWithDestructors(<br>
   ClassWithDestructor c = make3(v);<br>
   // Check if the last destructor is an automatic destructor.<br>
   // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
   clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}<br>
 #endif<br>
  }<br>
<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
  // 0. Construct the variable. Yes, constructor in make1() constructs<br>
  //  the variable 'c'.<br>
  // 1. Destroy the variable.<br>
<br>
Modified: cfe/trunk/test/Analysis/gtest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/gtest.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/gtest.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/gtest.cpp (original)<br>
+++ cfe/trunk/test/Analysis/gtest.cpp Wed Jun 27 17:30:18 2018<br>
@@ -154,13 +154,11 @@ void testConstrainState(int p) {<br>
 void testAssertSymbolicPtr(const bool *b) {<br>
  ASSERT_TRUE(*b); // no-crash<br>
<br>
-Â // FIXME: Our solver doesn't handle this well yet.<br>
-Â clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}<br>
+Â clang_analyzer_eval(*b); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void testAssertSymbolicRef(const bool &b) {<br>
  ASSERT_TRUE(b); // no-crash<br>
<br>
-Â // FIXME: Our solver doesn't handle this well yet.<br>
-Â clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}<br>
+Â clang_analyzer_eval(b); // expected-warning{{TRUE}}<br>
 }<br>
<br>
Modified: cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp (original)<br>
+++ cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp Wed Jun 27 17:30:18 2018<br>
@@ -30,19 +30,14 @@ public:<br>
 };<br>
<br>
 void test(int coin) {<br>
-Â // We'd divide by zero in the temporary destructor that goes after the<br>
-Â // elidable copy-constructor from C(0) to the lifetime-extended temporary.<br>
-Â // So in fact this example has nothing to do with lifetime extension.<br>
-Â // Actually, it would probably be better to elide the constructor, and<br>
-Â // put the note for the destructor call at the closing brace after nop.<br>
+Â // We'd divide by zero in the automatic destructor for variable 'c'.<br>
  const C &c = coin ? C(1) : C(0); // expected-note  {{Assuming 'coin' is 0}}<br>
                  // expected-note@-1{{'?' condition is false}}<br>
                  // expected-note@-2{{Passing the value 0 via 1st parameter 'x'}}<br>
                  // expected-note@-3{{Calling constructor for 'C'}}<br>
                  // expected-note@-4{{Returning from constructor for 'C'}}<br>
-Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // expected-note@-5{{Calling '~C'}}<br>
  c.nop();<br>
-}<br>
+} // expected-note{{Calling '~C'}}<br>
 } // end namespace test_lifetime_extended_temporary<br>
<br>
 namespace test_bug_after_dtor {<br>
<br>
Modified: cfe/trunk/test/Analysis/lifetime-extension.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lifetime-extension.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lifetime-extension.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/lifetime-extension.cpp (original)<br>
+++ cfe/trunk/test/Analysis/lifetime-extension.cpp Wed Jun 27 17:30:18 2018<br>
@@ -97,13 +97,10 @@ void f1() {<br>
<br>
 void f2() {<br>
  C *after, *before;<br>
-Â C c = C(1, &after, &before);<br>
-Â clang_analyzer_eval(after == before);<br>
-#ifdef TEMPORARIES<br>
-Â // expected-warning@-2{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+Â {<br>
+Â Â C c = C(1, &after, &before);<br>
+Â }<br>
+Â clang_analyzer_eval(after == before); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void f3(bool coin) {<br>
@@ -129,6 +126,7 @@ void f4(bool coin) {<br>
  // operator. Ideally also add support for the binary conditional operator in<br>
  // C++. Because for now it calls the constructor for the condition twice.<br>
  if (coin) {<br>
+Â Â // FIXME: Should not warn.<br>
   clang_analyzer_eval(after == before);<br>
 #ifdef TEMPORARIES<br>
  // expected-warning@-2{{The left operand of '==' is a garbage value}}<br>
@@ -136,13 +134,12 @@ void f4(bool coin) {<br>
  // expected-warning@-4{{UNKNOWN}}<br>
 #endif<br>
  } else {<br>
+Â Â // FIXME: Should be TRUE.<br>
   clang_analyzer_eval(after == before);<br>
 #ifdef TEMPORARIES<br>
-Â Â // Seems to work at the moment, but also seems accidental.<br>
-Â Â // Feel free to break.<br>
-Â // expected-warning@-4{{TRUE}}<br>
+Â // expected-warning@-2{{FALSE}}<br>
 #else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
+Â // expected-warning@-4{{UNKNOWN}}<br>
 #endif<br>
  }<br>
 }<br>
@@ -234,24 +231,25 @@ void f2() {<br>
 } // end namespace maintain_original_object_address_on_move<br>
<br>
 namespace maintain_address_of_copies {<br>
+class C;<br>
<br>
-template <typename T> struct AddressVector {<br>
-Â const T *buf[10];<br>
+struct AddressVector {<br>
+Â C *buf[10];<br>
  int len;<br>
<br>
  AddressVector() : len(0) {}<br>
<br>
-Â void push(const T *t) {<br>
-Â Â buf[len] = t;<br>
+Â void push(C *c) {<br>
+Â Â buf[len] = c;<br>
   ++len;<br>
  }<br>
 };<br>
<br>
 class C {<br>
-Â AddressVector<C> &v;<br>
+Â AddressVector &v;<br>
<br>
 public:<br>
-Â C(AddressVector<C> &v) : v(v) { v.push(this); }<br>
+Â C(AddressVector &v) : v(v) { v.push(this); }<br>
  ~C() { v.push(this); }<br>
<br>
 #ifdef MOVES<br>
@@ -267,132 +265,70 @@ public:<br>
 #endif<br>
  } // no-warning<br>
<br>
-Â static C make(AddressVector<C> &v) { return C(v); }<br>
+Â static C make(AddressVector &v) { return C(v); }<br>
 };<br>
<br>
 void f1() {<br>
-Â AddressVector<C> v;<br>
+Â AddressVector v;<br>
  {<br>
   C c = C(v);<br>
  }<br>
-Â // 0. Create the original temporary and lifetime-extend it into variable 'c'<br>
-Â //Â Â construction argument.<br>
-Â // 1. Construct variable 'c' (elidable copy/move).<br>
-Â // 2. Destroy the temporary.<br>
-Â // 3. Destroy variable 'c'.<br>
-Â clang_analyzer_eval(v.len == 4);<br>
-Â clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-Â clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
-#ifdef TEMPORARIES<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-#endif<br>
+Â // 0. Construct variable 'c' (copy/move elided).<br>
+Â // 1. Destroy variable 'c'.<br>
+Â clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void f2() {<br>
-Â AddressVector<C> v;<br>
+Â AddressVector v;<br>
  {<br>
   const C &c = C::make(v);<br>
  }<br>
-Â // 0. Construct the original temporary within make(),<br>
-Â // 1. Construct the return value of make() (elidable copy/move) and<br>
-Â //Â Â lifetime-extend it via reference 'c',<br>
-Â // 2. Destroy the temporary within make(),<br>
-Â // 3. Destroy the temporary lifetime-extended by 'c'.<br>
-Â clang_analyzer_eval(v.len == 4);<br>
-Â clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-Â clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
+Â // 0. Construct the return value of make() (copy/move elided) and<br>
+Â //Â Â lifetime-extend it directly via reference 'c',<br>
+Â // 1. Destroy the temporary lifetime-extended by 'c'.<br>
+Â clang_analyzer_eval(v.len == 2);<br>
+Â clang_analyzer_eval(v.buf[0] == v.buf[1]);<br>
 #ifdef TEMPORARIES<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
+Â // expected-warning@-3{{TRUE}}<br>
+Â // expected-warning@-3{{TRUE}}<br>
 #else<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
+Â // expected-warning@-6{{UNKNOWN}}<br>
+Â // expected-warning@-6{{UNKNOWN}}<br>
 #endif<br>
 }<br>
<br>
 void f3() {<br>
-Â AddressVector<C> v;<br>
+Â AddressVector v;<br>
  {<br>
   C &&c = C::make(v);<br>
  }<br>
-Â // 0. Construct the original temporary within make(),<br>
-Â // 1. Construct the return value of make() (elidable copy/move) and<br>
-Â //Â Â lifetime-extend it via reference 'c',<br>
-Â // 2. Destroy the temporary within make(),<br>
-Â // 3. Destroy the temporary lifetime-extended by 'c'.<br>
-Â clang_analyzer_eval(v.len == 4);<br>
-Â clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-Â clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
+Â // 0. Construct the return value of make() (copy/move elided) and<br>
+Â //Â Â lifetime-extend it directly via reference 'c',<br>
+Â // 1. Destroy the temporary lifetime-extended by 'c'.<br>
+Â clang_analyzer_eval(v.len == 2);<br>
+Â clang_analyzer_eval(v.buf[0] == v.buf[1]);<br>
 #ifdef TEMPORARIES<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
-Â // expected-warning@-4{{TRUE}}<br>
+Â // expected-warning@-3{{TRUE}}<br>
+Â // expected-warning@-3{{TRUE}}<br>
 #else<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
-Â // expected-warning@-8{{UNKNOWN}}<br>
+Â // expected-warning@-6{{UNKNOWN}}<br>
+Â // expected-warning@-6{{UNKNOWN}}<br>
 #endif<br>
 }<br>
<br>
-C doubleMake(AddressVector<C> &v) {<br>
+C doubleMake(AddressVector &v) {<br>
  return C::make(v);<br>
 }<br>
<br>
 void f4() {<br>
-Â AddressVector<C> v;<br>
+Â AddressVector v;<br>
  {<br>
   C c = doubleMake(v);<br>
  }<br>
-Â // 0. Construct the original temporary within make(),<br>
-Â // 1. Construct the return value of make() (elidable copy/move) and<br>
-Â //Â Â lifetime-extend it into the return value constructor argument within<br>
-Â //Â Â doubleMake(),<br>
-Â // 2. Destroy the temporary within make(),<br>
-Â // 3. Construct the return value of doubleMake() (elidable copy/move) and<br>
-Â //Â Â lifetime-extend it into the variable 'c' constructor argument,<br>
-Â // 4. Destroy the return value of make(),<br>
-Â // 5. Construct variable 'c' (elidable copy/move),<br>
-Â // 6. Destroy the return value of doubleMake(),<br>
-Â // 7. Destroy variable 'c'.<br>
-Â clang_analyzer_eval(v.len == 8);<br>
-Â clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-Â clang_analyzer_eval(v.buf[1] == v.buf[4]);<br>
-Â clang_analyzer_eval(v.buf[3] == v.buf[6]);<br>
-Â clang_analyzer_eval(v.buf[5] == v.buf[7]);<br>
-#ifdef TEMPORARIES<br>
-Â // expected-warning@-6{{TRUE}}<br>
-Â // expected-warning@-6{{TRUE}}<br>
-Â // expected-warning@-6{{TRUE}}<br>
-Â // expected-warning@-6{{TRUE}}<br>
-Â // expected-warning@-6{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-12{{UNKNOWN}}<br>
-Â // expected-warning@-12{{UNKNOWN}}<br>
-Â // expected-warning@-12{{UNKNOWN}}<br>
-Â // expected-warning@-12{{UNKNOWN}}<br>
-Â // expected-warning@-12{{UNKNOWN}}<br>
-#endif<br>
-}<br>
-<br>
-class NoDtor {<br>
-Â AddressVector<NoDtor> &v;<br>
-<br>
-public:<br>
-Â NoDtor(AddressVector<NoDtor> &v) : v(v) { v.push(this); }<br>
-};<br>
-<br>
-void f5() {<br>
-Â AddressVector<NoDtor> v;<br>
-Â const NoDtor &N = NoDtor(v);<br>
-Â clang_analyzer_eval(v.buf[0] == &N); // expected-warning{{TRUE}}<br>
+Â // 0. Construct variable 'c' (all copies/moves elided),<br>
+Â // 1. Destroy variable 'c'.<br>
+Â clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}<br>
 }<br>
-<br>
 } // end namespace maintain_address_of_copies<br>
<br>
Modified: cfe/trunk/test/Analysis/temporaries.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/temporaries.cpp (original)<br>
+++ cfe/trunk/test/Analysis/temporaries.cpp Wed Jun 27 17:30:18 2018<br>
@@ -612,107 +612,44 @@ void test() {<br>
  clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  C c4 = returnTemporaryWithConstruction();<br>
-Â clang_analyzer_eval(c4.getX() == 1);<br>
-Â clang_analyzer_eval(c4.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c4.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c4.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  C c5 = returnTemporaryWithAnotherFunctionWithConstruction();<br>
-Â clang_analyzer_eval(c5.getX() == 1);<br>
-Â clang_analyzer_eval(c5.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  C c6 = returnTemporaryWithCopyConstructionWithConstruction();<br>
-Â clang_analyzer_eval(c5.getX() == 1);<br>
-Â clang_analyzer_eval(c5.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
 #if __cplusplus >= 201103L<br>
<br>
  C c7 = returnTemporaryWithBraces();<br>
-Â clang_analyzer_eval(c7.getX() == 1);<br>
-Â clang_analyzer_eval(c7.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c7.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c7.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  C c8 = returnTemporaryWithAnotherFunctionWithBraces();<br>
-Â clang_analyzer_eval(c8.getX() == 1);<br>
-Â clang_analyzer_eval(c8.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c8.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c8.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  C c9 = returnTemporaryWithCopyConstructionWithBraces();<br>
-Â clang_analyzer_eval(c9.getX() == 1);<br>
-Â clang_analyzer_eval(c9.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c9.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(c9.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
 #endif // C++11<br>
<br>
  D d1 = returnTemporaryWithVariableAndNonTrivialCopy();<br>
-Â clang_analyzer_eval(d1.getX() == 1);<br>
-Â clang_analyzer_eval(d1.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(d1.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(d1.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy();<br>
-Â clang_analyzer_eval(d2.getX() == 1);<br>
-Â clang_analyzer_eval(d2.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(d2.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(d2.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
  D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy();<br>
-Â clang_analyzer_eval(d3.getX() == 1);<br>
-Â clang_analyzer_eval(d3.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(d3.getX() == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(d3.getY() == 2); // expected-warning{{TRUE}}<br>
 }<br>
 } // namespace test_return_temporary<br>
<br>
@@ -767,19 +704,9 @@ void test(int coin) {<br>
<br>
  C c2 = coin ? C(1) : C(2);<br>
  if (coin) {<br>
-Â Â clang_analyzer_eval(c2.getX() == 1);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-2{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+Â Â clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}<br>
  } else {<br>
-Â Â clang_analyzer_eval(c2.getX() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-2{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+Â Â clang_analyzer_eval(c2.getX() == 2); // expected-warning{{TRUE}}<br>
  }<br>
 }<br>
<br>
@@ -816,16 +743,9 @@ void test_simple_temporary_with_copy() {<br>
  {<br>
   C c = C(x, y);<br>
  }<br>
-Â // Two constructors (temporary object expr and copy) and two destructors.<br>
-Â clang_analyzer_eval(x == 2);<br>
-Â clang_analyzer_eval(y == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-3{{TRUE}}<br>
-Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â // Only one constructor directly into the variable, and one destructor.<br>
+Â clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}<br>
+Â clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void test_ternary_temporary(int coin) {<br>
@@ -833,10 +753,10 @@ void test_ternary_temporary(int coin) {<br>
  {<br>
   const C &c = coin ? C(x, y) : C(z, w);<br>
  }<br>
-Â // This time each branch contains an additional elidable copy constructor.<br>
+Â // Only one constructor on every branch, and one automatic destructor.<br>
  if (coin) {<br>
-Â Â clang_analyzer_eval(x == 2);<br>
-Â Â clang_analyzer_eval(y == 2);<br>
+Â Â clang_analyzer_eval(x == 1);<br>
+Â Â clang_analyzer_eval(y == 1);<br>
 #ifdef TEMPORARY_DTORS<br>
   // expected-warning@-3{{TRUE}}<br>
   // expected-warning@-3{{TRUE}}<br>
@@ -850,8 +770,8 @@ void test_ternary_temporary(int coin) {<br>
  } else {<br>
   clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}<br>
   clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}<br>
-Â Â clang_analyzer_eval(z == 2);<br>
-Â Â clang_analyzer_eval(w == 2);<br>
+Â Â clang_analyzer_eval(z == 1);<br>
+Â Â clang_analyzer_eval(w == 1);<br>
 #ifdef TEMPORARY_DTORS<br>
   // expected-warning@-3{{TRUE}}<br>
   // expected-warning@-3{{TRUE}}<br>
@@ -867,33 +787,18 @@ void test_ternary_temporary_with_copy(in<br>
  {<br>
   C c = coin ? C(x, y) : C(z, w);<br>
  }<br>
-Â // Temporary expression, elidable copy within branch,<br>
-Â // constructor for variable - 3 total.<br>
+Â // On each branch the variable is constructed directly.<br>
  if (coin) {<br>
-Â Â clang_analyzer_eval(x == 3);<br>
-Â Â clang_analyzer_eval(y == 3);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â Â // expected-warning@-3{{TRUE}}<br>
-Â Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â Â // expected-warning@-6{{UNKNOWN}}<br>
-Â Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â Â clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}<br>
+Â Â clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}<br>
   clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}<br>
   clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}<br>
<br>
  } else {<br>
   clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}<br>
   clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}<br>
-Â Â clang_analyzer_eval(z == 3);<br>
-Â Â clang_analyzer_eval(w == 3);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â Â // expected-warning@-3{{TRUE}}<br>
-Â Â // expected-warning@-3{{TRUE}}<br>
-#else<br>
-Â Â // expected-warning@-6{{UNKNOWN}}<br>
-Â Â // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+Â Â clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}<br>
+Â Â clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}<br>
  }<br>
 }<br>
 } // namespace test_match_constructors_and_destructors<br>
@@ -928,12 +833,13 @@ void testLifetimeExtendedCall() {<br>
 }<br>
<br>
 void testCopiedCall() {<br>
-Â C c = make();<br>
-Â // Should have divided by zero in the temporary destructor.<br>
-Â clang_analyzer_warnIfReached();<br>
-#ifndef TEMPORARY_DTORS<br>
-Â Â // expected-warning@-2{{REACHABLE}}<br>
-#endif<br>
+Â {<br>
+Â Â C c = make();<br>
+Â Â // Should have elided the constructor/destructor for the temporary<br>
+Â Â clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}<br>
+Â }<br>
+Â // Should have divided by zero in the destructor.<br>
+Â clang_analyzer_warnIfReached(); // no-warning<br>
 }<br>
 } // namespace destructors_for_return_values<br>
<br>
@@ -1018,20 +924,10 @@ void test() {<br>
 #endif<br>
<br>
  S s = 20;<br>
-Â clang_analyzer_eval(s.x == 20);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-2{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(s.x == 20); // expected-warning{{TRUE}}<br>
<br>
  C c2 = s;<br>
-Â clang_analyzer_eval(c2.getX() == 20);<br>
-#ifdef TEMPORARY_DTORS<br>
-Â // expected-warning@-2{{TRUE}}<br>
-#else<br>
-Â // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+Â clang_analyzer_eval(c2.getX() == 20); // expected-warning{{TRUE}}<br>
 }<br>
 } // end namespace implicit_constructor_conversion<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>