Index: cfe/trunk/include/clang/Analysis/CFG.h =================================================================== --- cfe/trunk/include/clang/Analysis/CFG.h +++ cfe/trunk/include/clang/Analysis/CFG.h @@ -38,6 +38,7 @@ class ASTContext; class BinaryOperator; class CFG; +class ConstructionContext; class CXXBaseSpecifier; class CXXBindTemporaryExpr; class CXXCtorInitializer; @@ -140,94 +141,6 @@ CFGStmt() = default; }; -// This is bulky data for CFGConstructor which would not fit into the -// CFGElement's room (pair of pointers). Contains the information -// necessary to express what memory is being initialized by -// the construction. -class ConstructionContext { -public: - typedef llvm::PointerUnion TriggerTy; - -private: - // The construction site - the statement that triggered the construction - // for one of its parts. For instance, stack variable declaration statement - // triggers construction of itself or its elements if it's an array, - // new-expression triggers construction of the newly allocated object(s). - TriggerTy Trigger; - - // Sometimes a single trigger is not enough to describe the construction site. - // In this case we'd have a chain of "partial" construction contexts. - // Some examples: - // - A constructor within in an aggregate initializer list within a variable - // would have a construction context of the initializer list with the parent - // construction context of a variable. - // - A constructor for a temporary that needs to be both destroyed - // and materialized into an elidable copy constructor would have a - // construction context of a CXXBindTemporaryExpr with the parent - // construction context of a MaterializeTemproraryExpr. - // Not all of these are currently supported. - const ConstructionContext *Parent = nullptr; - - ConstructionContext() = default; - ConstructionContext(TriggerTy Trigger, const ConstructionContext *Parent) - : Trigger(Trigger), Parent(Parent) {} - -public: - static const ConstructionContext * - create(BumpVectorContext &C, TriggerTy Trigger, - const ConstructionContext *Parent = nullptr) { - ConstructionContext *CC = C.getAllocator().Allocate(); - return new (CC) ConstructionContext(Trigger, Parent); - } - - bool isNull() const { return Trigger.isNull(); } - - TriggerTy getTrigger() const { return Trigger; } - const ConstructionContext *getParent() const { return Parent; } - - const Stmt *getTriggerStmt() const { - return Trigger.dyn_cast(); - } - - const CXXCtorInitializer *getTriggerInit() const { - return Trigger.dyn_cast(); - } - - const MaterializeTemporaryExpr *getMaterializedTemporary() const { - // TODO: Be more careful to ensure that there's only one MTE around. - for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) { - if (const auto *MTE = dyn_cast_or_null( - CC->getTriggerStmt())) { - return MTE; - } - } - return nullptr; - } - - bool isSameAsPartialContext(const ConstructionContext *Other) const { - assert(Other); - return (Trigger == Other->Trigger); - } - - // See if Other is a proper initial segment of this construction context - // in terms of the parent chain - i.e. a few first parents coincide and - // then the other context terminates but our context goes further - i.e., - // we are providing the same context that the other context provides, - // and a bit more above that. - bool isStrictlyMoreSpecificThan(const ConstructionContext *Other) const { - const ConstructionContext *Self = this; - while (true) { - if (!Other) - return Self; - if (!Self || !Self->isSameAsPartialContext(Other)) - return false; - Self = Self->getParent(); - Other = Other->getParent(); - } - llvm_unreachable("The above loop can only be terminated via return!"); - } -}; - /// CFGConstructor - Represents C++ constructor call. Maintains information /// necessary to figure out what memory is being initialized by the /// constructor expression. For now this is only used by the analyzer's CFG. @@ -235,7 +148,7 @@ public: explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C) : CFGStmt(CE, Constructor) { - assert(!C->isNull()); + assert(C); Data2.setPointer(const_cast(C)); } @@ -247,22 +160,6 @@ return cast(getStmt())->getType(); } - ConstructionContext::TriggerTy getTrigger() const { - return getConstructionContext()->getTrigger(); - } - - const Stmt *getTriggerStmt() const { - return getConstructionContext()->getTriggerStmt(); - } - - const CXXCtorInitializer *getTriggerInit() const { - return getConstructionContext()->getTriggerInit(); - } - - const MaterializeTemporaryExpr *getMaterializedTemporary() const { - return getConstructionContext()->getMaterializedTemporary(); - } - private: friend class CFGElement; Index: cfe/trunk/include/clang/Analysis/ConstructionContext.h =================================================================== --- cfe/trunk/include/clang/Analysis/ConstructionContext.h +++ cfe/trunk/include/clang/Analysis/ConstructionContext.h @@ -0,0 +1,245 @@ +//===- ConstructionContext.h - CFG constructor information ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ConstructionContext class and its sub-classes, +// which represent various different ways of constructing C++ objects +// with the additional information the users may want to know about +// the constructor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H +#define LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H + +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/AST/ExprCXX.h" + +namespace clang { + +/// Construction context is a linked list of multiple layers. Layers are +/// created gradually while traversing the AST, and layers that represent +/// the outmost AST nodes are built first, while the node that immediately +/// contains the constructor would be built last and capture the previous +/// layers as its parents. Construction context captures the last layer +/// (which has links to the previous layers) and classifies the seemingly +/// arbitrary chain of layers into one of the possible ways of constructing +/// an object in C++ for user-friendly experience. +class ConstructionContextLayer { +public: + typedef llvm::PointerUnion TriggerTy; + +private: + /// The construction site - the statement that triggered the construction + /// for one of its parts. For instance, stack variable declaration statement + /// triggers construction of itself or its elements if it's an array, + /// new-expression triggers construction of the newly allocated object(s). + TriggerTy Trigger; + + /// Sometimes a single trigger is not enough to describe the construction + /// site. In this case we'd have a chain of "partial" construction context + /// layers. + /// Some examples: + /// - A constructor within in an aggregate initializer list within a variable + /// would have a construction context of the initializer list with + /// the parent construction context of a variable. + /// - A constructor for a temporary that needs to be both destroyed + /// and materialized into an elidable copy constructor would have a + /// construction context of a CXXBindTemporaryExpr with the parent + /// construction context of a MaterializeTemproraryExpr. + /// Not all of these are currently supported. + const ConstructionContextLayer *Parent = nullptr; + + ConstructionContextLayer(TriggerTy Trigger, + const ConstructionContextLayer *Parent) + : Trigger(Trigger), Parent(Parent) {} + +public: + static const ConstructionContextLayer * + create(BumpVectorContext &C, TriggerTy Trigger, + const ConstructionContextLayer *Parent = nullptr); + + const ConstructionContextLayer *getParent() const { return Parent; } + bool isLast() const { return !Parent; } + + const Stmt *getTriggerStmt() const { + return Trigger.dyn_cast(); + } + + const CXXCtorInitializer *getTriggerInit() const { + return Trigger.dyn_cast(); + } + + /// Returns true if these layers are equal as individual layers, even if + /// their parents are different. + bool isSameLayer(const ConstructionContextLayer *Other) const { + assert(Other); + return (Trigger == Other->Trigger); + } + + /// See if Other is a proper initial segment of this construction context + /// in terms of the parent chain - i.e. a few first parents coincide and + /// then the other context terminates but our context goes further - i.e., + /// we are providing the same context that the other context provides, + /// and a bit more above that. + bool isStrictlyMoreSpecificThan(const ConstructionContextLayer *Other) const; +}; + + +/// ConstructionContext's subclasses describe different ways of constructing +/// an object in C++. The context re-captures the essential parent AST nodes +/// of the CXXConstructExpr it is assigned to and presents these nodes +/// through easy-to-understand accessor methods. +class ConstructionContext { +public: + enum Kind { + SimpleVariableKind, + ConstructorInitializerKind, + NewAllocatedObjectKind, + TemporaryObjectKind, + ReturnedValueKind + }; + +protected: + Kind K; + +protected: + // Do not make public! These need to only be constructed + // via createFromLayers(). + explicit ConstructionContext(Kind K) : K(K) {} + +public: + + /// Consume the construction context layer, together with its parent layers, + /// and wrap it up into a complete construction context. + static const ConstructionContext * + createFromLayers(BumpVectorContext &C, + const ConstructionContextLayer *TopLayer); + + Kind getKind() const { return K; } +}; + +/// Represents construction into a simple local variable, eg. T var(123);. +class SimpleVariableConstructionContext : public ConstructionContext { + const DeclStmt *DS; + +public: + explicit SimpleVariableConstructionContext(const DeclStmt *DS) + : ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) { + assert(DS); + } + + const DeclStmt *getDeclStmt() const { return DS; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == SimpleVariableKind; + } +}; + +/// Represents construction into a field or a base class within a bigger object +/// via a constructor initializer, eg. T(): field(123) { ... }. +class ConstructorInitializerConstructionContext : public ConstructionContext { + const CXXCtorInitializer *I; + +public: + explicit ConstructorInitializerConstructionContext( + const CXXCtorInitializer *I) + : ConstructionContext(ConstructionContext::ConstructorInitializerKind), + I(I) { + assert(I); + } + + const CXXCtorInitializer *getCXXCtorInitializer() const { return I; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == ConstructorInitializerKind; + } +}; + +/// Represents immediate initialization of memory allocated by operator new, +/// eg. new T(123);. +class NewAllocatedObjectConstructionContext : public ConstructionContext { + const CXXNewExpr *NE; + +public: + explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE) + : ConstructionContext(ConstructionContext::NewAllocatedObjectKind), + NE(NE) { + assert(NE); + } + + const CXXNewExpr *getCXXNewExpr() const { return NE; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == NewAllocatedObjectKind; + } +}; + +/// Represents a temporary object, eg. T(123), that does not immediately cross +/// function boundaries "by value"; constructors that construct function +/// value-type arguments or values that are immediately returned from the +/// function that returns a value receive separate construction context kinds. +class TemporaryObjectConstructionContext : public ConstructionContext { + const CXXBindTemporaryExpr *BTE; + const MaterializeTemporaryExpr *MTE; + +public: + explicit TemporaryObjectConstructionContext( + const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE) + : ConstructionContext(ConstructionContext::TemporaryObjectKind), + BTE(BTE), MTE(MTE) { + // Both BTE and MTE can be null here, all combinations possible. + // Even though for now at least one should be non-null, we simply haven't + // implemented this case yet (this would be a temporary in the middle of + // nowhere that doesn't have a non-trivial destructor). + } + + /// CXXBindTemporaryExpr here is non-null as long as the temporary has + /// a non-trivial destructor. + const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { + return BTE; + } + + /// MaterializeTemporaryExpr is non-null as long as the temporary is actually + /// used after construction, eg. by binding to a reference (lifetime + /// extension), accessing a field, calling a method, or passing it into + /// a function (an elidable copy or move constructor would be a common + /// example) by reference. + const MaterializeTemporaryExpr *getMaterializedTemporaryExpr() const { + return MTE; + } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == TemporaryObjectKind; + } +}; + +/// Represents a temporary object that is being immediately returned from a +/// function by value, eg. return t; or return T(123);. In this case there is +/// always going to be a constructor at the return site. However, the usual +/// temporary-related bureaucracy (CXXBindTemporaryExpr, +/// MaterializeTemporaryExpr) is normally located in the caller function's AST. +class ReturnedValueConstructionContext : public ConstructionContext { + const ReturnStmt *RS; + +public: + explicit ReturnedValueConstructionContext(const ReturnStmt *RS) + : ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) { + assert(RS); + } + + const ReturnStmt *getReturnStmt() const { return RS; } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == ReturnedValueKind; + } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H Index: cfe/trunk/lib/Analysis/CFG.cpp =================================================================== --- cfe/trunk/lib/Analysis/CFG.cpp +++ cfe/trunk/lib/Analysis/CFG.cpp @@ -29,6 +29,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/Analysis/Support/BumpVector.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/LLVM.h" @@ -475,7 +476,7 @@ // Information about the currently visited C++ object construction site. // This is set in the construction trigger and read when the constructor // itself is being visited. - llvm::DenseMap + llvm::DenseMap ConstructionContextMap; bool badCFG = false; @@ -652,18 +653,19 @@ return Block; } - // Remember to apply \p CC when constructing the CFG element for \p CE. - void consumeConstructionContext(const ConstructionContext *CC, + // Remember to apply the construction context based on the current \p Layer + // when constructing the CFG element for \p CE. + void consumeConstructionContext(const ConstructionContextLayer *Layer, CXXConstructExpr *CE); - // Scan the child statement \p Child to find the constructor that might - // have been directly triggered by the current node, \p Trigger. If such - // constructor has been found, set current construction context to point - // to the trigger statement. The construction context will be unset once - // it is consumed when the CFG building procedure processes the - // construct-expression and adds the respective CFGConstructor element. - void findConstructionContexts(const ConstructionContext *ContextSoFar, + // Scan \p Child statement to find constructors in it, while keeping in mind + // that its parent statement is providing a partial construction context + // described by \p Layer. If a constructor is found, it would be assigned + // the context based on the layer. If an additional construction context layer + // is found, the function recurses into that. + void findConstructionContexts(const ConstructionContextLayer *Layer, Stmt *Child); + // Unset the construction context after consuming it. This is done immediately // after adding the CFGConstructor element, so there's no need to // do this manually in every Visit... function. @@ -710,7 +712,11 @@ void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) { if (BuildOpts.AddRichCXXConstructors) { - if (const ConstructionContext *CC = ConstructionContextMap.lookup(CE)) { + if (const ConstructionContextLayer *Layer = + ConstructionContextMap.lookup(CE)) { + const ConstructionContext *CC = + ConstructionContext::createFromLayers(cfg->getBumpVectorContext(), + Layer); B->appendConstructor(CE, CC, cfg->getBumpVectorContext()); cleanupConstructionContext(CE); return; @@ -1155,20 +1161,21 @@ return nullptr; } -void CFGBuilder::consumeConstructionContext(const ConstructionContext *CC, CXXConstructExpr *CE) { - if (const ConstructionContext *PreviousContext = +void CFGBuilder::consumeConstructionContext( + const ConstructionContextLayer *Layer, CXXConstructExpr *CE) { + if (const ConstructionContextLayer *PreviouslyStoredLayer = ConstructionContextMap.lookup(CE)) { // We might have visited this child when we were finding construction // contexts within its parents. - assert(PreviousContext->isStrictlyMoreSpecificThan(CC) && + assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) && "Already within a different construction context!"); } else { - ConstructionContextMap[CE] = CC; + ConstructionContextMap[CE] = Layer; } } void CFGBuilder::findConstructionContexts( - const ConstructionContext *ContextSoFar, Stmt *Child) { + const ConstructionContextLayer *Layer, Stmt *Child) { if (!BuildOpts.AddRichCXXConstructors) return; @@ -1178,36 +1185,36 @@ switch(Child->getStmtClass()) { case Stmt::CXXConstructExprClass: case Stmt::CXXTemporaryObjectExprClass: { - consumeConstructionContext(ContextSoFar, cast(Child)); + consumeConstructionContext(Layer, cast(Child)); break; } case Stmt::ExprWithCleanupsClass: { auto *Cleanups = cast(Child); - findConstructionContexts(ContextSoFar, Cleanups->getSubExpr()); + findConstructionContexts(Layer, Cleanups->getSubExpr()); break; } case Stmt::CXXFunctionalCastExprClass: { auto *Cast = cast(Child); - findConstructionContexts(ContextSoFar, Cast->getSubExpr()); + findConstructionContexts(Layer, Cast->getSubExpr()); break; } case Stmt::ImplicitCastExprClass: { auto *Cast = cast(Child); - findConstructionContexts(ContextSoFar, Cast->getSubExpr()); + findConstructionContexts(Layer, Cast->getSubExpr()); break; } case Stmt::CXXBindTemporaryExprClass: { auto *BTE = cast(Child); findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), BTE, - ContextSoFar), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), + BTE, Layer), BTE->getSubExpr()); break; } case Stmt::ConditionalOperatorClass: { auto *CO = cast(Child); - findConstructionContexts(ContextSoFar, CO->getLHS()); - findConstructionContexts(ContextSoFar, CO->getRHS()); + findConstructionContexts(Layer, CO->getLHS()); + findConstructionContexts(Layer, CO->getRHS()); break; } default: @@ -1356,7 +1363,7 @@ if (Init) { findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), I), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), I), Init); if (HasTemporaries) { @@ -2448,7 +2455,7 @@ appendStmt(Block, DS); findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), DS), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS), Init); // Keep track of the last non-null block, as 'Block' can be nulled out @@ -2642,7 +2649,7 @@ addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), R), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), R), R->getRetValue()); // If the one of the destructors does not return, we already have the Exit @@ -3015,7 +3022,7 @@ CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE, AddStmtChoice asc) { findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), MTE), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), MTE), MTE->getTemporary()); return VisitStmt(MTE, asc); @@ -4002,7 +4009,7 @@ appendStmt(Block, E); findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), E), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), E), E->getSubExpr()); // We do not want to propagate the AlwaysAdd property. @@ -4025,7 +4032,7 @@ appendStmt(Block, NE); findConstructionContexts( - ConstructionContext::create(cfg->getBumpVectorContext(), NE), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), NE), const_cast(NE->getConstructExpr())); if (NE->getInitializer()) @@ -4752,19 +4759,44 @@ } else if (const CXXConstructExpr *CCE = dyn_cast(S)) { OS << " (CXXConstructExpr, "; if (Optional CE = E.getAs()) { - // TODO: Refactor into ConstructionContext::print(). - if (const Stmt *S = CE->getTriggerStmt()) - Helper.handledStmt(const_cast(S), OS); - else if (const CXXCtorInitializer *I = CE->getTriggerInit()) - print_initializer(OS, Helper, I); - else - llvm_unreachable("Unexpected trigger kind!"); - OS << ", "; - if (const Stmt *S = CE->getMaterializedTemporary()) { - if (S != CE->getTriggerStmt()) { - Helper.handledStmt(const_cast(S), OS); - OS << ", "; - } + const ConstructionContext *CC = CE->getConstructionContext(); + const Stmt *S1 = nullptr, *S2 = nullptr; + switch (CC->getKind()) { + case ConstructionContext::ConstructorInitializerKind: { + const auto *ICC = cast(CC); + print_initializer(OS, Helper, ICC->getCXXCtorInitializer()); + OS << ", "; + break; + } + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast(CC); + S1 = DSCC->getDeclStmt(); + break; + } + case ConstructionContext::NewAllocatedObjectKind: { + const auto *NECC = cast(CC); + S1 = NECC->getCXXNewExpr(); + break; + } + case ConstructionContext::ReturnedValueKind: { + const auto *RSCC = cast(CC); + S1 = RSCC->getReturnStmt(); + break; + } + case ConstructionContext::TemporaryObjectKind: { + const auto *TOCC = cast(CC); + S1 = TOCC->getCXXBindTemporaryExpr(); + S2 = TOCC->getMaterializedTemporaryExpr(); + break; + } + } + if (S1) { + Helper.handledStmt(const_cast(S1), OS); + OS << ", "; + } + if (S2) { + Helper.handledStmt(const_cast(S2), OS); + OS << ", "; } } OS << CCE->getType().getAsString() << ")"; Index: cfe/trunk/lib/Analysis/CMakeLists.txt =================================================================== --- cfe/trunk/lib/Analysis/CMakeLists.txt +++ cfe/trunk/lib/Analysis/CMakeLists.txt @@ -11,6 +11,7 @@ CallGraph.cpp CloneDetection.cpp CocoaConventions.cpp + ConstructionContext.cpp Consumed.cpp CodeInjector.cpp Dominators.cpp Index: cfe/trunk/lib/Analysis/ConstructionContext.cpp =================================================================== --- cfe/trunk/lib/Analysis/ConstructionContext.cpp +++ cfe/trunk/lib/Analysis/ConstructionContext.cpp @@ -0,0 +1,92 @@ +//===- ConstructionContext.cpp - CFG constructor information --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ConstructionContext class and its sub-classes, +// which represent various different ways of constructing C++ objects +// with the additional information the users may want to know about +// the constructor. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/ConstructionContext.h" + +using namespace clang; + +const ConstructionContextLayer * +ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger, + const ConstructionContextLayer *Parent) { + ConstructionContextLayer *CC = + C.getAllocator().Allocate(); + return new (CC) ConstructionContextLayer(Trigger, Parent); +} + +bool ConstructionContextLayer::isStrictlyMoreSpecificThan( + const ConstructionContextLayer *Other) const { + const ConstructionContextLayer *Self = this; + while (true) { + if (!Other) + return Self; + if (!Self || !Self->isSameLayer(Other)) + return false; + Self = Self->getParent(); + Other = Other->getParent(); + } + llvm_unreachable("The above loop can only be terminated via return!"); +} + +const ConstructionContext *ConstructionContext::createFromLayers( + BumpVectorContext &C, const ConstructionContextLayer *TopLayer) { + // Before this point all we've had was a stockpile of arbitrary layers. + // Now validate that it is shaped as one of the finite amount of expected + // patterns. + if (const Stmt *S = TopLayer->getTriggerStmt()) { + if (const auto *DS = dyn_cast(S)) { + assert(TopLayer->isLast()); + auto *CC = + C.getAllocator().Allocate(); + return new (CC) SimpleVariableConstructionContext(DS); + } else if (const auto *NE = dyn_cast(S)) { + assert(TopLayer->isLast()); + auto *CC = + C.getAllocator().Allocate(); + return new (CC) NewAllocatedObjectConstructionContext(NE); + } else if (const auto *BTE = dyn_cast(S)) { + const MaterializeTemporaryExpr *MTE = nullptr; + assert(BTE->getType().getCanonicalType() + ->getAsCXXRecordDecl()->hasNonTrivialDestructor()); + // For temporaries with destructors, there may or may not be + // lifetime extension on the parent layer. + if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) { + assert(ParentLayer->isLast()); + MTE = cast(ParentLayer->getTriggerStmt()); + } + auto *CC = + C.getAllocator().Allocate(); + return new (CC) TemporaryObjectConstructionContext(BTE, MTE); + } else if (const auto *MTE = dyn_cast(S)) { + assert(MTE->getType().getCanonicalType() + ->getAsCXXRecordDecl()->hasTrivialDestructor()); + assert(TopLayer->isLast()); + auto *CC = + C.getAllocator().Allocate(); + return new (CC) TemporaryObjectConstructionContext(nullptr, MTE); + } else if (const auto *RS = dyn_cast(S)) { + assert(TopLayer->isLast()); + auto *CC = + C.getAllocator().Allocate(); + return new (CC) ReturnedValueConstructionContext(RS); + } + } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) { + assert(TopLayer->isLast()); + auto *CC = + C.getAllocator().Allocate(); + return new (CC) ConstructorInitializerConstructionContext(I); + } + llvm_unreachable("Unexpected construction context!"); +} Index: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" @@ -111,47 +112,20 @@ // See if we're constructing an existing region by looking at the // current construction context. if (CC) { - if (const Stmt *TriggerStmt = CC->getTriggerStmt()) { - if (const CXXNewExpr *CNE = dyn_cast(TriggerStmt)) { - if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { - // TODO: Detect when the allocator returns a null pointer. - // Constructor shall not be called in this case. - if (const SubRegion *MR = dyn_cast_or_null( - getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) { - if (CNE->isArray()) { - // TODO: In fact, we need to call the constructor for every - // allocated element, not just the first one! - CallOpts.IsArrayCtorOrDtor = true; - return getStoreManager().GetElementZeroRegion( - MR, CNE->getType()->getPointeeType()); - } - return MR; - } - } - } else if (auto *DS = dyn_cast(TriggerStmt)) { - const auto *Var = cast(DS->getSingleDecl()); - SVal LValue = State->getLValue(Var, LCtx); - QualType Ty = Var->getType(); - LValue = makeZeroElementRegion(State, LValue, Ty, - CallOpts.IsArrayCtorOrDtor); - return LValue.getAsRegion(); - } else if (isa(TriggerStmt)) { - // TODO: We should construct into a CXXBindTemporaryExpr or a - // MaterializeTemporaryExpr around the call-expression on the previous - // stack frame. Currently we re-bind the temporary to the correct region - // later, but that's not semantically correct. This of course does not - // apply when we're in the top frame. But if we are in an inlined - // function, we should be able to take the call-site CFG element, - // and it should contain (but right now it wouldn't) some sort of - // construction context that'd give us the right temporary expression. - CallOpts.IsTemporaryCtorOrDtor = true; - return MRMgr.getCXXTempObjectRegion(CE, LCtx); - } else if (isa(TriggerStmt)) { - CallOpts.IsTemporaryCtorOrDtor = true; - return MRMgr.getCXXTempObjectRegion(CE, LCtx); - } - // TODO: Consider other directly initialized elements. - } else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) { + switch (CC->getKind()) { + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast(CC); + const auto *DS = DSCC->getDeclStmt(); + const auto *Var = cast(DS->getSingleDecl()); + SVal LValue = State->getLValue(Var, LCtx); + QualType Ty = Var->getType(); + LValue = + makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor); + return LValue.getAsRegion(); + } + case ConstructionContext::ConstructorInitializerKind: { + const auto *ICC = cast(CC); + const auto *Init = ICC->getCXXCtorInitializer(); assert(Init->isAnyMemberInitializer()); const CXXMethodDecl *CurCtor = cast(LCtx->getDecl()); Loc ThisPtr = @@ -173,10 +147,45 @@ CallOpts.IsArrayCtorOrDtor); return FieldVal.getAsRegion(); } - - // FIXME: This will eventually need to handle new-expressions as well. - // Don't forget to update the pre-constructor initialization code in - // ExprEngine::VisitCXXConstructExpr. + case ConstructionContext::NewAllocatedObjectKind: { + if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + const auto *NECC = cast(CC); + const auto *NE = NECC->getCXXNewExpr(); + // TODO: Detect when the allocator returns a null pointer. + // Constructor shall not be called in this case. + if (const SubRegion *MR = dyn_cast_or_null( + getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) { + if (NE->isArray()) { + // TODO: In fact, we need to call the constructor for every + // allocated element, not just the first one! + CallOpts.IsArrayCtorOrDtor = true; + return getStoreManager().GetElementZeroRegion( + MR, NE->getType()->getPointeeType()); + } + return MR; + } + } + break; + } + case ConstructionContext::TemporaryObjectKind: { + // TODO: Support temporaries lifetime-extended via static references. + // They'd need a getCXXStaticTempObjectRegion(). + CallOpts.IsTemporaryCtorOrDtor = true; + return MRMgr.getCXXTempObjectRegion(CE, LCtx); + } + case ConstructionContext::ReturnedValueKind: { + // TODO: We should construct into a CXXBindTemporaryExpr or a + // MaterializeTemporaryExpr around the call-expression on the previous + // stack frame. Currently we re-bind the temporary to the correct region + // later, but that's not semantically correct. This of course does not + // apply when we're in the top frame. But if we are in an inlined + // function, we should be able to take the call-site CFG element, + // and it should contain (but right now it wouldn't) some sort of + // construction context that'd give us the right temporary expression. + CallOpts.IsTemporaryCtorOrDtor = true; + return MRMgr.getCXXTempObjectRegion(CE, LCtx); + } + } } // If we couldn't find an existing region to construct into, assume we're // constructing a temporary. Notify the caller of our failure. @@ -227,6 +236,7 @@ EvalCallOptions CallOpts; auto C = getCurrentCFGElement().getAs(); + assert(C || getCurrentCFGElement().getAs()); const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; const CXXBindTemporaryExpr *BTE = nullptr; @@ -235,15 +245,27 @@ switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts); - if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() && - !CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion && - CallOpts.IsTemporaryCtorOrDtor) { - MTE = CC->getMaterializedTemporary(); - if (!MTE || MTE->getStorageDuration() == SD_FullExpression) { - // If the temporary is lifetime-extended, don't save the BTE, - // because we don't need a temporary destructor, but an automatic - // destructor. The cast may fail because it may as well be a ReturnStmt. - BTE = dyn_cast(CC->getTriggerStmt()); + + // In case of temporary object construction, extract data necessary for + // destruction and lifetime extension. + if (const auto *TCC = + dyn_cast_or_null(CC)) { + assert(CallOpts.IsTemporaryCtorOrDtor); + assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion); + if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) { + BTE = TCC->getCXXBindTemporaryExpr(); + MTE = TCC->getMaterializedTemporaryExpr(); + if (!BTE) { + // FIXME: lifetime extension for temporaries without destructors + // is not implemented yet. + MTE = nullptr; + } + if (MTE && MTE->getStorageDuration() != SD_FullExpression) { + // If the temporary is lifetime-extended, don't save the BTE, + // because we don't need a temporary destructor, but an automatic + // destructor. + BTE = nullptr; + } } } break; Index: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -16,6 +16,7 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "llvm/ADT/SmallSet.h" @@ -635,10 +636,11 @@ const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); - auto CC = getCurrentCFGElement().getAs(); - const Stmt *ParentExpr = CC ? CC->getTriggerStmt() : nullptr; + auto CCE = getCurrentCFGElement().getAs(); + const ConstructionContext *CC = CCE ? CCE->getConstructionContext() + : nullptr; - if (ParentExpr && isa(ParentExpr) && + if (CC && isa(CC) && !Opts.mayInlineCXXAllocator()) return CIP_DisallowedOnce;