r326238 - [CFG] NFC: Refactor ConstructionContext into a finite set of cases.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 27 12:03:35 PST 2018


Author: dergachev
Date: Tue Feb 27 12:03:35 2018
New Revision: 326238

URL: http://llvm.org/viewvc/llvm-project?rev=326238&view=rev
Log:
[CFG] NFC: Refactor ConstructionContext into a finite set of cases.

ConstructionContext is moved into a separate translation unit and is separated
into multiple classes. The "old" "raw" ConstructionContext is renamed into
ConstructionContextLayer - which corresponds to the idea of building the context
gradually layer-by-layer, but it isn't easy to use in the clients. Once
CXXConstructExpr is reached, layers that we've gathered so far are transformed
into the actual, "new-style" "flat" ConstructionContext, which is put into the
CFGConstructor element and has no layers whatsoever (until it actually needs
them, eg. aggregate initialization). The new-style ConstructionContext is
instead presented as a variety of sub-classes that enumerate different ways of
constructing an object in C++. There are 5 of these supported for now,
which is around a half of what needs to be supported.

The layer-by-layer buildup process is still a little bit weird, but it hides
all the weirdness in one place, that sounds like a good thing.

Differential Revision: https://reviews.llvm.org/D43533

Added:
    cfe/trunk/include/clang/Analysis/ConstructionContext.h
    cfe/trunk/lib/Analysis/ConstructionContext.cpp
Modified:
    cfe/trunk/include/clang/Analysis/CFG.h
    cfe/trunk/lib/Analysis/CFG.cpp
    cfe/trunk/lib/Analysis/CMakeLists.txt
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Tue Feb 27 12:03:35 2018
@@ -38,6 +38,7 @@ namespace clang {
 class ASTContext;
 class BinaryOperator;
 class CFG;
+class ConstructionContext;
 class CXXBaseSpecifier;
 class CXXBindTemporaryExpr;
 class CXXCtorInitializer;
@@ -140,94 +141,6 @@ protected:
   CFGStmt() = default;
 };
 
-// This is bulky data for CFGConstructor which would not fit into the
-// CFGElement's room (pair of pointers). Contains the information
-// necessary to express what memory is being initialized by
-// the construction.
-class ConstructionContext {
-public:
-  typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
-
-private:
-  // The construction site - the statement that triggered the construction
-  // for one of its parts. For instance, stack variable declaration statement
-  // triggers construction of itself or its elements if it's an array,
-  // new-expression triggers construction of the newly allocated object(s).
-  TriggerTy Trigger;
-
-  // Sometimes a single trigger is not enough to describe the construction site.
-  // In this case we'd have a chain of "partial" construction contexts.
-  // Some examples:
-  // - A constructor within in an aggregate initializer list within a variable
-  //   would have a construction context of the initializer list with the parent
-  //   construction context of a variable.
-  // - A constructor for a temporary that needs to be both destroyed
-  //   and materialized into an elidable copy constructor would have a
-  //   construction context of a CXXBindTemporaryExpr with the parent
-  //   construction context of a MaterializeTemproraryExpr.
-  // Not all of these are currently supported.
-  const ConstructionContext *Parent = nullptr;
-
-  ConstructionContext() = default;
-  ConstructionContext(TriggerTy Trigger, const ConstructionContext *Parent)
-      : Trigger(Trigger), Parent(Parent) {}
-
-public:
-  static const ConstructionContext *
-  create(BumpVectorContext &C, TriggerTy Trigger,
-         const ConstructionContext *Parent = nullptr) {
-    ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
-    return new (CC) ConstructionContext(Trigger, Parent);
-  }
-
-  bool isNull() const { return Trigger.isNull(); }
-
-  TriggerTy getTrigger() const { return Trigger; }
-  const ConstructionContext *getParent() const { return Parent; }
-
-  const Stmt *getTriggerStmt() const {
-    return Trigger.dyn_cast<Stmt *>();
-  }
-
-  const CXXCtorInitializer *getTriggerInit() const {
-    return Trigger.dyn_cast<CXXCtorInitializer *>();
-  }
-
-  const MaterializeTemporaryExpr *getMaterializedTemporary() const {
-    // TODO: Be more careful to ensure that there's only one MTE around.
-    for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) {
-      if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
-              CC->getTriggerStmt())) {
-        return MTE;
-      }
-    }
-    return nullptr;
-  }
-
-  bool isSameAsPartialContext(const ConstructionContext *Other) const {
-    assert(Other);
-    return (Trigger == Other->Trigger);
-  }
-
-  // See if Other is a proper initial segment of this construction context
-  // in terms of the parent chain - i.e. a few first parents coincide and
-  // then the other context terminates but our context goes further - i.e.,
-  // we are providing the same context that the other context provides,
-  // and a bit more above that.
-  bool isStrictlyMoreSpecificThan(const ConstructionContext *Other) const {
-    const ConstructionContext *Self = this;
-    while (true) {
-      if (!Other)
-        return Self;
-      if (!Self || !Self->isSameAsPartialContext(Other))
-        return false;
-      Self = Self->getParent();
-      Other = Other->getParent();
-    }
-    llvm_unreachable("The above loop can only be terminated via return!");
-  }
-};
-
 /// CFGConstructor - Represents C++ constructor call. Maintains information
 /// necessary to figure out what memory is being initialized by the
 /// constructor expression. For now this is only used by the analyzer's CFG.
@@ -235,7 +148,7 @@ class CFGConstructor : public CFGStmt {
 public:
   explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C)
       : CFGStmt(CE, Constructor) {
-    assert(!C->isNull());
+    assert(C);
     Data2.setPointer(const_cast<ConstructionContext *>(C));
   }
 
@@ -247,22 +160,6 @@ public:
     return cast<CXXConstructExpr>(getStmt())->getType();
   }
 
-  ConstructionContext::TriggerTy getTrigger() const {
-    return getConstructionContext()->getTrigger();
-  }
-
-  const Stmt *getTriggerStmt() const {
-    return getConstructionContext()->getTriggerStmt();
-  }
-
-  const CXXCtorInitializer *getTriggerInit() const {
-    return getConstructionContext()->getTriggerInit();
-  }
-
-  const MaterializeTemporaryExpr *getMaterializedTemporary() const {
-    return getConstructionContext()->getMaterializedTemporary();
-  }
-
 private:
   friend class CFGElement;
 

Added: cfe/trunk/include/clang/Analysis/ConstructionContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/ConstructionContext.h?rev=326238&view=auto
==============================================================================
--- cfe/trunk/include/clang/Analysis/ConstructionContext.h (added)
+++ cfe/trunk/include/clang/Analysis/ConstructionContext.h Tue Feb 27 12:03:35 2018
@@ -0,0 +1,245 @@
+//===- ConstructionContext.h - CFG constructor information ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ConstructionContext class and its sub-classes,
+// which represent various different ways of constructing C++ objects
+// with the additional information the users may want to know about
+// the constructor.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
+#define LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
+
+#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/AST/ExprCXX.h"
+
+namespace clang {
+
+/// Construction context is a linked list of multiple layers. Layers are
+/// created gradually while traversing the AST, and layers that represent
+/// the outmost AST nodes are built first, while the node that immediately
+/// contains the constructor would be built last and capture the previous
+/// layers as its parents. Construction context captures the last layer
+/// (which has links to the previous layers) and classifies the seemingly
+/// arbitrary chain of layers into one of the possible ways of constructing
+/// an object in C++ for user-friendly experience.
+class ConstructionContextLayer {
+public:
+  typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
+
+private:
+  /// The construction site - the statement that triggered the construction
+  /// for one of its parts. For instance, stack variable declaration statement
+  /// triggers construction of itself or its elements if it's an array,
+  /// new-expression triggers construction of the newly allocated object(s).
+  TriggerTy Trigger;
+
+  /// Sometimes a single trigger is not enough to describe the construction
+  /// site. In this case we'd have a chain of "partial" construction context
+  /// layers.
+  /// Some examples:
+  /// - A constructor within in an aggregate initializer list within a variable
+  ///   would have a construction context of the initializer list with
+  ///   the parent construction context of a variable.
+  /// - A constructor for a temporary that needs to be both destroyed
+  ///   and materialized into an elidable copy constructor would have a
+  ///   construction context of a CXXBindTemporaryExpr with the parent
+  ///   construction context of a MaterializeTemproraryExpr.
+  /// Not all of these are currently supported.
+  const ConstructionContextLayer *Parent = nullptr;
+
+  ConstructionContextLayer(TriggerTy Trigger,
+                          const ConstructionContextLayer *Parent)
+      : Trigger(Trigger), Parent(Parent) {}
+
+public:
+  static const ConstructionContextLayer *
+  create(BumpVectorContext &C, TriggerTy Trigger,
+         const ConstructionContextLayer *Parent = nullptr);
+
+  const ConstructionContextLayer *getParent() const { return Parent; }
+  bool isLast() const { return !Parent; }
+
+  const Stmt *getTriggerStmt() const {
+    return Trigger.dyn_cast<Stmt *>();
+  }
+
+  const CXXCtorInitializer *getTriggerInit() const {
+    return Trigger.dyn_cast<CXXCtorInitializer *>();
+  }
+
+  /// Returns true if these layers are equal as individual layers, even if
+  /// their parents are different.
+  bool isSameLayer(const ConstructionContextLayer *Other) const {
+    assert(Other);
+    return (Trigger == Other->Trigger);
+  }
+
+  /// See if Other is a proper initial segment of this construction context
+  /// in terms of the parent chain - i.e. a few first parents coincide and
+  /// then the other context terminates but our context goes further - i.e.,
+  /// we are providing the same context that the other context provides,
+  /// and a bit more above that.
+  bool isStrictlyMoreSpecificThan(const ConstructionContextLayer *Other) const;
+};
+
+
+/// ConstructionContext's subclasses describe different ways of constructing
+/// an object in C++. The context re-captures the essential parent AST nodes
+/// of the CXXConstructExpr it is assigned to and presents these nodes
+/// through easy-to-understand accessor methods.
+class ConstructionContext {
+public:
+  enum Kind {
+    SimpleVariableKind,
+    ConstructorInitializerKind,
+    NewAllocatedObjectKind,
+    TemporaryObjectKind,
+    ReturnedValueKind
+  };
+
+protected:
+  Kind K;
+
+protected:
+  // Do not make public! These need to only be constructed
+  // via createFromLayers().
+  explicit ConstructionContext(Kind K) : K(K) {}
+
+public:
+
+  /// Consume the construction context layer, together with its parent layers,
+  /// and wrap it up into a complete construction context.
+  static const ConstructionContext *
+  createFromLayers(BumpVectorContext &C,
+                   const ConstructionContextLayer *TopLayer);
+
+  Kind getKind() const { return K; }
+};
+
+/// Represents construction into a simple local variable, eg. T var(123);.
+class SimpleVariableConstructionContext : public ConstructionContext {
+  const DeclStmt *DS;
+
+public:
+  explicit SimpleVariableConstructionContext(const DeclStmt *DS)
+      : ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) {
+    assert(DS);
+  }
+
+  const DeclStmt *getDeclStmt() const { return DS; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == SimpleVariableKind;
+  }
+};
+
+/// Represents construction into a field or a base class within a bigger object
+/// via a constructor initializer, eg. T(): field(123) { ... }.
+class ConstructorInitializerConstructionContext : public ConstructionContext {
+  const CXXCtorInitializer *I;
+
+public:
+  explicit ConstructorInitializerConstructionContext(
+      const CXXCtorInitializer *I)
+      : ConstructionContext(ConstructionContext::ConstructorInitializerKind),
+        I(I) {
+    assert(I);
+  }
+
+  const CXXCtorInitializer *getCXXCtorInitializer() const { return I; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == ConstructorInitializerKind;
+  }
+};
+
+/// Represents immediate initialization of memory allocated by operator new,
+/// eg. new T(123);.
+class NewAllocatedObjectConstructionContext : public ConstructionContext {
+  const CXXNewExpr *NE;
+
+public:
+  explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE)
+      : ConstructionContext(ConstructionContext::NewAllocatedObjectKind),
+        NE(NE) {
+    assert(NE);
+  }
+
+  const CXXNewExpr *getCXXNewExpr() const { return NE; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == NewAllocatedObjectKind;
+  }
+};
+
+/// Represents a temporary object, eg. T(123), that does not immediately cross
+/// function boundaries "by value"; constructors that construct function
+/// value-type arguments or values that are immediately returned from the
+/// function that returns a value receive separate construction context kinds.
+class TemporaryObjectConstructionContext : public ConstructionContext {
+  const CXXBindTemporaryExpr *BTE;
+  const MaterializeTemporaryExpr *MTE;
+
+public:
+  explicit TemporaryObjectConstructionContext(
+      const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
+      : ConstructionContext(ConstructionContext::TemporaryObjectKind),
+        BTE(BTE), MTE(MTE) {
+    // Both BTE and MTE can be null here, all combinations possible.
+    // Even though for now at least one should be non-null, we simply haven't
+    // implemented this case yet (this would be a temporary in the middle of
+    // nowhere that doesn't have a non-trivial destructor).
+  }
+
+  /// CXXBindTemporaryExpr here is non-null as long as the temporary has
+  /// a non-trivial destructor.
+  const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const {
+    return BTE;
+  }
+
+  /// MaterializeTemporaryExpr is non-null as long as the temporary is actually
+  /// used after construction, eg. by binding to a reference (lifetime
+  /// extension), accessing a field, calling a method, or passing it into
+  /// a function (an elidable copy or move constructor would be a common
+  /// example) by reference.
+  const MaterializeTemporaryExpr *getMaterializedTemporaryExpr() const {
+    return MTE;
+  }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == TemporaryObjectKind;
+  }
+};
+
+/// Represents a temporary object that is being immediately returned from a
+/// function by value, eg. return t; or return T(123);. In this case there is
+/// always going to be a constructor at the return site. However, the usual
+/// temporary-related bureaucracy (CXXBindTemporaryExpr,
+/// MaterializeTemporaryExpr) is normally located in the caller function's AST.
+class ReturnedValueConstructionContext : public ConstructionContext {
+  const ReturnStmt *RS;
+
+public:
+  explicit ReturnedValueConstructionContext(const ReturnStmt *RS)
+      : ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) {
+    assert(RS);
+  }
+
+  const ReturnStmt *getReturnStmt() const { return RS; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == ReturnedValueKind;
+  }
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Tue Feb 27 12:03:35 2018
@@ -29,6 +29,7 @@
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Analysis/ConstructionContext.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/ExceptionSpecificationType.h"
 #include "clang/Basic/LLVM.h"
@@ -475,7 +476,7 @@ class CFGBuilder {
   // Information about the currently visited C++ object construction site.
   // This is set in the construction trigger and read when the constructor
   // itself is being visited.
-  llvm::DenseMap<CXXConstructExpr *, const ConstructionContext *>
+  llvm::DenseMap<CXXConstructExpr *, const ConstructionContextLayer *>
       ConstructionContextMap;
 
   bool badCFG = false;
@@ -652,18 +653,19 @@ private:
     return Block;
   }
 
-  // Remember to apply \p CC when constructing the CFG element for \p CE.
-  void consumeConstructionContext(const ConstructionContext *CC,
+  // Remember to apply the construction context based on the current \p Layer
+  // when constructing the CFG element for \p CE.
+  void consumeConstructionContext(const ConstructionContextLayer *Layer,
                                   CXXConstructExpr *CE);
 
-  // Scan the child statement \p Child to find the constructor that might
-  // have been directly triggered by the current node, \p Trigger. If such
-  // constructor has been found, set current construction context to point
-  // to the trigger statement. The construction context will be unset once
-  // it is consumed when the CFG building procedure processes the
-  // construct-expression and adds the respective CFGConstructor element.
-  void findConstructionContexts(const ConstructionContext *ContextSoFar,
+  // Scan \p Child statement to find constructors in it, while keeping in mind
+  // that its parent statement is providing a partial construction context
+  // described by \p Layer. If a constructor is found, it would be assigned
+  // the context based on the layer. If an additional construction context layer
+  // is found, the function recurses into that.
+  void findConstructionContexts(const ConstructionContextLayer *Layer,
                                 Stmt *Child);
+
   // Unset the construction context after consuming it. This is done immediately
   // after adding the CFGConstructor element, so there's no need to
   // do this manually in every Visit... function.
@@ -710,7 +712,11 @@ private:
 
   void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) {
     if (BuildOpts.AddRichCXXConstructors) {
-      if (const ConstructionContext *CC = ConstructionContextMap.lookup(CE)) {
+      if (const ConstructionContextLayer *Layer =
+              ConstructionContextMap.lookup(CE)) {
+        const ConstructionContext *CC =
+            ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
+                                                  Layer);
         B->appendConstructor(CE, CC, cfg->getBumpVectorContext());
         cleanupConstructionContext(CE);
         return;
@@ -1155,20 +1161,21 @@ static const VariableArrayType *FindVA(c
   return nullptr;
 }
 
-void CFGBuilder::consumeConstructionContext(const ConstructionContext *CC, CXXConstructExpr *CE) {
-  if (const ConstructionContext *PreviousContext =
+void CFGBuilder::consumeConstructionContext(
+    const ConstructionContextLayer *Layer, CXXConstructExpr *CE) {
+  if (const ConstructionContextLayer *PreviouslyStoredLayer =
           ConstructionContextMap.lookup(CE)) {
     // We might have visited this child when we were finding construction
     // contexts within its parents.
-    assert(PreviousContext->isStrictlyMoreSpecificThan(CC) &&
+    assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) &&
            "Already within a different construction context!");
   } else {
-    ConstructionContextMap[CE] = CC;
+    ConstructionContextMap[CE] = Layer;
   }
 }
 
 void CFGBuilder::findConstructionContexts(
-    const ConstructionContext *ContextSoFar, Stmt *Child) {
+    const ConstructionContextLayer *Layer, Stmt *Child) {
   if (!BuildOpts.AddRichCXXConstructors)
     return;
 
@@ -1178,36 +1185,36 @@ void CFGBuilder::findConstructionContext
   switch(Child->getStmtClass()) {
   case Stmt::CXXConstructExprClass:
   case Stmt::CXXTemporaryObjectExprClass: {
-    consumeConstructionContext(ContextSoFar, cast<CXXConstructExpr>(Child));
+    consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
     break;
   }
   case Stmt::ExprWithCleanupsClass: {
     auto *Cleanups = cast<ExprWithCleanups>(Child);
-    findConstructionContexts(ContextSoFar, Cleanups->getSubExpr());
+    findConstructionContexts(Layer, Cleanups->getSubExpr());
     break;
   }
   case Stmt::CXXFunctionalCastExprClass: {
     auto *Cast = cast<CXXFunctionalCastExpr>(Child);
-    findConstructionContexts(ContextSoFar, Cast->getSubExpr());
+    findConstructionContexts(Layer, Cast->getSubExpr());
     break;
   }
   case Stmt::ImplicitCastExprClass: {
     auto *Cast = cast<ImplicitCastExpr>(Child);
-    findConstructionContexts(ContextSoFar, Cast->getSubExpr());
+    findConstructionContexts(Layer, Cast->getSubExpr());
     break;
   }
   case Stmt::CXXBindTemporaryExprClass: {
     auto *BTE = cast<CXXBindTemporaryExpr>(Child);
     findConstructionContexts(
-        ConstructionContext::create(cfg->getBumpVectorContext(), BTE,
-                                    ContextSoFar),
+        ConstructionContextLayer::create(cfg->getBumpVectorContext(),
+                                         BTE, Layer),
         BTE->getSubExpr());
     break;
   }
   case Stmt::ConditionalOperatorClass: {
     auto *CO = cast<ConditionalOperator>(Child);
-    findConstructionContexts(ContextSoFar, CO->getLHS());
-    findConstructionContexts(ContextSoFar, CO->getRHS());
+    findConstructionContexts(Layer, CO->getLHS());
+    findConstructionContexts(Layer, CO->getRHS());
     break;
   }
   default:
@@ -1356,7 +1363,7 @@ CFGBlock *CFGBuilder::addInitializer(CXX
 
   if (Init) {
     findConstructionContexts(
-        ConstructionContext::create(cfg->getBumpVectorContext(), I),
+        ConstructionContextLayer::create(cfg->getBumpVectorContext(), I),
         Init);
 
     if (HasTemporaries) {
@@ -2448,7 +2455,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(D
   appendStmt(Block, DS);
 
   findConstructionContexts(
-      ConstructionContext::create(cfg->getBumpVectorContext(), DS),
+      ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS),
       Init);
 
   // Keep track of the last non-null block, as 'Block' can be nulled out
@@ -2642,7 +2649,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Re
   addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);
 
   findConstructionContexts(
-      ConstructionContext::create(cfg->getBumpVectorContext(), R),
+      ConstructionContextLayer::create(cfg->getBumpVectorContext(), R),
       R->getRetValue());
 
   // If the one of the destructors does not return, we already have the Exit
@@ -3015,7 +3022,7 @@ CFGBlock *
 CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE,
                                           AddStmtChoice asc) {
   findConstructionContexts(
-      ConstructionContext::create(cfg->getBumpVectorContext(), MTE),
+      ConstructionContextLayer::create(cfg->getBumpVectorContext(), MTE),
       MTE->getTemporary());
 
   return VisitStmt(MTE, asc);
@@ -4002,7 +4009,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTempor
     appendStmt(Block, E);
 
     findConstructionContexts(
-        ConstructionContext::create(cfg->getBumpVectorContext(), E),
+        ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
         E->getSubExpr());
 
     // We do not want to propagate the AlwaysAdd property.
@@ -4025,7 +4032,7 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CX
   appendStmt(Block, NE);
 
   findConstructionContexts(
-      ConstructionContext::create(cfg->getBumpVectorContext(), NE),
+      ConstructionContextLayer::create(cfg->getBumpVectorContext(), NE),
       const_cast<CXXConstructExpr *>(NE->getConstructExpr()));
 
   if (NE->getInitializer())
@@ -4752,19 +4759,44 @@ static void print_elem(raw_ostream &OS,
     } else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
       OS << " (CXXConstructExpr, ";
       if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
-        // TODO: Refactor into ConstructionContext::print().
-        if (const Stmt *S = CE->getTriggerStmt())
-          Helper.handledStmt(const_cast<Stmt *>(S), OS);
-        else if (const CXXCtorInitializer *I = CE->getTriggerInit())
-          print_initializer(OS, Helper, I);
-        else
-          llvm_unreachable("Unexpected trigger kind!");
-        OS << ", ";
-        if (const Stmt *S = CE->getMaterializedTemporary()) {
-          if (S != CE->getTriggerStmt()) {
-            Helper.handledStmt(const_cast<Stmt *>(S), OS);
-            OS << ", ";
-          }
+        const ConstructionContext *CC = CE->getConstructionContext();
+        const Stmt *S1 = nullptr, *S2 = nullptr;
+        switch (CC->getKind()) {
+        case ConstructionContext::ConstructorInitializerKind: {
+          const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+          print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+          OS << ", ";
+          break;
+        }
+        case ConstructionContext::SimpleVariableKind: {
+          const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+          S1 = DSCC->getDeclStmt();
+          break;
+        }
+        case ConstructionContext::NewAllocatedObjectKind: {
+          const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+          S1 = NECC->getCXXNewExpr();
+          break;
+        }
+        case ConstructionContext::ReturnedValueKind: {
+          const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+          S1 = RSCC->getReturnStmt();
+          break;
+        }
+        case ConstructionContext::TemporaryObjectKind: {
+          const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+          S1 = TOCC->getCXXBindTemporaryExpr();
+          S2 = TOCC->getMaterializedTemporaryExpr();
+          break;
+        }
+        }
+        if (S1) {
+          Helper.handledStmt(const_cast<Stmt *>(S1), OS);
+          OS << ", ";
+        }
+        if (S2) {
+          Helper.handledStmt(const_cast<Stmt *>(S2), OS);
+          OS << ", ";
         }
       }
       OS << CCE->getType().getAsString() << ")";

Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
+++ cfe/trunk/lib/Analysis/CMakeLists.txt Tue Feb 27 12:03:35 2018
@@ -11,6 +11,7 @@ add_clang_library(clangAnalysis
   CallGraph.cpp
   CloneDetection.cpp
   CocoaConventions.cpp
+  ConstructionContext.cpp
   Consumed.cpp
   CodeInjector.cpp
   Dominators.cpp

Added: cfe/trunk/lib/Analysis/ConstructionContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ConstructionContext.cpp?rev=326238&view=auto
==============================================================================
--- cfe/trunk/lib/Analysis/ConstructionContext.cpp (added)
+++ cfe/trunk/lib/Analysis/ConstructionContext.cpp Tue Feb 27 12:03:35 2018
@@ -0,0 +1,92 @@
+//===- ConstructionContext.cpp - CFG constructor information --------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ConstructionContext class and its sub-classes,
+// which represent various different ways of constructing C++ objects
+// with the additional information the users may want to know about
+// the constructor.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/ConstructionContext.h"
+
+using namespace clang;
+
+const ConstructionContextLayer *
+ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
+                                 const ConstructionContextLayer *Parent) {
+  ConstructionContextLayer *CC =
+      C.getAllocator().Allocate<ConstructionContextLayer>();
+  return new (CC) ConstructionContextLayer(Trigger, Parent);
+}
+
+bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
+    const ConstructionContextLayer *Other) const {
+  const ConstructionContextLayer *Self = this;
+  while (true) {
+    if (!Other)
+      return Self;
+    if (!Self || !Self->isSameLayer(Other))
+      return false;
+    Self = Self->getParent();
+    Other = Other->getParent();
+  }
+  llvm_unreachable("The above loop can only be terminated via return!");
+}
+
+const ConstructionContext *ConstructionContext::createFromLayers(
+    BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
+  // Before this point all we've had was a stockpile of arbitrary layers.
+  // Now validate that it is shaped as one of the finite amount of expected
+  // patterns.
+  if (const Stmt *S = TopLayer->getTriggerStmt()) {
+    if (const auto *DS = dyn_cast<DeclStmt>(S)) {
+      assert(TopLayer->isLast());
+      auto *CC =
+          C.getAllocator().Allocate<SimpleVariableConstructionContext>();
+      return new (CC) SimpleVariableConstructionContext(DS);
+    } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
+      assert(TopLayer->isLast());
+      auto *CC =
+          C.getAllocator().Allocate<NewAllocatedObjectConstructionContext>();
+      return new (CC) NewAllocatedObjectConstructionContext(NE);
+    } else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) {
+      const MaterializeTemporaryExpr *MTE = nullptr;
+      assert(BTE->getType().getCanonicalType()
+                ->getAsCXXRecordDecl()->hasNonTrivialDestructor());
+      // For temporaries with destructors, there may or may not be
+      // lifetime extension on the parent layer.
+      if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
+        assert(ParentLayer->isLast());
+        MTE = cast<MaterializeTemporaryExpr>(ParentLayer->getTriggerStmt());
+      }
+      auto *CC =
+          C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+      return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
+    } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
+      assert(MTE->getType().getCanonicalType()
+                ->getAsCXXRecordDecl()->hasTrivialDestructor());
+      assert(TopLayer->isLast());
+      auto *CC =
+          C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
+      return new (CC) TemporaryObjectConstructionContext(nullptr, MTE);
+    } else if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
+      assert(TopLayer->isLast());
+      auto *CC =
+          C.getAllocator().Allocate<ReturnedValueConstructionContext>();
+      return new (CC) ReturnedValueConstructionContext(RS);
+    }
+  } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
+    assert(TopLayer->isLast());
+    auto *CC =
+        C.getAllocator().Allocate<ConstructorInitializerConstructionContext>();
+    return new (CC) ConstructorInitializerConstructionContext(I);
+  }
+  llvm_unreachable("Unexpected construction context!");
+}

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Tue Feb 27 12:03:35 2018
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/ConstructionContext.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/ParentMap.h"
@@ -111,47 +112,20 @@ ExprEngine::getRegionForConstructedObjec
   // See if we're constructing an existing region by looking at the
   // current construction context.
   if (CC) {
-    if (const Stmt *TriggerStmt = CC->getTriggerStmt()) {
-      if (const CXXNewExpr *CNE = dyn_cast<CXXNewExpr>(TriggerStmt)) {
-        if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
-          // TODO: Detect when the allocator returns a null pointer.
-          // Constructor shall not be called in this case.
-          if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
-                  getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) {
-            if (CNE->isArray()) {
-              // TODO: In fact, we need to call the constructor for every
-              // allocated element, not just the first one!
-              CallOpts.IsArrayCtorOrDtor = true;
-              return getStoreManager().GetElementZeroRegion(
-                  MR, CNE->getType()->getPointeeType());
-            }
-            return MR;
-          }
-        }
-      } else if (auto *DS = dyn_cast<DeclStmt>(TriggerStmt)) {
-        const auto *Var = cast<VarDecl>(DS->getSingleDecl());
-        SVal LValue = State->getLValue(Var, LCtx);
-        QualType Ty = Var->getType();
-        LValue = makeZeroElementRegion(State, LValue, Ty,
-                                       CallOpts.IsArrayCtorOrDtor);
-        return LValue.getAsRegion();
-      } else if (isa<ReturnStmt>(TriggerStmt)) {
-        // TODO: We should construct into a CXXBindTemporaryExpr or a
-        // MaterializeTemporaryExpr around the call-expression on the previous
-        // stack frame. Currently we re-bind the temporary to the correct region
-        // later, but that's not semantically correct. This of course does not
-        // apply when we're in the top frame. But if we are in an inlined
-        // function, we should be able to take the call-site CFG element,
-        // and it should contain (but right now it wouldn't) some sort of
-        // construction context that'd give us the right temporary expression.
-        CallOpts.IsTemporaryCtorOrDtor = true;
-        return MRMgr.getCXXTempObjectRegion(CE, LCtx);
-      } else if (isa<CXXBindTemporaryExpr>(TriggerStmt)) {
-        CallOpts.IsTemporaryCtorOrDtor = true;
-        return MRMgr.getCXXTempObjectRegion(CE, LCtx);
-      }
-      // TODO: Consider other directly initialized elements.
-    } else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) {
+    switch (CC->getKind()) {
+    case ConstructionContext::SimpleVariableKind: {
+      const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+      const auto *DS = DSCC->getDeclStmt();
+      const auto *Var = cast<VarDecl>(DS->getSingleDecl());
+      SVal LValue = State->getLValue(Var, LCtx);
+      QualType Ty = Var->getType();
+      LValue =
+          makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
+      return LValue.getAsRegion();
+    }
+    case ConstructionContext::ConstructorInitializerKind: {
+      const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+      const auto *Init = ICC->getCXXCtorInitializer();
       assert(Init->isAnyMemberInitializer());
       const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
       Loc ThisPtr =
@@ -173,10 +147,45 @@ ExprEngine::getRegionForConstructedObjec
                                        CallOpts.IsArrayCtorOrDtor);
       return FieldVal.getAsRegion();
     }
-
-    // FIXME: This will eventually need to handle new-expressions as well.
-    // Don't forget to update the pre-constructor initialization code in
-    // ExprEngine::VisitCXXConstructExpr.
+    case ConstructionContext::NewAllocatedObjectKind: {
+      if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+        const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+        const auto *NE = NECC->getCXXNewExpr();
+        // TODO: Detect when the allocator returns a null pointer.
+        // Constructor shall not be called in this case.
+        if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
+                getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) {
+          if (NE->isArray()) {
+            // TODO: In fact, we need to call the constructor for every
+            // allocated element, not just the first one!
+            CallOpts.IsArrayCtorOrDtor = true;
+            return getStoreManager().GetElementZeroRegion(
+                MR, NE->getType()->getPointeeType());
+          }
+          return MR;
+        }
+      }
+      break;
+    }
+    case ConstructionContext::TemporaryObjectKind: {
+      // TODO: Support temporaries lifetime-extended via static references.
+      // They'd need a getCXXStaticTempObjectRegion().
+      CallOpts.IsTemporaryCtorOrDtor = true;
+      return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+    }
+    case ConstructionContext::ReturnedValueKind: {
+      // TODO: We should construct into a CXXBindTemporaryExpr or a
+      // MaterializeTemporaryExpr around the call-expression on the previous
+      // stack frame. Currently we re-bind the temporary to the correct region
+      // later, but that's not semantically correct. This of course does not
+      // apply when we're in the top frame. But if we are in an inlined
+      // function, we should be able to take the call-site CFG element,
+      // and it should contain (but right now it wouldn't) some sort of
+      // construction context that'd give us the right temporary expression.
+      CallOpts.IsTemporaryCtorOrDtor = true;
+      return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+    }
+    }
   }
   // If we couldn't find an existing region to construct into, assume we're
   // constructing a temporary. Notify the caller of our failure.
@@ -227,6 +236,7 @@ void ExprEngine::VisitCXXConstructExpr(c
 
   EvalCallOptions CallOpts;
   auto C = getCurrentCFGElement().getAs<CFGConstructor>();
+  assert(C || getCurrentCFGElement().getAs<CFGStmt>());
   const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
 
   const CXXBindTemporaryExpr *BTE = nullptr;
@@ -235,15 +245,27 @@ void ExprEngine::VisitCXXConstructExpr(c
   switch (CE->getConstructionKind()) {
   case CXXConstructExpr::CK_Complete: {
     Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts);
-    if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() &&
-        !CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion &&
-        CallOpts.IsTemporaryCtorOrDtor) {
-      MTE = CC->getMaterializedTemporary();
-      if (!MTE || MTE->getStorageDuration() == SD_FullExpression) {
-        // If the temporary is lifetime-extended, don't save the BTE,
-        // because we don't need a temporary destructor, but an automatic
-        // destructor. The cast may fail because it may as well be a ReturnStmt.
-        BTE = dyn_cast<CXXBindTemporaryExpr>(CC->getTriggerStmt());
+
+    // In case of temporary object construction, extract data necessary for
+    // destruction and lifetime extension.
+    if (const auto *TCC =
+            dyn_cast_or_null<TemporaryObjectConstructionContext>(CC)) {
+      assert(CallOpts.IsTemporaryCtorOrDtor);
+      assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
+      if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
+        BTE = TCC->getCXXBindTemporaryExpr();
+        MTE = TCC->getMaterializedTemporaryExpr();
+        if (!BTE) {
+          // FIXME: lifetime extension for temporaries without destructors
+          // is not implemented yet.
+          MTE = nullptr;
+        }
+        if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
+          // If the temporary is lifetime-extended, don't save the BTE,
+          // because we don't need a temporary destructor, but an automatic
+          // destructor.
+          BTE = nullptr;
+        }
       }
     }
     break;

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=326238&r1=326237&r2=326238&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Tue Feb 27 12:03:35 2018
@@ -16,6 +16,7 @@
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/ConstructionContext.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "llvm/ADT/SmallSet.h"
@@ -635,10 +636,11 @@ ExprEngine::mayInlineCallKind(const Call
 
     const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
 
-    auto CC = getCurrentCFGElement().getAs<CFGConstructor>();
-    const Stmt *ParentExpr = CC ? CC->getTriggerStmt() : nullptr;
+    auto CCE = getCurrentCFGElement().getAs<CFGConstructor>();
+    const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
+                                        : nullptr;
 
-    if (ParentExpr && isa<CXXNewExpr>(ParentExpr) &&
+    if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
         !Opts.mayInlineCXXAllocator())
       return CIP_DisallowedOnce;
 




More information about the cfe-commits mailing list