diff options
Diffstat (limited to 'lib/Analysis/CFG.cpp')
-rw-r--r-- | lib/Analysis/CFG.cpp | 644 |
1 files changed, 562 insertions, 82 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 714b85d3a9fff..8a3ab15458dd7 100644 --- a/lib/Analysis/CFG.cpp +++ b/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" @@ -233,6 +234,13 @@ public: assert(VarIter != 0 && "Iterator has invalid value of VarIter member"); return &Scope->Vars[VarIter - 1]; } + + const VarDecl *getFirstVarInScope() const { + assert(Scope && "Dereferencing invalid iterator is not allowed"); + assert(VarIter != 0 && "Iterator has invalid value of VarIter member"); + return Scope->Vars[0]; + } + VarDecl *operator*() const { return *this->operator->(); } @@ -266,6 +274,7 @@ public: int distance(const_iterator L); const_iterator shared_parent(const_iterator L); + bool pointsToFirstDeclaredVar() { return VarIter == 1; } }; private: @@ -472,6 +481,15 @@ class CFGBuilder { using LabelSetTy = llvm::SmallSetVector<LabelDecl *, 8>; LabelSetTy AddressTakenLabels; + // Information about the currently visited C++ object construction site. + // This is set in the construction trigger and read when the constructor + // or a function that returns an object by value is being visited. + llvm::DenseMap<Expr *, const ConstructionContextLayer *> + ConstructionContextMap; + + using DeclsWithEndedScopeSetTy = llvm::SmallSetVector<VarDecl *, 16>; + DeclsWithEndedScopeSetTy DeclsWithEndedScope; + bool badCFG = false; const CFG::BuildOptions &BuildOpts; @@ -491,7 +509,8 @@ public: explicit CFGBuilder(ASTContext *astContext, const CFG::BuildOptions &buildOpts) : Context(astContext), cfg(new CFG()), // crew a new CFG - BuildOpts(buildOpts) {} + ConstructionContextMap(), BuildOpts(buildOpts) {} + // buildCFG - Used by external clients to construct the CFG. std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement); @@ -541,6 +560,8 @@ private: Stmt *Term, CFGBlock *TrueBlock, CFGBlock *FalseBlock); + CFGBlock *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE, + AddStmtChoice asc); CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc); CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S); CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S); @@ -566,6 +587,12 @@ private: CFGBlock *VisitChildren(Stmt *S); CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); + void maybeAddScopeBeginForVarDecl(CFGBlock *B, const VarDecl *VD, + const Stmt *S) { + if (ScopePos && (VD == ScopePos.getFirstVarInScope())) + appendScopeBegin(B, VD, S); + } + /// When creating the CFG for temporary destructors, we want to mirror the /// branch structure of the corresponding constructor calls. /// Thus, while visiting a statement for temporary destructors, we keep a @@ -643,6 +670,24 @@ private: return Block; } + // 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, + Expr *E); + + // 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 or CFGCXXRecordTypedCall element, so + // there's no need to do this manually in every Visit... function. + void cleanupConstructionContext(Expr *E); + void autoCreateBlock() { if (!Block) Block = createBlock(); } CFGBlock *createBlock(bool add_successor = true); CFGBlock *createNoReturnBlock(); @@ -660,6 +705,11 @@ private: void addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); + void addScopesEnd(LocalScope::const_iterator B, LocalScope::const_iterator E, + Stmt *S); + + void getDeclsWithEndedScope(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S); // Local scopes creation. LocalScope* createOrReuseLocalScope(LocalScope* Scope); @@ -682,6 +732,45 @@ private: B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext()); } + void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) { + if (BuildOpts.AddRichCXXConstructors) { + if (const ConstructionContextLayer *Layer = + ConstructionContextMap.lookup(CE)) { + cleanupConstructionContext(CE); + if (const auto *CC = ConstructionContext::createFromLayers( + cfg->getBumpVectorContext(), Layer)) { + B->appendConstructor(CE, CC, cfg->getBumpVectorContext()); + return; + } + } + } + + // No valid construction context found. Fall back to statement. + B->appendStmt(CE, cfg->getBumpVectorContext()); + } + + void appendCall(CFGBlock *B, CallExpr *CE) { + if (alwaysAdd(CE) && cachedEntry) + cachedEntry->second = B; + + if (BuildOpts.AddRichCXXConstructors) { + if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) { + if (const ConstructionContextLayer *Layer = + ConstructionContextMap.lookup(CE)) { + cleanupConstructionContext(CE); + if (const auto *CC = ConstructionContext::createFromLayers( + cfg->getBumpVectorContext(), Layer)) { + B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext()); + return; + } + } + } + } + + // No valid construction context found. Fall back to statement. + B->appendStmt(CE, cfg->getBumpVectorContext()); + } + void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { B->appendInitializer(I, cfg->getBumpVectorContext()); } @@ -725,6 +814,11 @@ private: LocalScope::const_iterator B, LocalScope::const_iterator E); + const VarDecl * + prependAutomaticObjScopeEndWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, + LocalScope::const_iterator E); + void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) { B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable), cfg->getBumpVectorContext()); @@ -737,7 +831,27 @@ private: cfg->getBumpVectorContext()); } - /// \brief Find a relational comparison with an expression evaluating to a + void appendScopeBegin(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeBegin(VD, S, cfg->getBumpVectorContext()); + } + + void prependScopeBegin(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->prependScopeBegin(VD, S, cfg->getBumpVectorContext()); + } + + void appendScopeEnd(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->appendScopeEnd(VD, S, cfg->getBumpVectorContext()); + } + + void prependScopeEnd(CFGBlock *B, const VarDecl *VD, const Stmt *S) { + if (BuildOpts.AddScopes) + B->prependScopeEnd(VD, S, cfg->getBumpVectorContext()); + } + + /// Find a relational comparison with an expression evaluating to a /// boolean and a constant other than 0 and 1. /// e.g. if ((x < y) == 10) TryResult checkIncorrectRelationalOperator(const BinaryOperator *B) { @@ -850,7 +964,7 @@ private: } } - /// \brief Find a pair of comparison expressions with or without parentheses + /// Find a pair of comparison expressions with or without parentheses /// with a shared variable and constants and a logical operator between them /// that always evaluates to either true or false. /// e.g. if (x != 3 || x != 4) @@ -1006,7 +1120,7 @@ private: return evaluateAsBooleanConditionNoCache(S); } - /// \brief Evaluate as boolean \param E without using the cache. + /// Evaluate as boolean \param E without using the cache. TryResult evaluateAsBooleanConditionNoCache(Expr *E) { if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(E)) { if (Bop->isLogicalOp()) { @@ -1116,6 +1230,127 @@ static const VariableArrayType *FindVA(const Type *t) { return nullptr; } +void CFGBuilder::consumeConstructionContext( + const ConstructionContextLayer *Layer, Expr *E) { + if (const ConstructionContextLayer *PreviouslyStoredLayer = + ConstructionContextMap.lookup(E)) { + (void)PreviouslyStoredLayer; + // We might have visited this child when we were finding construction + // contexts within its parents. + assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) && + "Already within a different construction context!"); + } else { + ConstructionContextMap[E] = Layer; + } +} + +void CFGBuilder::findConstructionContexts( + const ConstructionContextLayer *Layer, Stmt *Child) { + if (!BuildOpts.AddRichCXXConstructors) + return; + + if (!Child) + return; + + auto withExtraLayer = [this, Layer](Stmt *S) { + return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S, + Layer); + }; + + switch(Child->getStmtClass()) { + case Stmt::CXXConstructExprClass: + case Stmt::CXXTemporaryObjectExprClass: { + // Support pre-C++17 copy elision AST. + auto *CE = cast<CXXConstructExpr>(Child); + if (BuildOpts.MarkElidedCXXConstructors && CE->isElidable()) { + findConstructionContexts(withExtraLayer(CE), CE->getArg(0)); + } + + consumeConstructionContext(Layer, CE); + break; + } + // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr. + // FIXME: An isa<> would look much better but this whole switch is a + // workaround for an internal compiler error in MSVC 2015 (see r326021). + case Stmt::CallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::UserDefinedLiteralClass: { + auto *CE = cast<CallExpr>(Child); + if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) + consumeConstructionContext(Layer, CE); + break; + } + case Stmt::ExprWithCleanupsClass: { + auto *Cleanups = cast<ExprWithCleanups>(Child); + findConstructionContexts(Layer, Cleanups->getSubExpr()); + break; + } + case Stmt::CXXFunctionalCastExprClass: { + auto *Cast = cast<CXXFunctionalCastExpr>(Child); + findConstructionContexts(Layer, Cast->getSubExpr()); + break; + } + case Stmt::ImplicitCastExprClass: { + auto *Cast = cast<ImplicitCastExpr>(Child); + // Should we support other implicit cast kinds? + switch (Cast->getCastKind()) { + case CK_NoOp: + case CK_ConstructorConversion: + findConstructionContexts(Layer, Cast->getSubExpr()); + default: + break; + } + break; + } + case Stmt::CXXBindTemporaryExprClass: { + auto *BTE = cast<CXXBindTemporaryExpr>(Child); + findConstructionContexts(withExtraLayer(BTE), BTE->getSubExpr()); + break; + } + case Stmt::MaterializeTemporaryExprClass: { + // Normally we don't want to search in MaterializeTemporaryExpr because + // it indicates the beginning of a temporary object construction context, + // so it shouldn't be found in the middle. However, if it is the beginning + // of an elidable copy or move construction context, we need to include it. + if (const auto *CE = + dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) { + if (CE->isElidable()) { + auto *MTE = cast<MaterializeTemporaryExpr>(Child); + findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr()); + } + } + break; + } + case Stmt::ConditionalOperatorClass: { + auto *CO = cast<ConditionalOperator>(Child); + if (!dyn_cast_or_null<MaterializeTemporaryExpr>(Layer->getTriggerStmt())) { + // If the object returned by the conditional operator is not going to be a + // temporary object that needs to be immediately materialized, then + // it must be C++17 with its mandatory copy elision. Do not yet promise + // to support this case. + assert(!CO->getType()->getAsCXXRecordDecl() || CO->isGLValue() || + Context->getLangOpts().CPlusPlus17); + break; + } + findConstructionContexts(Layer, CO->getLHS()); + findConstructionContexts(Layer, CO->getRHS()); + break; + } + default: + break; + } +} + +void CFGBuilder::cleanupConstructionContext(Expr *E) { + assert(BuildOpts.AddRichCXXConstructors && + "We should not be managing construction contexts!"); + assert(ConstructionContextMap.count(E) && + "Cannot exit construction context without the context!"); + ConstructionContextMap.erase(E); +} + + /// BuildCFG - Constructs a CFG from an AST (a Stmt*). The AST can represent an /// arbitrary statement. Examples include a single expression or a function /// body (compound statement). The ownership of the returned CFG is @@ -1176,6 +1411,9 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { JT.scopePosition); prependAutomaticObjDtorsWithTerminator(B, I->scopePosition, JT.scopePosition); + const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator( + B, I->scopePosition, JT.scopePosition); + appendScopeBegin(JT.block, VD, G); addSuccessor(B, JT.block); } @@ -1196,6 +1434,10 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { // Create an empty entry block that has no predecessors. cfg->setEntry(createBlock()); + if (BuildOpts.AddRichCXXConstructors) + assert(ConstructionContextMap.empty() && + "Not all construction contexts were cleaned up!"); + return std::move(cfg); } @@ -1243,6 +1485,10 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { appendInitializer(Block, I); if (Init) { + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), I), + Init); + if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit // generating destructors for the second time. @@ -1267,21 +1513,20 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { return Block; } -/// \brief Retrieve the type of the temporary object whose lifetime was +/// Retrieve the type of the temporary object whose lifetime was /// extended by a local reference with the given initializer. -static QualType getReferenceInitTemporaryType(ASTContext &Context, - const Expr *Init, +static QualType getReferenceInitTemporaryType(const Expr *Init, bool *FoundMTE = nullptr) { while (true) { // Skip parentheses. Init = Init->IgnoreParens(); - + // Skip through cleanups. if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) { Init = EWC->getSubExpr(); continue; } - + // Skip through the temporary-materialization expression. if (const MaterializeTemporaryExpr *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) { @@ -1290,26 +1535,17 @@ static QualType getReferenceInitTemporaryType(ASTContext &Context, *FoundMTE = true; continue; } - - // Skip derived-to-base and no-op casts. - if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) { - if ((CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) && - Init->getType()->isRecordType()) { - Init = CE->getSubExpr(); - continue; - } - } - - // Skip member accesses into rvalues. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) { - if (!ME->isArrow() && ME->getBase()->isRValue()) { - Init = ME->getBase(); - continue; - } + + // Skip sub-object accesses into rvalues. + SmallVector<const Expr *, 2> CommaLHSs; + SmallVector<SubobjectAdjustment, 2> Adjustments; + const Expr *SkippedInit = + Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); + if (SkippedInit != Init) { + Init = SkippedInit; + continue; } - + break; } @@ -1325,9 +1561,34 @@ void CFGBuilder::addLoopExit(const Stmt *LoopStmt){ appendLoopExit(Block, LoopStmt); } +void CFGBuilder::getDeclsWithEndedScope(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (!BuildOpts.AddScopes) + return; + + if (B == E) + return; + + // To go from B to E, one first goes up the scopes from B to P + // then sideways in one scope from P to P' and then down + // the scopes from P' to E. + // The lifetime of all objects between B and P end. + LocalScope::const_iterator P = B.shared_parent(E); + int Dist = B.distance(P); + if (Dist <= 0) + return; + + for (LocalScope::const_iterator I = B; I != P; ++I) + if (I.pointsToFirstDeclaredVar()) + DeclsWithEndedScope.insert(*I); +} + void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S) { + getDeclsWithEndedScope(B, E, S); + if (BuildOpts.AddScopes) + addScopesEnd(B, E, S); if (BuildOpts.AddImplicitDtors) addAutomaticObjDtors(B, E, S); if (BuildOpts.AddLifetime) @@ -1379,6 +1640,23 @@ void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, appendLifetimeEnds(Block, *I, S); } +/// Add to current block markers for ending scopes. +void CFGBuilder::addScopesEnd(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + // If implicit destructors are enabled, we'll add scope ends in + // addAutomaticObjDtors. + if (BuildOpts.AddImplicitDtors) + return; + + autoCreateBlock(); + + for (auto I = DeclsWithEndedScope.rbegin(), E = DeclsWithEndedScope.rend(); + I != E; ++I) + appendScopeEnd(Block, *I, S); + + return; +} + /// addAutomaticObjDtors - Add to current block automatic objects destructors /// for objects in range of local scope positions. Use S as trigger statement /// for destructors. @@ -1402,12 +1680,21 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(), E = Decls.rend(); I != E; ++I) { + if (hasTrivialDestructor(*I)) { + // If AddScopes is enabled and *I is a first variable in a scope, add a + // ScopeEnd marker in a Block. + if (BuildOpts.AddScopes && DeclsWithEndedScope.count(*I)) { + autoCreateBlock(); + appendScopeEnd(Block, *I, S); + } + continue; + } // If this destructor is marked as a no-return destructor, we need to // create a new block for the destructor which does not have as a successor // anything built thus far: control won't flow out of this block. QualType Ty = (*I)->getType(); if (Ty->isReferenceType()) { - Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit()); + Ty = getReferenceInitTemporaryType((*I)->getInit()); } Ty = Context->getBaseElementType(Ty); @@ -1416,6 +1703,9 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, else autoCreateBlock(); + // Add ScopeEnd just after automatic obj destructor. + if (BuildOpts.AddScopes && DeclsWithEndedScope.count(*I)) + appendScopeEnd(Block, *I, S); appendAutomaticObjDtor(Block, *I, S); } } @@ -1478,7 +1768,8 @@ LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement /// that should create implicit scope (e.g. if/else substatements). void CFGBuilder::addLocalScopeForStmt(Stmt *S) { - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return; LocalScope *Scope = nullptr; @@ -1503,7 +1794,8 @@ void CFGBuilder::addLocalScopeForStmt(Stmt *S) { /// reuse Scope if not NULL. LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope) { - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return Scope; for (auto *DI : DS->decls()) @@ -1515,7 +1807,7 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { // Check for const references bound to temporary. Set type to pointee. QualType QT = VD->getType(); - if (QT.getTypePtr()->isReferenceType()) { + if (QT->isReferenceType()) { // Attempt to determine whether this declaration lifetime-extends a // temporary. // @@ -1525,12 +1817,16 @@ bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { // MaterializeTemporaryExpr instead. const Expr *Init = VD->getInit(); - if (!Init) + if (!Init) { + // Probably an exception catch-by-reference variable. + // FIXME: It doesn't really mean that the object has a trivial destructor. + // Also are there other cases? return true; + } - // Lifetime-extending a temporary. + // Lifetime-extending a temporary? bool FoundMTE = false; - QT = getReferenceInitTemporaryType(*Context, Init, &FoundMTE); + QT = getReferenceInitTemporaryType(Init, &FoundMTE); if (!FoundMTE) return true; } @@ -1555,7 +1851,8 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope) { assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && "AddImplicitDtors and AddLifetime cannot be used at the same time"); - if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) return Scope; // Check if variable is local. @@ -1568,7 +1865,7 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, } if (BuildOpts.AddImplicitDtors) { - if (!hasTrivialDestructor(VD)) { + if (!hasTrivialDestructor(VD) || BuildOpts.AddScopes) { // Add the variable to scope Scope = createOrReuseLocalScope(Scope); Scope->addVar(VD); @@ -1628,6 +1925,26 @@ void CFGBuilder::prependAutomaticObjLifetimeWithTerminator( InsertPos = Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminator()); } +/// prependAutomaticObjScopeEndWithTerminator - Prepend scope end CFGElements for +/// variables with automatic storage duration to CFGBlock's elements vector. +/// Elements will be prepended to physical beginning of the vector which +/// happens to be logical end. Use blocks terminator as statement that specifies +/// where scope ends. +const VarDecl * +CFGBuilder::prependAutomaticObjScopeEndWithTerminator( + CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) { + if (!BuildOpts.AddScopes) + return nullptr; + BumpVectorContext &C = cfg->getBumpVectorContext(); + CFGBlock::iterator InsertPos = + Blk->beginScopeEndInsert(Blk->end(), 1, C); + LocalScope::const_iterator PlaceToInsert = B; + for (LocalScope::const_iterator I = B; I != E; ++I) + PlaceToInsert = I; + Blk->insertScopeEnd(InsertPos, *PlaceToInsert, Blk->getTerminator()); + return *PlaceToInsert; +} + /// Visit - Walk the subtree of a statement and add extra /// blocks for ternary operators, &&, and ||. We also process "," and /// DeclStmts (which may contain nested control-flow). @@ -1756,6 +2073,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::LambdaExprClass: return VisitLambdaExpr(cast<LambdaExpr>(S), asc); + case Stmt::MaterializeTemporaryExprClass: + return VisitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(S), + asc); + case Stmt::MemberExprClass: return VisitMemberExpr(cast<MemberExpr>(S), asc); @@ -2045,7 +2366,7 @@ static bool CanThrow(Expr *E, ASTContext &Ctx) { if (FT) { if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT)) if (!isUnresolvedExceptionSpec(Proto->getExceptionSpecType()) && - Proto->isNothrow(Ctx)) + Proto->isNothrow()) return false; } return true; @@ -2062,6 +2383,13 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { if (!boundType.isNull()) calleeType = boundType; } + // FIXME: Once actually implemented, this construction context layer should + // include the number of the argument as well. + for (auto Arg: C->arguments()) { + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), Arg); + } + // If this is a call to a no-return function, this stops the block here. bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn(); @@ -2078,7 +2406,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { bool OmitArguments = false; if (FunctionDecl *FD = C->getDirectCallee()) { - if (FD->isNoReturn()) + if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context)) NoReturn = true; if (FD->hasAttr<NoThrowAttr>()) AddEHEdge = false; @@ -2098,7 +2426,10 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { } if (!NoReturn && !AddEHEdge) { - return VisitStmt(C, asc.withAlwaysAdd(true)); + autoCreateBlock(); + appendCall(Block, C); + + return VisitChildren(C); } if (Block) { @@ -2112,7 +2443,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { else Block = createBlock(); - appendStmt(Block, C); + appendCall(Block, C); if (AddEHEdge) { // Add exceptional edges. @@ -2326,7 +2657,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { autoCreateBlock(); appendStmt(Block, DS); - + + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS), + Init); + // Keep track of the last non-null block, as 'Block' can be nulled out // if the initializer expression is something like a 'while' in a // statement-expression. @@ -2353,6 +2688,8 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { LastBlock = newBlock; } + maybeAddScopeBeginForVarDecl(Block, VD, DS); + // Remove variable from local scope. if (ScopePos && VD == *ScopePos) ++ScopePos; @@ -2517,6 +2854,10 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), R), + R->getRetValue()); + // If the one of the destructors does not return, we already have the Exit // block as a successor. if (!Block->hasNoReturnElement()) @@ -2813,6 +3154,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { do { Expr *C = F->getCond(); + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); // Specially handle logical operators, which have a slightly // more optimal CFG representation. @@ -2843,9 +3185,16 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { if (VarDecl *VD = F->getConditionVariable()) { if (Expr *Init = VD->getInit()) { autoCreateBlock(); - appendStmt(Block, F->getConditionVariableDeclStmt()); + const DeclStmt *DS = F->getConditionVariableDeclStmt(); + assert(DS->isSingleDecl()); + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), + const_cast<DeclStmt *>(DS)), + Init); + appendStmt(Block, DS); EntryConditionBlock = addStmt(Init); assert(Block == EntryConditionBlock); + maybeAddScopeBeginForVarDecl(EntryConditionBlock, VD, C); } } @@ -2872,6 +3221,8 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // If the loop contains initialization, create a new block for those // statements. This block can also contain statements that precede the loop. if (Stmt *I = F->getInit()) { + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + ScopePos = LoopBeginScopePos; Block = createBlock(); return addStmt(I); } @@ -2883,6 +3234,16 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { return EntryConditionBlock; } +CFGBlock * +CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE, + AddStmtChoice asc) { + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), MTE), + MTE->getTemporary()); + + return VisitStmt(MTE, asc); +} + CFGBlock *CFGBuilder::VisitMemberExpr(MemberExpr *M, AddStmtChoice asc) { if (asc.alwaysAdd(*this, M)) { autoCreateBlock(); @@ -3155,9 +3516,16 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { if (VarDecl *VD = W->getConditionVariable()) { if (Expr *Init = VD->getInit()) { autoCreateBlock(); - appendStmt(Block, W->getConditionVariableDeclStmt()); + const DeclStmt *DS = W->getConditionVariableDeclStmt(); + assert(DS->isSingleDecl()); + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), + const_cast<DeclStmt *>(DS)), + Init); + appendStmt(Block, DS); EntryConditionBlock = addStmt(Init); assert(Block == EntryConditionBlock); + maybeAddScopeBeginForVarDecl(EntryConditionBlock, VD, C); } } @@ -3483,6 +3851,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { autoCreateBlock(); appendStmt(Block, Terminator->getConditionVariableDeclStmt()); LastBlock = addStmt(Init); + maybeAddScopeBeginForVarDecl(LastBlock, VD, Init); } } @@ -3863,6 +4232,10 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, autoCreateBlock(); appendStmt(Block, E); + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), E), + E->getSubExpr()); + // We do not want to propagate the AlwaysAdd property. asc = asc.withAlwaysAdd(false); } @@ -3872,7 +4245,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc) { autoCreateBlock(); - appendStmt(Block, C); + appendConstructor(Block, C); return VisitChildren(C); } @@ -3882,15 +4255,23 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE, autoCreateBlock(); appendStmt(Block, NE); + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), NE), + const_cast<CXXConstructExpr *>(NE->getConstructExpr())); + if (NE->getInitializer()) Block = Visit(NE->getInitializer()); + if (BuildOpts.AddCXXNewAllocator) appendNewAllocator(Block, NE); + if (NE->isArray()) Block = Visit(NE->getArraySize()); + for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(), E = NE->placement_arg_end(); I != E; ++I) Block = Visit(*I); + return Block; } @@ -3925,7 +4306,7 @@ CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, AddStmtChoice asc) { autoCreateBlock(); - appendStmt(Block, C); + appendConstructor(Block, C); return VisitChildren(C); } @@ -4027,9 +4408,11 @@ tryAgain: auto *LE = cast<LambdaExpr>(E); CFGBlock *B = Block; for (Expr *Init : LE->capture_inits()) { - if (CFGBlock *R = VisitForTemporaryDtors( - Init, /*BindToTemporary=*/false, Context)) - B = R; + if (Init) { + if (CFGBlock *R = VisitForTemporaryDtors( + Init, /*BindToTemporary=*/false, Context)) + B = R; + } } return B; } @@ -4210,11 +4593,15 @@ std::unique_ptr<CFG> CFG::buildCFG(const Decl *D, Stmt *Statement, const CXXDestructorDecl * CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { switch (getKind()) { - case CFGElement::Statement: case CFGElement::Initializer: case CFGElement::NewAllocator: case CFGElement::LoopExit: case CFGElement::LifetimeEnds: + case CFGElement::Statement: + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -4227,7 +4614,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { // temporary in an initializer expression. if (ty->isReferenceType()) { if (const Expr *Init = var->getInit()) { - ty = getReferenceInitTemporaryType(astContext, Init); + ty = getReferenceInitTemporaryType(Init); } } @@ -4343,8 +4730,8 @@ public: switch (stmt->getStmtClass()) { case Stmt::DeclStmtClass: - DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; - break; + DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; + break; case Stmt::IfStmtClass: { const VarDecl *var = cast<IfStmt>(stmt)->getConditionVariable(); if (var) @@ -4544,6 +4931,95 @@ public: } // namespace +static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper, + const CXXCtorInitializer *I) { + if (I->isBaseInitializer()) + OS << I->getBaseClass()->getAsCXXRecordDecl()->getName(); + else if (I->isDelegatingInitializer()) + OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName(); + else + OS << I->getAnyMember()->getName(); + OS << "("; + if (Expr *IE = I->getInit()) + IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); + OS << ")"; + + if (I->isBaseInitializer()) + OS << " (Base initializer)"; + else if (I->isDelegatingInitializer()) + OS << " (Delegating initializer)"; + else + OS << " (Member initializer)"; +} + +static void print_construction_context(raw_ostream &OS, + StmtPrinterHelper &Helper, + const ConstructionContext *CC) { + SmallVector<const Stmt *, 3> Stmts; + switch (CC->getKind()) { + case ConstructionContext::SimpleConstructorInitializerKind: { + OS << ", "; + const auto *SICC = cast<SimpleConstructorInitializerConstructionContext>(CC); + print_initializer(OS, Helper, SICC->getCXXCtorInitializer()); + break; + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: { + OS << ", "; + const auto *CICC = + cast<CXX17ElidedCopyConstructorInitializerConstructionContext>(CC); + print_initializer(OS, Helper, CICC->getCXXCtorInitializer()); + Stmts.push_back(CICC->getCXXBindTemporaryExpr()); + break; + } + case ConstructionContext::SimpleVariableKind: { + const auto *SDSCC = cast<SimpleVariableConstructionContext>(CC); + Stmts.push_back(SDSCC->getDeclStmt()); + break; + } + case ConstructionContext::CXX17ElidedCopyVariableKind: { + const auto *CDSCC = cast<CXX17ElidedCopyVariableConstructionContext>(CC); + Stmts.push_back(CDSCC->getDeclStmt()); + Stmts.push_back(CDSCC->getCXXBindTemporaryExpr()); + break; + } + case ConstructionContext::NewAllocatedObjectKind: { + const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC); + Stmts.push_back(NECC->getCXXNewExpr()); + break; + } + case ConstructionContext::SimpleReturnedValueKind: { + const auto *RSCC = cast<SimpleReturnedValueConstructionContext>(CC); + Stmts.push_back(RSCC->getReturnStmt()); + break; + } + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const auto *RSCC = + cast<CXX17ElidedCopyReturnedValueConstructionContext>(CC); + Stmts.push_back(RSCC->getReturnStmt()); + Stmts.push_back(RSCC->getCXXBindTemporaryExpr()); + break; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TOCC = cast<SimpleTemporaryObjectConstructionContext>(CC); + Stmts.push_back(TOCC->getCXXBindTemporaryExpr()); + Stmts.push_back(TOCC->getMaterializedTemporaryExpr()); + break; + } + case ConstructionContext::ElidedTemporaryObjectKind: { + const auto *TOCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + Stmts.push_back(TOCC->getCXXBindTemporaryExpr()); + Stmts.push_back(TOCC->getMaterializedTemporaryExpr()); + Stmts.push_back(TOCC->getConstructorAfterElision()); + break; + } + } + for (auto I: Stmts) + if (I) { + OS << ", "; + Helper.handledStmt(const_cast<Stmt *>(I), OS); + } +} + static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E) { if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) { @@ -4573,16 +5049,23 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, } S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); - if (isa<CXXOperatorCallExpr>(S)) { + if (auto VTC = E.getAs<CFGCXXRecordTypedCall>()) { + if (isa<CXXOperatorCallExpr>(S)) + OS << " (OperatorCall)"; + OS << " (CXXRecordTypedCall"; + print_construction_context(OS, Helper, VTC->getConstructionContext()); + OS << ")"; + } else if (isa<CXXOperatorCallExpr>(S)) { OS << " (OperatorCall)"; - } - else if (isa<CXXBindTemporaryExpr>(S)) { + } else if (isa<CXXBindTemporaryExpr>(S)) { OS << " (BindTemporary)"; - } - else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { - OS << " (CXXConstructExpr, " << CCE->getType().getAsString() << ")"; - } - else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { + } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { + OS << " (CXXConstructExpr"; + if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) { + print_construction_context(OS, Helper, CE->getConstructionContext()); + } + OS << ", " << CCE->getType().getAsString() << ")"; + } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() << ", " << CE->getType().getAsString() @@ -4593,32 +5076,19 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, if (isa<Expr>(S)) OS << '\n'; } else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) { - const CXXCtorInitializer *I = IE->getInitializer(); - if (I->isBaseInitializer()) - OS << I->getBaseClass()->getAsCXXRecordDecl()->getName(); - else if (I->isDelegatingInitializer()) - OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName(); - else OS << I->getAnyMember()->getName(); - - OS << "("; - if (Expr *IE = I->getInit()) - IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); - OS << ")"; - - if (I->isBaseInitializer()) - OS << " (Base initializer)\n"; - else if (I->isDelegatingInitializer()) - OS << " (Delegating initializer)\n"; - else OS << " (Member initializer)\n"; + print_initializer(OS, Helper, IE->getInitializer()); + OS << '\n'; } else if (Optional<CFGAutomaticObjDtor> DE = E.getAs<CFGAutomaticObjDtor>()) { const VarDecl *VD = DE->getVarDecl(); Helper.handleDecl(VD, OS); - const Type* T = VD->getType().getTypePtr(); - if (const ReferenceType* RT = T->getAs<ReferenceType>()) - T = RT->getPointeeType().getTypePtr(); - T = T->getBaseElementTypeUnsafe(); + ASTContext &ACtx = VD->getASTContext(); + QualType T = VD->getType(); + if (T->isReferenceType()) + T = getReferenceInitTemporaryType(VD->getInit(), nullptr); + if (const ArrayType *AT = ACtx.getAsArrayType(T)) + T = ACtx.getBaseElementType(AT); OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; @@ -4630,6 +5100,16 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, } else if (Optional<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) { const Stmt *LoopStmt = LE->getLoopStmt(); OS << LoopStmt->getStmtClassName() << " (LoopExit)\n"; + } else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) { + OS << "CFGScopeBegin("; + if (const VarDecl *VD = SB->getVarDecl()) + OS << VD->getQualifiedNameAsString(); + OS << ")\n"; + } else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) { + OS << "CFGScopeEnd("; + if (const VarDecl *VD = SE->getVarDecl()) + OS << VD->getQualifiedNameAsString(); + OS << ")\n"; } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { OS << "CFGNewAllocator("; if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) |