diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Analysis/CFG.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Analysis/CFG.cpp | 1098 |
1 files changed, 697 insertions, 401 deletions
diff --git a/contrib/llvm-project/clang/lib/Analysis/CFG.cpp b/contrib/llvm-project/clang/lib/Analysis/CFG.cpp index ba5eceda24b5..03ab4c6fdf29 100644 --- a/contrib/llvm-project/clang/lib/Analysis/CFG.cpp +++ b/contrib/llvm-project/clang/lib/Analysis/CFG.cpp @@ -40,7 +40,6 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -56,6 +55,7 @@ #include "llvm/Support/raw_ostream.h" #include <cassert> #include <memory> +#include <optional> #include <string> #include <tuple> #include <utility> @@ -72,6 +72,10 @@ static SourceLocation GetEndLoc(Decl *D) { /// Returns true on constant values based around a single IntegerLiteral. /// Allow for use of parentheses, integer casts, and negative signs. +/// FIXME: it would be good to unify this function with +/// getIntegerLiteralSubexpressionValue at some point given the similarity +/// between the functions. + static bool IsIntegerLiteralConstantExpr(const Expr *E) { // Allow parentheses E = E->IgnoreParens(); @@ -296,6 +300,7 @@ public: int distance(const_iterator L); const_iterator shared_parent(const_iterator L); bool pointsToFirstDeclaredVar() { return VarIter == 1; } + bool inSameLocalScope(const_iterator rhs) { return Scope == rhs.Scope; } }; private: @@ -345,18 +350,33 @@ int LocalScope::const_iterator::distance(LocalScope::const_iterator L) { /// between this and shared_parent(L) end. LocalScope::const_iterator LocalScope::const_iterator::shared_parent(LocalScope::const_iterator L) { - llvm::SmallPtrSet<const LocalScope *, 4> ScopesOfL; + // one of iterators is not valid (we are not in scope), so common + // parent is const_iterator() (i.e. sentinel). + if ((*this == const_iterator()) || (L == const_iterator())) { + return const_iterator(); + } + + const_iterator F = *this; + if (F.inSameLocalScope(L)) { + // Iterators are in the same scope, get common subset of variables. + F.VarIter = std::min(F.VarIter, L.VarIter); + return F; + } + + llvm::SmallDenseMap<const LocalScope *, unsigned, 4> ScopesOfL; while (true) { - ScopesOfL.insert(L.Scope); + ScopesOfL.try_emplace(L.Scope, L.VarIter); if (L == const_iterator()) break; L = L.Scope->Prev; } - const_iterator F = *this; while (true) { - if (ScopesOfL.count(F.Scope)) + if (auto LIt = ScopesOfL.find(F.Scope); LIt != ScopesOfL.end()) { + // Get common subset of variables in given scope + F.VarIter = std::min(F.VarIter, LIt->getSecond()); return F; + } assert(F != const_iterator() && "L iterator is not reachable from F iterator."); F = F.Scope->Prev; @@ -432,8 +452,8 @@ reverse_children::reverse_children(Stmt *S) { // Note: Fill in this switch with more cases we want to optimize. case Stmt::InitListExprClass: { InitListExpr *IE = cast<InitListExpr>(S); - children = llvm::makeArrayRef(reinterpret_cast<Stmt**>(IE->getInits()), - IE->getNumInits()); + children = llvm::ArrayRef(reinterpret_cast<Stmt **>(IE->getInits()), + IE->getNumInits()); return; } default: @@ -441,8 +461,7 @@ reverse_children::reverse_children(Stmt *S) { } // Default case for all other statements. - for (Stmt *SubStmt : S->children()) - childrenBuf.push_back(SubStmt); + llvm::append_range(childrenBuf, S->children()); // This needs to be done *after* childrenBuf has been populated. children = childrenBuf; @@ -482,8 +501,10 @@ class CFGBuilder { CFGBlock *SwitchTerminatedBlock = nullptr; CFGBlock *DefaultCaseBlock = nullptr; - // This can point either to a try or a __try block. The frontend forbids - // mixing both kinds in one function, so having one for both is enough. + // This can point to either a C++ try, an Objective-C @try, or an SEH __try. + // try and @try can be mixed and generally work the same. + // The frontend forbids mixing SEH __try with either try or @try. + // So having one for all three is enough. CFGBlock *TryTerminatedBlock = nullptr; // Current position in local scope. @@ -508,9 +529,6 @@ class CFGBuilder { llvm::DenseMap<Expr *, const ConstructionContextLayer *> ConstructionContextMap; - using DeclsWithEndedScopeSetTy = llvm::SmallSetVector<VarDecl *, 16>; - DeclsWithEndedScopeSetTy DeclsWithEndedScope; - bool badCFG = false; const CFG::BuildOptions &BuildOpts; @@ -529,9 +547,7 @@ class CFGBuilder { public: explicit CFGBuilder(ASTContext *astContext, const CFG::BuildOptions &buildOpts) - : Context(astContext), cfg(new CFG()), // crew a new CFG - ConstructionContextMap(), BuildOpts(buildOpts) {} - + : Context(astContext), cfg(new CFG()), BuildOpts(buildOpts) {} // buildCFG - Used by external clients to construct the CFG. std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement); @@ -542,6 +558,7 @@ private: // Visitors to walk an AST and construct the CFG. CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc); CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc); + CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc); CFGBlock *VisitBinaryOperator(BinaryOperator *B, AddStmtChoice asc); CFGBlock *VisitBreakStmt(BreakStmt *B); CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc); @@ -564,6 +581,7 @@ private: AddStmtChoice asc); CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); + CFGBlock *VisitCXXTypeidExpr(CXXTypeidExpr *S, AddStmtChoice asc); CFGBlock *VisitDeclStmt(DeclStmt *DS); CFGBlock *VisitDeclSubExpr(DeclStmt *DS); CFGBlock *VisitDefaultStmt(DefaultStmt *D); @@ -597,6 +615,8 @@ private: CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc); CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); CFGBlock *VisitReturnStmt(Stmt *S); + CFGBlock *VisitCoroutineSuspendExpr(CoroutineSuspendExpr *S, + AddStmtChoice asc); CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S); CFGBlock *VisitSEHFinallyStmt(SEHFinallyStmt *S); CFGBlock *VisitSEHLeaveStmt(SEHLeaveStmt *S); @@ -607,6 +627,7 @@ private: AddStmtChoice asc); CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); CFGBlock *VisitWhileStmt(WhileStmt *W); + CFGBlock *VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, AddStmtChoice asc); CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd, bool ExternallyDestructed = false); @@ -719,9 +740,9 @@ private: // hence strict duck-typing. template <typename CallLikeExpr, typename = std::enable_if_t< - std::is_base_of<CallExpr, CallLikeExpr>::value || - std::is_base_of<CXXConstructExpr, CallLikeExpr>::value || - std::is_base_of<ObjCMessageExpr, CallLikeExpr>::value>> + std::is_base_of_v<CallExpr, CallLikeExpr> || + std::is_base_of_v<CXXConstructExpr, CallLikeExpr> || + std::is_base_of_v<ObjCMessageExpr, CallLikeExpr>>> void findConstructionContextsForArguments(CallLikeExpr *E) { for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { Expr *Arg = E->getArg(i); @@ -748,18 +769,20 @@ private: CFGBlock *addInitializer(CXXCtorInitializer *I); void addLoopExit(const Stmt *LoopStmt); - void addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); - void addLifetimeEnds(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S); void addAutomaticObjHandling(LocalScope::const_iterator B, LocalScope::const_iterator E, Stmt *S); + void addAutomaticObjDestruction(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S); + void addScopeExitHandling(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); + void addScopeChangesHandling(LocalScope::const_iterator SrcPos, + LocalScope::const_iterator DstPos, + Stmt *S); + CFGBlock *createScopeChangesHandlingBlock(LocalScope::const_iterator SrcPos, + CFGBlock *SrcBlk, + LocalScope::const_iterator DstPost, + CFGBlock *DstBlk); // Local scopes creation. LocalScope* createOrReuseLocalScope(LocalScope* Scope); @@ -858,6 +881,10 @@ private: B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext()); } + void appendCleanupFunction(CFGBlock *B, VarDecl *VD) { + B->appendCleanupFunction(VD, cfg->getBumpVectorContext()); + } + void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) { B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext()); } @@ -870,18 +897,6 @@ private: B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); } - void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, - LocalScope::const_iterator B, LocalScope::const_iterator E); - - void prependAutomaticObjLifetimeWithTerminator(CFGBlock *Blk, - 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()); @@ -899,21 +914,11 @@ private: 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) @@ -960,15 +965,16 @@ private: const Expr *LHSExpr = B->getLHS()->IgnoreParens(); const Expr *RHSExpr = B->getRHS()->IgnoreParens(); - const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(LHSExpr); + std::optional<llvm::APInt> IntLiteral1 = + getIntegerLiteralSubexpressionValue(LHSExpr); const Expr *BoolExpr = RHSExpr; - if (!IntLiteral) { - IntLiteral = dyn_cast<IntegerLiteral>(RHSExpr); + if (!IntLiteral1) { + IntLiteral1 = getIntegerLiteralSubexpressionValue(RHSExpr); BoolExpr = LHSExpr; } - if (!IntLiteral) + if (!IntLiteral1) return TryResult(); const BinaryOperator *BitOp = dyn_cast<BinaryOperator>(BoolExpr); @@ -977,26 +983,26 @@ private: const Expr *LHSExpr2 = BitOp->getLHS()->IgnoreParens(); const Expr *RHSExpr2 = BitOp->getRHS()->IgnoreParens(); - const IntegerLiteral *IntLiteral2 = dyn_cast<IntegerLiteral>(LHSExpr2); + std::optional<llvm::APInt> IntLiteral2 = + getIntegerLiteralSubexpressionValue(LHSExpr2); if (!IntLiteral2) - IntLiteral2 = dyn_cast<IntegerLiteral>(RHSExpr2); + IntLiteral2 = getIntegerLiteralSubexpressionValue(RHSExpr2); if (!IntLiteral2) return TryResult(); - llvm::APInt L1 = IntLiteral->getValue(); - llvm::APInt L2 = IntLiteral2->getValue(); - if ((BitOp->getOpcode() == BO_And && (L2 & L1) != L1) || - (BitOp->getOpcode() == BO_Or && (L2 | L1) != L1)) { + if ((BitOp->getOpcode() == BO_And && + (*IntLiteral2 & *IntLiteral1) != *IntLiteral1) || + (BitOp->getOpcode() == BO_Or && + (*IntLiteral2 | *IntLiteral1) != *IntLiteral1)) { if (BuildOpts.Observer) BuildOpts.Observer->compareBitwiseEquality(B, B->getOpcode() != BO_EQ); - TryResult(B->getOpcode() != BO_EQ); + return TryResult(B->getOpcode() != BO_EQ); } } else if (BoolExpr->isKnownToHaveBooleanValue()) { - llvm::APInt IntValue = IntLiteral->getValue(); - if ((IntValue == 1) || (IntValue == 0)) { + if ((*IntLiteral1 == 1) || (*IntLiteral1 == 0)) { return TryResult(); } return TryResult(B->getOpcode() != BO_EQ); @@ -1005,6 +1011,47 @@ private: return TryResult(); } + // Helper function to get an APInt from an expression. Supports expressions + // which are an IntegerLiteral or a UnaryOperator and returns the value with + // all operations performed on it. + // FIXME: it would be good to unify this function with + // IsIntegerLiteralConstantExpr at some point given the similarity between the + // functions. + std::optional<llvm::APInt> + getIntegerLiteralSubexpressionValue(const Expr *E) { + + // If unary. + if (const auto *UnOp = dyn_cast<UnaryOperator>(E->IgnoreParens())) { + // Get the sub expression of the unary expression and get the Integer + // Literal. + const Expr *SubExpr = UnOp->getSubExpr()->IgnoreParens(); + + if (const auto *IntLiteral = dyn_cast<IntegerLiteral>(SubExpr)) { + + llvm::APInt Value = IntLiteral->getValue(); + + // Perform the operation manually. + switch (UnOp->getOpcode()) { + case UO_Plus: + return Value; + case UO_Minus: + return -Value; + case UO_Not: + return ~Value; + case UO_LNot: + return llvm::APInt(Context->getTypeSize(Context->IntTy), !Value); + default: + assert(false && "Unexpected unary operator!"); + return std::nullopt; + } + } + } else if (const auto *IntLiteral = + dyn_cast<IntegerLiteral>(E->IgnoreParens())) + return IntLiteral->getValue(); + + return std::nullopt; + } + TryResult analyzeLogicOperatorCondition(BinaryOperatorKind Relation, const llvm::APSInt &Value1, const llvm::APSInt &Value2) { @@ -1027,16 +1074,41 @@ private: } } - /// Find a pair of comparison expressions with or without parentheses + /// There are two checks handled by this function: + /// 1. Find a law-of-excluded-middle or law-of-noncontradiction expression + /// e.g. if (x || !x), if (x && !x) + /// 2. 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) TryResult checkIncorrectLogicOperator(const BinaryOperator *B) { assert(B->isLogicalOp()); - const BinaryOperator *LHS = - dyn_cast<BinaryOperator>(B->getLHS()->IgnoreParens()); - const BinaryOperator *RHS = - dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens()); + const Expr *LHSExpr = B->getLHS()->IgnoreParens(); + const Expr *RHSExpr = B->getRHS()->IgnoreParens(); + + auto CheckLogicalOpWithNegatedVariable = [this, B](const Expr *E1, + const Expr *E2) { + if (const auto *Negate = dyn_cast<UnaryOperator>(E1)) { + if (Negate->getOpcode() == UO_LNot && + Expr::isSameComparisonOperand(Negate->getSubExpr(), E2)) { + bool AlwaysTrue = B->getOpcode() == BO_LOr; + if (BuildOpts.Observer) + BuildOpts.Observer->logicAlwaysTrue(B, AlwaysTrue); + return TryResult(AlwaysTrue); + } + } + return TryResult(); + }; + + TryResult Result = CheckLogicalOpWithNegatedVariable(LHSExpr, RHSExpr); + if (Result.isKnown()) + return Result; + Result = CheckLogicalOpWithNegatedVariable(RHSExpr, LHSExpr); + if (Result.isKnown()) + return Result; + + const auto *LHS = dyn_cast<BinaryOperator>(LHSExpr); + const auto *RHS = dyn_cast<BinaryOperator>(RHSExpr); if (!LHS || !RHS) return {}; @@ -1278,11 +1350,24 @@ private: return {}; } - bool hasTrivialDestructor(VarDecl *VD); + bool hasTrivialDestructor(const VarDecl *VD) const; + bool needsAutomaticDestruction(const VarDecl *VD) const; }; } // namespace +Expr * +clang::extractElementInitializerFromNestedAILE(const ArrayInitLoopExpr *AILE) { + if (!AILE) + return nullptr; + + Expr *AILEInit = AILE->getSubExpr(); + while (const auto *E = dyn_cast<ArrayInitLoopExpr>(AILEInit)) + AILEInit = E->getSubExpr(); + + return AILEInit; +} + inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder, const Stmt *stmt) const { return builder.alwaysAdd(stmt) || kind == AlwaysAdd; @@ -1476,7 +1561,6 @@ void CFGBuilder::cleanupConstructionContext(Expr *E) { 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 @@ -1494,9 +1578,6 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { assert(Succ == &cfg->getExit()); Block = nullptr; // the EXIT block is empty. Create all other blocks lazily. - assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && - "AddImplicitDtors and AddLifetime cannot be used at the same time"); - if (BuildOpts.AddImplicitDtors) if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D)) addImplicitDtorsForDestructor(DD); @@ -1560,16 +1641,11 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { if (LI == LabelMap.end()) continue; JumpTarget JT = LI->second; - prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition, - 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); - }; - if (auto *G = dyn_cast<GCCAsmStmt>(B->getTerminator())) { + + CFGBlock *SuccBlk = createScopeChangesHandlingBlock( + I->scopePosition, B, JT.scopePosition, JT.block); + addSuccessor(B, SuccBlk); + } else if (auto *G = dyn_cast<GCCAsmStmt>(B->getTerminator())) { CFGBlock *Successor = (I+1)->block; for (auto *L : G->labels()) { LabelMapTy::iterator LI = LabelMap.find(L->getLabel()); @@ -1612,7 +1688,7 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { } /// createBlock - Used to lazily create blocks that are connected -/// to the current (global) succcessor. +/// to the current (global) successor. CFGBlock *CFGBuilder::createBlock(bool add_successor) { CFGBlock *B = cfg->createBlock(); if (add_successor && Succ) @@ -1655,9 +1731,14 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { appendInitializer(Block, I); if (Init) { + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + auto *AILEInit = extractElementInitializerFromNestedAILE( + dyn_cast<ArrayInitLoopExpr>(Init)); + findConstructionContexts( ConstructionContextLayer::create(cfg->getBumpVectorContext(), I), - Init); + AILEInit ? AILEInit : Init); if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit @@ -1731,153 +1812,198 @@ 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) +/// Adds the CFG elements for leaving the scope of automatic objects in +/// range [B, E). This include following: +/// * AutomaticObjectDtor for variables with non-trivial destructor +/// * LifetimeEnds for all variables +/// * ScopeEnd for each scope left +void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, + LocalScope::const_iterator E, + Stmt *S) { + if (!BuildOpts.AddScopes && !BuildOpts.AddImplicitDtors && + !BuildOpts.AddLifetime) 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) + // Not leaving the scope, only need to handle destruction and lifetime + if (B.inSameLocalScope(E)) { + addAutomaticObjDestruction(B, E, S); return; + } - for (LocalScope::const_iterator I = B; I != P; ++I) - if (I.pointsToFirstDeclaredVar()) - DeclsWithEndedScope.insert(*I); -} + // Extract information about all local scopes that are left + SmallVector<LocalScope::const_iterator, 10> LocalScopeEndMarkers; + LocalScopeEndMarkers.push_back(B); + for (LocalScope::const_iterator I = B; I != E; ++I) { + if (!I.inSameLocalScope(LocalScopeEndMarkers.back())) + LocalScopeEndMarkers.push_back(I); + } + LocalScopeEndMarkers.push_back(E); + + // We need to leave the scope in reverse order, so we reverse the end + // markers + std::reverse(LocalScopeEndMarkers.begin(), LocalScopeEndMarkers.end()); + auto Pairwise = + llvm::zip(LocalScopeEndMarkers, llvm::drop_begin(LocalScopeEndMarkers)); + for (auto [E, B] : Pairwise) { + if (!B.inSameLocalScope(E)) + addScopeExitHandling(B, E, S); + addAutomaticObjDestruction(B, E, S); + } +} + +/// Add CFG elements corresponding to call destructor and end of lifetime +/// of all automatic variables with non-trivial destructor in range [B, E). +/// This include AutomaticObjectDtor and LifetimeEnds elements. +void CFGBuilder::addAutomaticObjDestruction(LocalScope::const_iterator B, + LocalScope::const_iterator E, + Stmt *S) { + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) + return; -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) - addLifetimeEnds(B, E, S); + if (B == E) + return; + + SmallVector<VarDecl *, 10> DeclsNeedDestruction; + DeclsNeedDestruction.reserve(B.distance(E)); + + for (VarDecl* D : llvm::make_range(B, E)) + if (needsAutomaticDestruction(D)) + DeclsNeedDestruction.push_back(D); + + for (VarDecl *VD : llvm::reverse(DeclsNeedDestruction)) { + if (BuildOpts.AddImplicitDtors) { + // 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 = VD->getType(); + if (Ty->isReferenceType()) + Ty = getReferenceInitTemporaryType(VD->getInit()); + Ty = Context->getBaseElementType(Ty); + + const CXXRecordDecl *CRD = Ty->getAsCXXRecordDecl(); + if (CRD && CRD->isAnyDestructorNoReturn()) + Block = createNoReturnBlock(); + } + + autoCreateBlock(); + + // Add LifetimeEnd after automatic obj with non-trivial destructors, + // as they end their lifetime when the destructor returns. For trivial + // objects, we end lifetime with scope end. + if (BuildOpts.AddLifetime) + appendLifetimeEnds(Block, VD, S); + if (BuildOpts.AddImplicitDtors && !hasTrivialDestructor(VD)) + appendAutomaticObjDtor(Block, VD, S); + if (VD->hasAttr<CleanupAttr>()) + appendCleanupFunction(Block, VD); + } } -/// Add to current block automatic objects that leave the scope. -void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S) { - if (!BuildOpts.AddLifetime) +/// Add CFG elements corresponding to leaving a scope. +/// Assumes that range [B, E) corresponds to single scope. +/// This add following elements: +/// * LifetimeEnds for all variables with non-trivial destructor +/// * ScopeEnd for each scope left +void CFGBuilder::addScopeExitHandling(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + assert(!B.inSameLocalScope(E)); + if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes) return; - if (B == E) - return; + if (BuildOpts.AddScopes) { + autoCreateBlock(); + appendScopeEnd(Block, B.getFirstVarInScope(), S); + } - // 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) + if (!BuildOpts.AddLifetime) return; // We need to perform the scope leaving in reverse order SmallVector<VarDecl *, 10> DeclsTrivial; - SmallVector<VarDecl *, 10> DeclsNonTrivial; - DeclsTrivial.reserve(dist); - DeclsNonTrivial.reserve(dist); + DeclsTrivial.reserve(B.distance(E)); - for (LocalScope::const_iterator I = B; I != P; ++I) - if (hasTrivialDestructor(*I)) - DeclsTrivial.push_back(*I); - else - DeclsNonTrivial.push_back(*I); + // Objects with trivial destructor ends their lifetime when their storage + // is destroyed, for automatic variables, this happens when the end of the + // scope is added. + for (VarDecl* D : llvm::make_range(B, E)) + if (!needsAutomaticDestruction(D)) + DeclsTrivial.push_back(D); - autoCreateBlock(); - // object with trivial destructor end their lifetime last (when storage - // duration ends) - for (SmallVectorImpl<VarDecl *>::reverse_iterator I = DeclsTrivial.rbegin(), - E = DeclsTrivial.rend(); - I != E; ++I) - appendLifetimeEnds(Block, *I, S); - - for (SmallVectorImpl<VarDecl *>::reverse_iterator - I = DeclsNonTrivial.rbegin(), - E = DeclsNonTrivial.rend(); - I != E; ++I) - 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) + if (DeclsTrivial.empty()) return; autoCreateBlock(); - - for (auto I = DeclsWithEndedScope.rbegin(), E = DeclsWithEndedScope.rend(); - I != E; ++I) - appendScopeEnd(Block, *I, S); - - return; + for (VarDecl *VD : llvm::reverse(DeclsTrivial)) + appendLifetimeEnds(Block, VD, S); } -/// addAutomaticObjDtors - Add to current block automatic objects destructors -/// for objects in range of local scope positions. Use S as trigger statement -/// for destructors. -void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, - LocalScope::const_iterator E, Stmt *S) { - if (!BuildOpts.AddImplicitDtors) +/// addScopeChangesHandling - appends information about destruction, lifetime +/// and cfgScopeEnd for variables in the scope that was left by the jump, and +/// appends cfgScopeBegin for all scopes that where entered. +/// We insert the cfgScopeBegin at the end of the jump node, as depending on +/// the sourceBlock, each goto, may enter different amount of scopes. +void CFGBuilder::addScopeChangesHandling(LocalScope::const_iterator SrcPos, + LocalScope::const_iterator DstPos, + Stmt *S) { + assert(Block && "Source block should be always crated"); + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + !BuildOpts.AddScopes) { return; + } - if (B == E) + if (SrcPos == DstPos) return; - // We need to append the destructors in reverse order, but any one of them - // may be a no-return destructor which changes the CFG. As a result, buffer - // this sequence up and replay them in reverse order when appending onto the - // CFGBlock(s). - SmallVector<VarDecl*, 10> Decls; - Decls.reserve(B.distance(E)); - for (LocalScope::const_iterator I = B; I != E; ++I) - Decls.push_back(*I); - - 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((*I)->getInit()); - } - Ty = Context->getBaseElementType(Ty); - - if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) - Block = createNoReturnBlock(); - else - autoCreateBlock(); + // Get common scope, the jump leaves all scopes [SrcPos, BasePos), and + // enter all scopes between [DstPos, BasePos) + LocalScope::const_iterator BasePos = SrcPos.shared_parent(DstPos); - // Add ScopeEnd just after automatic obj destructor. - if (BuildOpts.AddScopes && DeclsWithEndedScope.count(*I)) - appendScopeEnd(Block, *I, S); - appendAutomaticObjDtor(Block, *I, S); + // Append scope begins for scopes entered by goto + if (BuildOpts.AddScopes && !DstPos.inSameLocalScope(BasePos)) { + for (LocalScope::const_iterator I = DstPos; I != BasePos; ++I) + if (I.pointsToFirstDeclaredVar()) + appendScopeBegin(Block, *I, S); } + + // Append scopeEnds, destructor and lifetime with the terminator for + // block left by goto. + addAutomaticObjHandling(SrcPos, BasePos, S); +} + +/// createScopeChangesHandlingBlock - Creates a block with cfgElements +/// corresponding to changing the scope from the source scope of the GotoStmt, +/// to destination scope. Add destructor, lifetime and cfgScopeEnd +/// CFGElements to newly created CFGBlock, that will have the CFG terminator +/// transferred. +CFGBlock *CFGBuilder::createScopeChangesHandlingBlock( + LocalScope::const_iterator SrcPos, CFGBlock *SrcBlk, + LocalScope::const_iterator DstPos, CFGBlock *DstBlk) { + if (SrcPos == DstPos) + return DstBlk; + + if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && + (!BuildOpts.AddScopes || SrcPos.inSameLocalScope(DstPos))) + return DstBlk; + + // We will update CFBBuilder when creating new block, restore the + // previous state at exit. + SaveAndRestore save_Block(Block), save_Succ(Succ); + + // Create a new block, and transfer terminator + Block = createBlock(false); + Block->setTerminator(SrcBlk->getTerminator()); + SrcBlk->setTerminator(CFGTerminator()); + addSuccessor(Block, DstBlk); + + // Fill the created Block with the required elements. + addScopeChangesHandling(SrcPos, DstPos, Block->getTerminatorStmt()); + + assert(Block && "There should be at least one scope changing Block"); + return Block; } /// addImplicitDtorsForDestructor - Add implicit destructors generated for @@ -1893,7 +2019,7 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { // (which is different from the current class) is responsible for // destroying them. const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl(); - if (!CD->hasTrivialDestructor()) { + if (CD && !CD->hasTrivialDestructor()) { autoCreateBlock(); appendBaseDtor(Block, &VI); } @@ -1903,7 +2029,7 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { for (const auto &BI : RD->bases()) { if (!BI.isVirtual()) { const CXXRecordDecl *CD = BI.getType()->getAsCXXRecordDecl(); - if (!CD->hasTrivialDestructor()) { + if (CD && !CD->hasTrivialDestructor()) { autoCreateBlock(); appendBaseDtor(Block, &BI); } @@ -1914,9 +2040,10 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { for (auto *FI : RD->fields()) { // Check for constant size array. Set type to array element type. QualType QT = FI->getType(); - if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { + // It may be a multidimensional array. + while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { if (AT->getSize() == 0) - continue; + break; QT = AT->getElementType(); } @@ -1934,8 +2061,7 @@ LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { if (Scope) return Scope; llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); - return new (alloc.Allocate<LocalScope>()) - LocalScope(BumpVectorContext(alloc), ScopePos); + return new (alloc) LocalScope(BumpVectorContext(alloc), ScopePos); } /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement @@ -1977,7 +2103,11 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, return Scope; } -bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { +bool CFGBuilder::needsAutomaticDestruction(const VarDecl *VD) const { + return !hasTrivialDestructor(VD) || VD->hasAttr<CleanupAttr>(); +} + +bool CFGBuilder::hasTrivialDestructor(const VarDecl *VD) const { // Check for const references bound to temporary. Set type to pointee. QualType QT = VD->getType(); if (QT->isReferenceType()) { @@ -2022,32 +2152,20 @@ bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { /// const reference. Will reuse Scope if not NULL. 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 && !BuildOpts.AddScopes) return Scope; // Check if variable is local. - switch (VD->getStorageClass()) { - case SC_None: - case SC_Auto: - case SC_Register: - break; - default: return Scope; - } + if (!VD->hasLocalStorage()) + return Scope; - if (BuildOpts.AddImplicitDtors) { - if (!hasTrivialDestructor(VD) || BuildOpts.AddScopes) { - // Add the variable to scope - Scope = createOrReuseLocalScope(Scope); - Scope->addVar(VD); - ScopePos = Scope->begin(); - } + if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes && + !needsAutomaticDestruction(VD)) { + assert(BuildOpts.AddImplicitDtors); return Scope; } - assert(BuildOpts.AddLifetime); // Add the variable to scope Scope = createOrReuseLocalScope(Scope); Scope->addVar(VD); @@ -2063,63 +2181,6 @@ void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { addAutomaticObjHandling(ScopePos, scopeBeginPos, S); } -/// prependAutomaticObjDtorsWithTerminator - Prepend destructor 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 -/// destructors call site. -/// FIXME: This mechanism for adding automatic destructors doesn't handle -/// no-return destructors properly. -void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, - LocalScope::const_iterator B, LocalScope::const_iterator E) { - if (!BuildOpts.AddImplicitDtors) - return; - BumpVectorContext &C = cfg->getBumpVectorContext(); - CFGBlock::iterator InsertPos - = Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C); - for (LocalScope::const_iterator I = B; I != E; ++I) - InsertPos = Blk->insertAutomaticObjDtor(InsertPos, *I, - Blk->getTerminatorStmt()); -} - -/// prependAutomaticObjLifetimeWithTerminator - Prepend lifetime 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 lifetime ends. -void CFGBuilder::prependAutomaticObjLifetimeWithTerminator( - CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) { - if (!BuildOpts.AddLifetime) - return; - BumpVectorContext &C = cfg->getBumpVectorContext(); - CFGBlock::iterator InsertPos = - Blk->beginLifetimeEndsInsert(Blk->end(), B.distance(E), C); - for (LocalScope::const_iterator I = B; I != E; ++I) { - InsertPos = - Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminatorStmt()); - } -} - -/// 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->getTerminatorStmt()); - 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). @@ -2149,6 +2210,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::InitListExprClass: return VisitInitListExpr(cast<InitListExpr>(S), asc); + case Stmt::AttributedStmtClass: + return VisitAttributedStmt(cast<AttributedStmt>(S), asc); + case Stmt::AddrLabelExprClass: return VisitAddrLabelExpr(cast<AddrLabelExpr>(S), asc); @@ -2197,8 +2261,7 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, // FIXME: The expression inside a CXXDefaultArgExpr is owned by the // called function's declaration, not by the caller. If we simply add // this expression to the CFG, we could end up with the same Expr - // appearing multiple times. - // PR13385 / <rdar://problem/12156507> + // appearing multiple times (PR13385). // // It's likewise possible for multiple CXXDefaultInitExprs for the same // expression to be used in the same function (through aggregate @@ -2229,6 +2292,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::CXXTryStmtClass: return VisitCXXTryStmt(cast<CXXTryStmt>(S)); + case Stmt::CXXTypeidExprClass: + return VisitCXXTypeidExpr(cast<CXXTypeidExpr>(S), asc); + case Stmt::CXXForRangeStmtClass: return VisitCXXForRangeStmt(cast<CXXForRangeStmt>(S)); @@ -2282,7 +2348,7 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, return VisitObjCAtCatchStmt(cast<ObjCAtCatchStmt>(S)); case Stmt::ObjCAutoreleasePoolStmtClass: - return VisitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(S)); + return VisitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(S)); case Stmt::ObjCAtSynchronizedStmtClass: return VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S)); @@ -2309,6 +2375,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::CoreturnStmtClass: return VisitReturnStmt(S); + case Stmt::CoyieldExprClass: + case Stmt::CoawaitExprClass: + return VisitCoroutineSuspendExpr(cast<CoroutineSuspendExpr>(S), asc); + case Stmt::SEHExceptStmtClass: return VisitSEHExceptStmt(cast<SEHExceptStmt>(S)); @@ -2336,6 +2406,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::WhileStmtClass: return VisitWhileStmt(cast<WhileStmt>(S)); + + case Stmt::ArrayInitLoopExprClass: + return VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), asc); } } @@ -2398,8 +2471,32 @@ CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A, return Block; } -CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U, - AddStmtChoice asc) { +static bool isFallthroughStatement(const AttributedStmt *A) { + bool isFallthrough = hasSpecificAttr<FallThroughAttr>(A->getAttrs()); + assert((!isFallthrough || isa<NullStmt>(A->getSubStmt())) && + "expected fallthrough not to have children"); + return isFallthrough; +} + +CFGBlock *CFGBuilder::VisitAttributedStmt(AttributedStmt *A, + AddStmtChoice asc) { + // AttributedStmts for [[likely]] can have arbitrary statements as children, + // and the current visitation order here would add the AttributedStmts + // for [[likely]] after the child nodes, which is undesirable: For example, + // if the child contains an unconditional return, the [[likely]] would be + // considered unreachable. + // So only add the AttributedStmt for FallThrough, which has CFG effects and + // also no children, and omit the others. None of the other current StmtAttrs + // have semantic meaning for the CFG. + if (isFallthroughStatement(A) && asc.alwaysAdd(*this, A)) { + autoCreateBlock(); + appendStmt(Block, A); + } + + return VisitChildren(A); +} + +CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc) { if (asc.alwaysAdd(*this, U)) { autoCreateBlock(); appendStmt(Block, U); @@ -2711,7 +2808,8 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C, return addStmt(C->getCond()); } -CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestructed) { +CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C, + bool ExternallyDestructed) { LocalScope::const_iterator scopeBeginPos = ScopePos; addLocalScopeForStmt(C); @@ -2723,11 +2821,10 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestruct CFGBlock *LastBlock = Block; - for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); - I != E; ++I ) { + for (Stmt *S : llvm::reverse(C->body())) { // If we hit a segment of code just containing ';' (NullStmts), we can // get a null block back. In such cases, just use the LastBlock - CFGBlock *newBlock = Visit(*I, AddStmtChoice::AlwaysAdd, + CFGBlock *newBlock = Visit(S, AddStmtChoice::AlwaysAdd, ExternallyDestructed); if (newBlock) @@ -2902,12 +2999,30 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { } } + // If we bind to a tuple-like type, we iterate over the HoldingVars, and + // create a DeclStmt for each of them. + if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) { + for (auto *BD : llvm::reverse(DD->bindings())) { + if (auto *VD = BD->getHoldingVar()) { + DeclGroupRef DG(VD); + DeclStmt *DSNew = + new (Context) DeclStmt(DG, VD->getLocation(), GetEndLoc(VD)); + cfg->addSyntheticDeclStmt(DSNew, DS); + Block = VisitDeclSubExpr(DSNew); + } + } + } + autoCreateBlock(); appendStmt(Block, DS); + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init); + findConstructionContexts( ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS), - Init); + AILE ? AILE->getSubExpr() : 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 @@ -2966,7 +3081,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scope for C++17 if init-stmt if one exists. if (Stmt *Init = I->getInit()) @@ -2991,7 +3106,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { CFGBlock *ElseBlock = Succ; if (Stmt *Else = I->getElse()) { - SaveAndRestore<CFGBlock*> sv(Succ); + SaveAndRestore sv(Succ); // NULL out Block so that the recursive call to Visit will // create a new basic block. @@ -3017,7 +3132,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { { Stmt *Then = I->getThen(); assert(Then); - SaveAndRestore<CFGBlock*> sv(Succ); + SaveAndRestore sv(Succ); Block = nullptr; // If branch is not a compound statement create implicit scope @@ -3047,7 +3162,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { // control-flow transfer of '&&' or '||' go directly into the then/else // blocks directly. BinaryOperator *Cond = - I->getConditionVariable() + (I->isConsteval() || I->getConditionVariable()) ? nullptr : dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens()); CFGBlock *LastBlock; @@ -3061,7 +3176,9 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { Block->setTerminator(I); // See if this is a known constant. - const TryResult &KnownVal = tryEvaluateBool(I->getCond()); + TryResult KnownVal; + if (!I->isConsteval()) + KnownVal = tryEvaluateBool(I->getCond()); // Add the successors. If we know that specific branches are // unreachable, inform addSuccessor() of that knowledge. @@ -3122,9 +3239,41 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) { if (Expr *O = RS->getRetValue()) return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); return Block; - } else { // co_return - return VisitChildren(S); } + + CoreturnStmt *CRS = cast<CoreturnStmt>(S); + auto *B = Block; + if (CFGBlock *R = Visit(CRS->getPromiseCall())) + B = R; + + if (Expr *RV = CRS->getOperand()) + if (RV->getType()->isVoidType() && !isa<InitListExpr>(RV)) + // A non-initlist void expression. + if (CFGBlock *R = Visit(RV)) + B = R; + + return B; +} + +CFGBlock *CFGBuilder::VisitCoroutineSuspendExpr(CoroutineSuspendExpr *E, + AddStmtChoice asc) { + // We're modelling the pre-coro-xform CFG. Thus just evalate the various + // active components of the co_await or co_yield. Note we do not model the + // edge from the builtin_suspend to the exit node. + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + CFGBlock *B = Block; + if (auto *R = Visit(E->getResumeExpr())) + B = R; + if (auto *R = Visit(E->getSuspendExpr())) + B = R; + if (auto *R = Visit(E->getReadyExpr())) + B = R; + if (auto *R = Visit(E->getCommonExpr())) + B = R; + return B; } CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) { @@ -3133,7 +3282,7 @@ CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) { // Save local scope position because in case of exception variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); addStmt(ES->getBlock()); CFGBlock *SEHExceptBlock = Block; @@ -3223,14 +3372,13 @@ CFGBlock *CFGBuilder::VisitSEHTryStmt(SEHTryStmt *Terminator) { Succ = SEHTrySuccessor; // Save the current "__try" context. - SaveAndRestore<CFGBlock *> save_try(TryTerminatedBlock, - NewTryTerminatedBlock); + SaveAndRestore SaveTry(TryTerminatedBlock, NewTryTerminatedBlock); cfg->addTryDispatchBlock(TryTerminatedBlock); // Save the current value for the __leave target. // All __leaves should go to the code following the __try // (FIXME: or if the __try has a __finally, to the __finally.) - SaveAndRestore<JumpTarget> save_break(SEHLeaveJumpTarget); + SaveAndRestore save_break(SEHLeaveJumpTarget); SEHLeaveJumpTarget = JumpTarget(SEHTrySuccessor, ScopePos); assert(Terminator->getTryBlock() && "__try must contain a non-NULL body"); @@ -3246,8 +3394,7 @@ CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { if (!LabelBlock) // This can happen when the body is empty, i.e. LabelBlock = createBlock(); // scopes that only contains NullStmts. - assert(LabelMap.find(L->getDecl()) == LabelMap.end() && - "label already in map"); + assert(!LabelMap.contains(L->getDecl()) && "label already in map"); LabelMap[L->getDecl()] = JumpTarget(LabelBlock, ScopePos); // Labels partition blocks, so this is the end of the basic block we were @@ -3258,7 +3405,7 @@ CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { if (badCFG) return nullptr; - // We set Block to NULL to allow lazy creation of a new block (if necessary); + // We set Block to NULL to allow lazy creation of a new block (if necessary). Block = nullptr; // This block is now the implicit successor of other blocks. @@ -3281,9 +3428,21 @@ CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, AddStmtChoice asc) { CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) { CFGBlock *LastBlock = VisitNoRecurse(E, asc); + + unsigned Idx = 0; for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(), - et = E->capture_init_end(); it != et; ++it) { + et = E->capture_init_end(); + it != et; ++it, ++Idx) { if (Expr *Init = *it) { + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + auto *AILEInit = extractElementInitializerFromNestedAILE( + dyn_cast<ArrayInitLoopExpr>(Init)); + + findConstructionContexts(ConstructionContextLayer::create( + cfg->getBumpVectorContext(), {E, Idx}), + AILEInit ? AILEInit : Init); + CFGBlock *Tmp = Visit(Init); if (Tmp) LastBlock = Tmp; @@ -3307,8 +3466,8 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) { BackpatchBlocks.push_back(JumpSource(Block, ScopePos)); else { JumpTarget JT = I->second; - addAutomaticObjHandling(ScopePos, JT.scopePosition, G); addSuccessor(Block, JT.block); + addScopeChangesHandling(ScopePos, JT.scopePosition, G); } return Block; @@ -3333,7 +3492,7 @@ CFGBlock *CFGBuilder::VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc) { // Save "Succ" in BackpatchBlocks. In the backpatch processing, "Succ" is // used to avoid adding "Succ" again. BackpatchBlocks.push_back(JumpSource(Succ, ScopePos)); - return Block; + return VisitChildren(G); } CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { @@ -3341,7 +3500,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scope for init statement and possible condition variable. // Add destructor for init statement and condition variable. @@ -3369,7 +3528,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // Save the current value for the break targets. // All breaks should go to the code following the loop. - SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + SaveAndRestore save_break(BreakJumpTarget); BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; @@ -3379,8 +3538,8 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { assert(F->getBody()); // Save the current values for Block, Succ, continue and break targets. - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget); // Create an empty block to represent the transition block for looping back // to the head of the loop. If we have increment code, it will @@ -3388,6 +3547,11 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { Block = Succ = TransitionBlock = createBlock(false); TransitionBlock->setLoopTarget(F); + + // Loop iteration (after increment) should end with destructor of Condition + // variable (if any). + addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F); + if (Stmt *I = F->getInc()) { // Generate increment code in its own basic block. This is the target of // continue statements. @@ -3407,8 +3571,6 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); ContinueJumpTarget.block->setLoopTarget(F); - // Loop body should end with destructor of Condition variable (if any). - addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F); // If body is not a compound statement create implicit scope // and add destructors. @@ -3435,7 +3597,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { do { Expr *C = F->getCond(); - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Specially handle logical operators, which have a slightly // more optimal CFG representation. @@ -3501,7 +3663,7 @@ 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); + SaveAndRestore save_scope_pos(ScopePos); ScopePos = LoopBeginScopePos; Block = createBlock(); return addStmt(I); @@ -3604,9 +3766,9 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { // Now create the true branch. { // Save the current values for Succ, continue and break targets. - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), - save_break(BreakJumpTarget); + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget), + save_break(BreakJumpTarget); // Add an intermediate block between the BodyBlock and the // EntryConditionBlock to represent the "loop back" transition, for looping @@ -3670,11 +3832,6 @@ CFGBlock *CFGBuilder::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S) { return addStmt(S->getSynchExpr()); } -CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { - // FIXME - return NYS(); -} - CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) { autoCreateBlock(); @@ -3705,7 +3862,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scope for possible condition variable. // Store scope position for continue statement. @@ -3734,9 +3891,9 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { assert(W->getBody()); // Save the current values for Block, Succ, continue and break targets. - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), - save_break(BreakJumpTarget); + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget), + save_break(BreakJumpTarget); // Create an empty block to represent the transition block for looping back // to the head of the loop. @@ -3835,16 +3992,58 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { return EntryConditionBlock; } -CFGBlock *CFGBuilder::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) { - // FIXME: For now we pretend that @catch and the code it contains does not - // exit. - return Block; +CFGBlock *CFGBuilder::VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, A)) { + autoCreateBlock(); + appendStmt(Block, A); + } + + CFGBlock *B = Block; + + if (CFGBlock *R = Visit(A->getSubExpr())) + B = R; + + auto *OVE = dyn_cast<OpaqueValueExpr>(A->getCommonExpr()); + assert(OVE && "ArrayInitLoopExpr->getCommonExpr() should be wrapped in an " + "OpaqueValueExpr!"); + if (CFGBlock *R = Visit(OVE->getSourceExpr())) + B = R; + + return B; } -CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { - // FIXME: This isn't complete. We basically treat @throw like a return - // statement. +CFGBlock *CFGBuilder::VisitObjCAtCatchStmt(ObjCAtCatchStmt *CS) { + // ObjCAtCatchStmt are treated like labels, so they are the first statement + // in a block. + + // Save local scope position because in case of exception variable ScopePos + // won't be restored when traversing AST. + SaveAndRestore save_scope_pos(ScopePos); + + if (CS->getCatchBody()) + addStmt(CS->getCatchBody()); + + CFGBlock *CatchBlock = Block; + if (!CatchBlock) + CatchBlock = createBlock(); + appendStmt(CatchBlock, CS); + + // Also add the ObjCAtCatchStmt as a label, like with regular labels. + CatchBlock->setLabel(CS); + + // Bail out if the CFG is bad. + if (badCFG) + return nullptr; + + // We set Block to NULL to allow lazy creation of a new block (if necessary). + Block = nullptr; + + return CatchBlock; +} + +CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { // If we were in the middle of a block we stop processing that block. if (badCFG) return nullptr; @@ -3852,14 +4051,77 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { // Create the new block. Block = createBlock(false); - // The Exit block is the only successor. - addSuccessor(Block, &cfg->getExit()); + if (TryTerminatedBlock) + // The current try statement is the only successor. + addSuccessor(Block, TryTerminatedBlock); + else + // otherwise the Exit block is the only successor. + addSuccessor(Block, &cfg->getExit()); // Add the statement to the block. This may create new blocks if S contains // control-flow (short-circuit operations). return VisitStmt(S, AddStmtChoice::AlwaysAdd); } +CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *Terminator) { + // "@try"/"@catch" is a control-flow statement. Thus we stop processing the + // current block. + CFGBlock *TrySuccessor = nullptr; + + if (Block) { + if (badCFG) + return nullptr; + TrySuccessor = Block; + } else + TrySuccessor = Succ; + + // FIXME: Implement @finally support. + if (Terminator->getFinallyStmt()) + return NYS(); + + CFGBlock *PrevTryTerminatedBlock = TryTerminatedBlock; + + // Create a new block that will contain the try statement. + CFGBlock *NewTryTerminatedBlock = createBlock(false); + // Add the terminator in the try block. + NewTryTerminatedBlock->setTerminator(Terminator); + + bool HasCatchAll = false; + for (ObjCAtCatchStmt *CS : Terminator->catch_stmts()) { + // The code after the try is the implicit successor. + Succ = TrySuccessor; + if (CS->hasEllipsis()) { + HasCatchAll = true; + } + Block = nullptr; + CFGBlock *CatchBlock = VisitObjCAtCatchStmt(CS); + if (!CatchBlock) + return nullptr; + // Add this block to the list of successors for the block with the try + // statement. + addSuccessor(NewTryTerminatedBlock, CatchBlock); + } + + // FIXME: This needs updating when @finally support is added. + if (!HasCatchAll) { + if (PrevTryTerminatedBlock) + addSuccessor(NewTryTerminatedBlock, PrevTryTerminatedBlock); + else + addSuccessor(NewTryTerminatedBlock, &cfg->getExit()); + } + + // The code after the try is the implicit successor. + Succ = TrySuccessor; + + // Save the current "try" context. + SaveAndRestore SaveTry(TryTerminatedBlock, NewTryTerminatedBlock); + cfg->addTryDispatchBlock(TryTerminatedBlock); + + assert(Terminator->getTryBody() && "try must contain a non-NULL body"); + Block = nullptr; + return addStmt(Terminator->getTryBody()); +} + CFGBlock *CFGBuilder::VisitObjCMessageExpr(ObjCMessageExpr *ME, AddStmtChoice asc) { findConstructionContextsForArguments(ME); @@ -3890,6 +4152,25 @@ CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { return VisitStmt(T, AddStmtChoice::AlwaysAdd); } +CFGBlock *CFGBuilder::VisitCXXTypeidExpr(CXXTypeidExpr *S, AddStmtChoice asc) { + if (asc.alwaysAdd(*this, S)) { + autoCreateBlock(); + appendStmt(Block, S); + } + + // C++ [expr.typeid]p3: + // When typeid is applied to an expression other than an glvalue of a + // polymorphic class type [...] [the] expression is an unevaluated + // operand. [...] + // We add only potentially evaluated statements to the block to avoid + // CFG generation for unevaluated operands. + if (!S->isTypeDependent() && S->isPotentiallyEvaluated()) + return VisitChildren(S); + + // Return block without CFG for unevaluated operands. + return Block; +} + CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { CFGBlock *LoopSuccessor = nullptr; @@ -3936,8 +4217,8 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { assert(D->getBody()); // Save the current values for Block, Succ, and continue and break targets - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget), save_break(BreakJumpTarget); // All continues within this loop should go to the condition block @@ -4055,7 +4336,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scope for C++17 switch init-stmt if one exists. if (Stmt *Init = Terminator->getInit()) @@ -4075,9 +4356,9 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { } else SwitchSuccessor = Succ; // Save the current "switch" context. - SaveAndRestore<CFGBlock*> save_switch(SwitchTerminatedBlock), - save_default(DefaultCaseBlock); - SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + SaveAndRestore save_switch(SwitchTerminatedBlock), + save_default(DefaultCaseBlock); + SaveAndRestore save_break(BreakJumpTarget); // Set the "default" case to be the block after the switch statement. If the // switch statement contains a "default:", this value will be overwritten with @@ -4100,15 +4381,13 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // For pruning unreachable case statements, save the current state // for tracking the condition value. - SaveAndRestore<bool> save_switchExclusivelyCovered(switchExclusivelyCovered, - false); + SaveAndRestore save_switchExclusivelyCovered(switchExclusivelyCovered, false); // Determine if the switch condition can be explicitly evaluated. assert(Terminator->getCond() && "switch condition must be non-NULL"); Expr::EvalResult result; bool b = tryEvaluate(Terminator->getCond(), result); - SaveAndRestore<Expr::EvalResult*> save_switchCond(switchCond, - b ? &result : nullptr); + SaveAndRestore save_switchCond(switchCond, b ? &result : nullptr); // If body is not a compound statement create implicit scope // and add destructors. @@ -4244,7 +4523,7 @@ CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { shouldAddCase(switchExclusivelyCovered, switchCond, CS, *Context)); - // We set Block to NULL to allow lazy creation of a new block (if necessary) + // We set Block to NULL to allow lazy creation of a new block (if necessary). Block = nullptr; if (TopBlock) { @@ -4280,7 +4559,7 @@ CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { // (including a fall-through to the code after the switch statement) to always // be the last successor of a switch-terminated block. - // We set Block to NULL to allow lazy creation of a new block (if necessary) + // We set Block to NULL to allow lazy creation of a new block (if necessary). Block = nullptr; // This block is now the implicit successor of other blocks. @@ -4298,7 +4577,8 @@ CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { if (badCFG) return nullptr; TrySuccessor = Block; - } else TrySuccessor = Succ; + } else + TrySuccessor = Succ; CFGBlock *PrevTryTerminatedBlock = TryTerminatedBlock; @@ -4308,10 +4588,10 @@ CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { NewTryTerminatedBlock->setTerminator(Terminator); bool HasCatchAll = false; - for (unsigned h = 0; h <Terminator->getNumHandlers(); ++h) { + for (unsigned I = 0, E = Terminator->getNumHandlers(); I != E; ++I) { // The code after the try is the implicit successor. Succ = TrySuccessor; - CXXCatchStmt *CS = Terminator->getHandler(h); + CXXCatchStmt *CS = Terminator->getHandler(I); if (CS->getExceptionDecl() == nullptr) { HasCatchAll = true; } @@ -4334,7 +4614,7 @@ CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { Succ = TrySuccessor; // Save the current "try" context. - SaveAndRestore<CFGBlock*> save_try(TryTerminatedBlock, NewTryTerminatedBlock); + SaveAndRestore SaveTry(TryTerminatedBlock, NewTryTerminatedBlock); cfg->addTryDispatchBlock(TryTerminatedBlock); assert(Terminator->getTryBlock() && "try must contain a non-NULL body"); @@ -4348,7 +4628,7 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { // Save local scope position because in case of exception variable ScopePos // won't be restored when traversing AST. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scope for possible exception variable. // Store scope position. Add implicit destructor. @@ -4379,7 +4659,7 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { if (badCFG) return nullptr; - // We set Block to NULL to allow lazy creation of a new block (if necessary) + // We set Block to NULL to allow lazy creation of a new block (if necessary). Block = nullptr; return CatchBlock; @@ -4400,7 +4680,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // } // Save local scope position before the addition of the implicit variables. - SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + SaveAndRestore save_scope_pos(ScopePos); // Create local scopes and destructors for range, begin and end variables. if (Stmt *Range = S->getRangeStmt()) @@ -4425,7 +4705,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // Save the current value for the break targets. // All breaks should go to the code following the loop. - SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + SaveAndRestore save_break(BreakJumpTarget); BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); // The block for the __begin != __end expression. @@ -4458,8 +4738,8 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { assert(S->getBody()); // Save the current values for Block, Succ, and continue targets. - SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); - SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); + SaveAndRestore save_Block(Block), save_Succ(Succ); + SaveAndRestore save_continue(ContinueJumpTarget); // Generate increment code in its own basic block. This is the target of // continue statements. @@ -4944,8 +5224,7 @@ CFGBlock *CFG::createBlock() { bool first_block = begin() == end(); // Create the block. - CFGBlock *Mem = getAllocator().Allocate<CFGBlock>(); - new (Mem) CFGBlock(NumBlockIDs++, BlkBVC, this); + CFGBlock *Mem = new (getAllocator()) CFGBlock(NumBlockIDs++, BlkBVC, this); Blocks.push_back(Mem, BlkBVC); // If this is the first block, set it as the Entry and Exit. @@ -5020,6 +5299,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { case CFGElement::CXXRecordTypedCall: case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: + case CFGElement::CleanupFunction: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -5062,8 +5342,19 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { const CXXTemporary *temp = bindExpr->getTemporary(); return temp->getDestructor(); } + case CFGElement::MemberDtor: { + const FieldDecl *field = castAs<CFGMemberDtor>().getFieldDecl(); + QualType ty = field->getType(); + + while (const ArrayType *arrayType = astContext.getAsArrayType(ty)) { + ty = arrayType->getElementType(); + } + + const CXXRecordDecl *classDecl = ty->getAsCXXRecordDecl(); + assert(classDecl); + return classDecl->getDestructor(); + } case CFGElement::BaseDtor: - case CFGElement::MemberDtor: // Not yet supported. return nullptr; } @@ -5141,7 +5432,7 @@ public: unsigned j = 1; for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ; BI != BEnd; ++BI, ++j ) { - if (Optional<CFGStmt> SE = BI->getAs<CFGStmt>()) { + if (std::optional<CFGStmt> SE = BI->getAs<CFGStmt>()) { const Stmt *stmt= SE->getStmt(); std::pair<unsigned, unsigned> P((*I)->getBlockID(), j); StmtMap[stmt] = P; @@ -5287,13 +5578,11 @@ public: Terminator->getCond()->printPretty(OS, Helper, Policy); } - void VisitCXXTryStmt(CXXTryStmt *CS) { - OS << "try ..."; - } + void VisitCXXTryStmt(CXXTryStmt *) { OS << "try ..."; } - void VisitSEHTryStmt(SEHTryStmt *CS) { - OS << "__try ..."; - } + void VisitObjCAtTryStmt(ObjCAtTryStmt *) { OS << "@try ..."; } + + void VisitSEHTryStmt(SEHTryStmt *CS) { OS << "__try ..."; } void VisitAbstractConditionalOperator(AbstractConditionalOperator* C) { if (Stmt *Cond = C->getCond()) @@ -5439,6 +5728,12 @@ static void print_construction_context(raw_ostream &OS, Stmts.push_back(TOCC->getConstructorAfterElision()); break; } + case ConstructionContext::LambdaCaptureKind: { + const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); + Helper.handledStmt(const_cast<LambdaExpr *>(LCC->getLambdaExpr()), OS); + OS << "+" << LCC->getIndex(); + return; + } case ConstructionContext::ArgumentKind: { const auto *ACC = cast<ArgumentConstructionContext>(CC); if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) { @@ -5462,7 +5757,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E); void CFGElement::dumpToStream(llvm::raw_ostream &OS) const { - StmtPrinterHelper Helper(nullptr, {}); + LangOptions LangOpts; + StmtPrinterHelper Helper(nullptr, LangOpts); print_elem(OS, Helper, *this); } @@ -5511,15 +5807,13 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, OS << " (BindTemporary)"; } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { OS << " (CXXConstructExpr"; - if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) { + if (std::optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) { print_construction_context(OS, Helper, CE->getConstructionContext()); } - OS << ", " << CCE->getType().getAsString() << ")"; + OS << ", " << CCE->getType() << ")"; } else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { - OS << " (" << CE->getStmtClassName() << ", " - << CE->getCastKindName() - << ", " << CE->getType().getAsString() - << ")"; + OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() + << ", " << CE->getType() << ")"; } // Expressions need a newline. @@ -5549,6 +5843,11 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, break; } + case CFGElement::Kind::CleanupFunction: + OS << "CleanupFunction (" + << E.castAs<CFGCleanupFunction>().getFunctionDecl()->getName() << ")\n"; + break; + case CFGElement::Kind::LifetimeEnds: Helper.handleDecl(E.castAs<CFGLifetimeEnds>().getVarDecl(), OS); OS << " (Lifetime ends)\n"; @@ -5609,7 +5908,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, } case CFGElement::Kind::TemporaryDtor: { - const CXXBindTemporaryExpr *BT = E.castAs<CFGTemporaryDtor>().getBindTemporaryExpr(); + const CXXBindTemporaryExpr *BT = + E.castAs<CFGTemporaryDtor>().getBindTemporaryExpr(); OS << "~"; BT->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); OS << "() (Temporary object destructor)\n"; @@ -5653,21 +5953,25 @@ static void print_block(raw_ostream &OS, const CFG* cfg, OS << L->getName(); else if (CaseStmt *C = dyn_cast<CaseStmt>(Label)) { OS << "case "; - if (C->getLHS()) - C->getLHS()->printPretty(OS, &Helper, - PrintingPolicy(Helper.getLangOpts())); - if (C->getRHS()) { + if (const Expr *LHS = C->getLHS()) + LHS->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); + if (const Expr *RHS = C->getRHS()) { OS << " ... "; - C->getRHS()->printPretty(OS, &Helper, - PrintingPolicy(Helper.getLangOpts())); + RHS->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); } } else if (isa<DefaultStmt>(Label)) OS << "default"; else if (CXXCatchStmt *CS = dyn_cast<CXXCatchStmt>(Label)) { OS << "catch ("; - if (CS->getExceptionDecl()) - CS->getExceptionDecl()->print(OS, PrintingPolicy(Helper.getLangOpts()), - 0); + if (const VarDecl *ED = CS->getExceptionDecl()) + ED->print(OS, PrintingPolicy(Helper.getLangOpts()), 0); + else + OS << "..."; + OS << ")"; + } else if (ObjCAtCatchStmt *CS = dyn_cast<ObjCAtCatchStmt>(Label)) { + OS << "@catch ("; + if (const VarDecl *PD = CS->getCatchParamDecl()) + PD->print(OS, PrintingPolicy(Helper.getLangOpts()), 0); else OS << "..."; OS << ")"; @@ -5882,8 +6186,8 @@ static bool isImmediateSinkBlock(const CFGBlock *Blk) { // at least for now, but once we have better support for exceptions, // we'd need to carefully handle the case when the throw is being // immediately caught. - if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) { - if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>()) + if (llvm::any_of(*Blk, [](const CFGElement &Elm) { + if (std::optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>()) if (isa<CXXThrowExpr>(StmtElm->getStmt())) return true; return false; @@ -6028,17 +6332,13 @@ Stmt *CFGBlock::getTerminatorCondition(bool StripParens) { // CFG Graphviz Visualization //===----------------------------------------------------------------------===// -#ifndef NDEBUG -static StmtPrinterHelper* GraphHelper; -#endif +static StmtPrinterHelper *GraphHelper; void CFG::viewCFG(const LangOptions &LO) const { -#ifndef NDEBUG StmtPrinterHelper H(this, LO); GraphHelper = &H; llvm::ViewGraph(this,"CFG"); GraphHelper = nullptr; -#endif } namespace llvm { @@ -6047,8 +6347,7 @@ template<> struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits { DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} - static std::string getNodeLabel(const CFGBlock *Node, const CFG* Graph) { -#ifndef NDEBUG + static std::string getNodeLabel(const CFGBlock *Node, const CFG *Graph) { std::string OutSStr; llvm::raw_string_ostream Out(OutSStr); print_block(Out,Graph, *Node, *GraphHelper, false, false); @@ -6064,9 +6363,6 @@ struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits { } return OutStr; -#else - return {}; -#endif } }; |