diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-31 17:06:31 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-31 17:06:31 +0000 | 
| commit | 735bee93f1285c5c55c64d80fdc2ede4c0f23341 (patch) | |
| tree | e1209c2a0b4880eee15e0ce705016372f7c88724 /contrib/llvm/tools/clang/lib/Analysis/CFG.cpp | |
| parent | 51315c45ff5643a27f9c84b816db54ee870ba29b (diff) | |
| parent | 486754660bb926339aefcf012a3f848592babb8b (diff) | |
Notes
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Analysis/CFG.cpp')
| -rw-r--r-- | contrib/llvm/tools/clang/lib/Analysis/CFG.cpp | 644 | 
1 files changed, 562 insertions, 82 deletions
diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp index 714b85d3a9ff..8a3ab15458dd 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp +++ b/contrib/llvm/tools/clang/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())  | 
