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