r328255 - [CFG] [analyzer] Add C++17-specific ctor-initializer construction contexts.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 22 15:02:38 PDT 2018


Author: dergachev
Date: Thu Mar 22 15:02:38 2018
New Revision: 328255

URL: http://llvm.org/viewvc/llvm-project?rev=328255&view=rev
Log:
[CFG] [analyzer] Add C++17-specific ctor-initializer construction contexts.

CXXCtorInitializer-based constructors are also affected by the C++17 mandatory
copy elision, like variable constructors and return value constructors.
Extend r328248 to support those.

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

Modified:
    cfe/trunk/include/clang/Analysis/CFG.h
    cfe/trunk/include/clang/Analysis/ConstructionContext.h
    cfe/trunk/lib/Analysis/CFG.cpp
    cfe/trunk/lib/Analysis/ConstructionContext.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
    cfe/trunk/test/Analysis/cfg-rich-constructors.cpp

Modified: cfe/trunk/include/clang/Analysis/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CFG.h?rev=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CFG.h (original)
+++ cfe/trunk/include/clang/Analysis/CFG.h Thu Mar 22 15:02:38 2018
@@ -189,8 +189,10 @@ public:
     // into the constructor. An assertion would require passing an ASTContext
     // which would mean paying for something we don't use.
     assert(C && (isa<TemporaryObjectConstructionContext>(C) ||
+                 // These are possible in C++17 due to mandatory copy elision.
                  isa<ReturnedValueConstructionContext>(C) ||
-                 isa<VariableConstructionContext>(C)));
+                 isa<VariableConstructionContext>(C) ||
+                 isa<ConstructorInitializerConstructionContext>(C)));
     Data2.setPointer(const_cast<ConstructionContext *>(C));
   }
 

Modified: cfe/trunk/include/clang/Analysis/ConstructionContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/ConstructionContext.h?rev=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/ConstructionContext.h (original)
+++ cfe/trunk/include/clang/Analysis/ConstructionContext.h Thu Mar 22 15:02:38 2018
@@ -102,7 +102,10 @@ public:
     CXX17ElidedCopyVariableKind,
     VARIABLE_BEGIN = SimpleVariableKind,
     VARIABLE_END = CXX17ElidedCopyVariableKind,
-    ConstructorInitializerKind,
+    SimpleConstructorInitializerKind,
+    CXX17ElidedCopyConstructorInitializerKind,
+    INITIALIZER_BEGIN = SimpleConstructorInitializerKind,
+    INITIALIZER_END = CXX17ElidedCopyConstructorInitializerKind,
     NewAllocatedObjectKind,
     TemporaryObjectKind,
     SimpleReturnedValueKind,
@@ -202,17 +205,15 @@ public:
   }
 };
 
-/// Represents construction into a field or a base class within a bigger object
-/// via a constructor initializer, eg. T(): field(123) { ... }.
+// An abstract base class for constructor-initializer-based constructors.
 class ConstructorInitializerConstructionContext : public ConstructionContext {
   const CXXCtorInitializer *I;
 
-  friend class ConstructionContext; // Allows to create<>() itself.
-
+protected:
   explicit ConstructorInitializerConstructionContext(
-      const CXXCtorInitializer *I)
-      : ConstructionContext(ConstructionContext::ConstructorInitializerKind),
-        I(I) {
+      ConstructionContext::Kind K, const CXXCtorInitializer *I)
+      : ConstructionContext(K), I(I) {
+    assert(classof(this));
     assert(I);
   }
 
@@ -220,7 +221,57 @@ public:
   const CXXCtorInitializer *getCXXCtorInitializer() const { return I; }
 
   static bool classof(const ConstructionContext *CC) {
-    return CC->getKind() == ConstructorInitializerKind;
+    return CC->getKind() >= INITIALIZER_BEGIN &&
+           CC->getKind() <= INITIALIZER_END;
+  }
+};
+
+/// Represents construction into a field or a base class within a bigger object
+/// via a constructor initializer, eg. T(): field(123) { ... }.
+class SimpleConstructorInitializerConstructionContext
+    : public ConstructorInitializerConstructionContext {
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit SimpleConstructorInitializerConstructionContext(
+      const CXXCtorInitializer *I)
+      : ConstructorInitializerConstructionContext(
+            ConstructionContext::SimpleConstructorInitializerKind, I) {}
+
+public:
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == SimpleConstructorInitializerKind;
+  }
+};
+
+/// Represents construction into a field or a base class within a bigger object
+/// via a constructor initializer, with a single constructor, eg.
+/// T(): field(Field(123)) { ... }. Such construction context may only appear
+/// in C++17 because previously it was split into a temporary object constructor
+/// and an elidable simple constructor-initializer copy-constructor and we were
+/// producing separate construction contexts for these constructors. In C++17
+/// we have a single construction context that combines both. Note that if the
+/// object has trivial destructor, then this code is indistinguishable from
+/// a simple constructor-initializer constructor on the AST level; in this case
+/// we provide a simple constructor-initializer construction context.
+class CXX17ElidedCopyConstructorInitializerConstructionContext
+    : public ConstructorInitializerConstructionContext {
+  const CXXBindTemporaryExpr *BTE;
+
+  friend class ConstructionContext; // Allows to create<>() itself.
+
+  explicit CXX17ElidedCopyConstructorInitializerConstructionContext(
+      const CXXCtorInitializer *I, const CXXBindTemporaryExpr *BTE)
+      : ConstructorInitializerConstructionContext(
+            CXX17ElidedCopyConstructorInitializerKind, I),
+        BTE(BTE) {
+    assert(BTE);
+  }
+
+public:
+  const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
+
+  static bool classof(const ConstructionContext *CC) {
+    return CC->getKind() == CXX17ElidedCopyConstructorInitializerKind;
   }
 };
 

Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Thu Mar 22 15:02:38 2018
@@ -4911,10 +4911,18 @@ static void print_construction_context(r
                                        const ConstructionContext *CC) {
   const Stmt *S1 = nullptr, *S2 = nullptr;
   switch (CC->getKind()) {
-  case ConstructionContext::ConstructorInitializerKind: {
+  case ConstructionContext::SimpleConstructorInitializerKind: {
     OS << ", ";
-    const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
-    print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+    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());
+    S2 = CICC->getCXXBindTemporaryExpr();
     break;
   }
   case ConstructionContext::SimpleVariableKind: {

Modified: cfe/trunk/lib/Analysis/ConstructionContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ConstructionContext.cpp?rev=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ConstructionContext.cpp (original)
+++ cfe/trunk/lib/Analysis/ConstructionContext.cpp Thu Mar 22 15:02:38 2018
@@ -62,21 +62,31 @@ const ConstructionContext *ConstructionC
       // lifetime extension on the parent layer.
       if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
         assert(ParentLayer->isLast());
+        // C++17 *requires* elision of the constructor at the return site
+        // and at variable/member initialization site, while previous standards
+        // were allowing an optional elidable constructor.
+        // This is the C++17 copy-elided construction into a ctor initializer.
+        if (const CXXCtorInitializer *I = ParentLayer->getTriggerInit()) {
+          return create<
+              CXX17ElidedCopyConstructorInitializerConstructionContext>(C,
+                                                                        I, BTE);
+        }
+        assert(ParentLayer->getTriggerStmt() &&
+               "Non-statement-based layers have been handled above!");
+        // This is the normal, non-C++17 case: a temporary object which has
+        // both destruction and materialization info attached to it in the AST.
         if ((MTE = dyn_cast<MaterializeTemporaryExpr>(
                  ParentLayer->getTriggerStmt()))) {
-          // A temporary object which has both destruction and
-          // materialization info.
           return create<TemporaryObjectConstructionContext>(C, BTE, MTE);
         }
-        // C++17 *requires* elision of the constructor at the return site
-        // and at variable initialization site, while previous standards
-        // were allowing an optional elidable constructor.
+        // This is C++17 copy-elided construction into return statement.
         if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
           assert(!RS->getRetValue()->getType().getCanonicalType()
                     ->getAsCXXRecordDecl()->hasTrivialDestructor());
           return create<CXX17ElidedCopyReturnedValueConstructionContext>(C,
                                                                        RS, BTE);
         }
+        // This is C++17 copy-elided construction into a simple variable.
         if (auto *DS = dyn_cast<DeclStmt>(ParentLayer->getTriggerStmt())) {
           assert(!cast<VarDecl>(DS->getSingleDecl())->getType()
                       .getCanonicalType()->getAsCXXRecordDecl()
@@ -104,7 +114,7 @@ const ConstructionContext *ConstructionC
     llvm_unreachable("Unexpected construction context with statement!");
   } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
     assert(TopLayer->isLast());
-    return create<ConstructorInitializerConstructionContext>(C, I);
+    return create<SimpleConstructorInitializerConstructionContext>(C, 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=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Thu Mar 22 15:02:38 2018
@@ -134,7 +134,7 @@ ExprEngine::getRegionForConstructedObjec
           makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
       return LValue.getAsRegion();
     }
-    case ConstructionContext::ConstructorInitializerKind: {
+    case ConstructionContext::SimpleConstructorInitializerKind: {
       const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
       const auto *Init = ICC->getCXXCtorInitializer();
       assert(Init->isAnyMemberInitializer());
@@ -217,6 +217,7 @@ ExprEngine::getRegionForConstructedObjec
     }
     case ConstructionContext::CXX17ElidedCopyVariableKind:
     case ConstructionContext::CXX17ElidedCopyReturnedValueKind:
+    case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
       // Not implemented yet.
       break;
     }

Modified: cfe/trunk/test/Analysis/cfg-rich-constructors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cfg-rich-constructors.cpp?rev=328255&r1=328254&r2=328255&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/cfg-rich-constructors.cpp (original)
+++ cfe/trunk/test/Analysis/cfg-rich-constructors.cpp Thu Mar 22 15:02:38 2018
@@ -252,6 +252,34 @@ public:
   D(double): C(C::get()), c1(new C(C::get())) {}
 };
 
+// Let's see if initializers work well for fields with destructors.
+class E {
+public:
+  static E get();
+  ~E();
+};
+
+class F {
+  E e;
+
+public:
+// FIXME: There should be no temporary destructor in C++17.
+// CHECK: F()
+// CHECK:          1: E::get
+// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class ctor_initializers::E (*)(
+// CXX11-NEXT:     3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6])
+// CXX11-NEXT:     4: [B1.3] (BindTemporary)
+// CXX11-NEXT:     5: [B1.4] (ImplicitCastExpr, NoOp, const class ctor_initializers::E)
+// CXX11-NEXT:     6: [B1.5]
+// CXX11-NEXT:     7: [B1.6] (CXXConstructExpr, e([B1.6]) (Member initializer), class ctor_initializers
+// CXX11-NEXT:     8: e([B1.7]) (Member initializer)
+// CXX11-NEXT:     9: ~ctor_initializers::E() (Temporary object destructor)
+// CXX17-NEXT:     3: [B1.2]() (CXXRecordTypedCall, e([B1.4]) (Member initializer), [B1.4])
+// CXX17-NEXT:     4: [B1.3] (BindTemporary)
+// CXX17-NEXT:     5: e([B1.4]) (Member initializer)
+// CXX17-NEXT:     6: ~ctor_initializers::E() (Temporary object destructor)
+  F(): e(E::get()) {}
+};
 } // end namespace ctor_initializers
 
 namespace return_stmt_without_dtor {




More information about the cfe-commits mailing list