NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet.
Herald added subscribers: cfe-commits, rnkovacs.

Implicit constructor conversions such as `A a = B()` are represented by 
surrounding the constructor for `B()` with an `ImplicitCastExpr` of 
`CK_ConstructorConversion` kind, similarly to how explicit constructor 
conversions are surrounded by a `CXXFunctionalCastExpr`. Support this syntax 
pattern in when understanding the construction context for the implicit 
constructor that performs the conversion.


Repository:
  rC Clang

https://reviews.llvm.org/D44051

Files:
  lib/Analysis/CFG.cpp
  lib/Analysis/ConstructionContext.cpp
  test/Analysis/cfg-rich-constructors.cpp
  test/Analysis/temporaries.cpp

Index: test/Analysis/temporaries.cpp
===================================================================
--- test/Analysis/temporaries.cpp
+++ test/Analysis/temporaries.cpp
@@ -940,3 +940,48 @@
 }
 } // namespace temporary_list_crash
 #endif // C++11
+
+namespace implicit_constructor_conversion {
+struct S {
+  int x;
+  S(int x) : x(x) {}
+  ~S() {}
+};
+
+class C {
+  int x;
+
+public:
+  C(const S &s) : x(s.x) {}
+  ~C() {}
+  int getX() const { return x; }
+};
+
+void test() {
+  const C &c1 = S(10);
+  clang_analyzer_eval(c1.getX() == 10);
+#ifdef TEMPORARY_DTORS
+  // expected-warning@-2{{TRUE}}
+#else
+  // expected-warning@-4{{UNKNOWN}}
+#endif
+
+  S s = 20;
+  clang_analyzer_eval(s.x == 20);
+#ifdef TEMPORARY_DTORS
+  // expected-warning@-2{{TRUE}}
+#else
+  // expected-warning@-4{{UNKNOWN}}
+#endif
+
+  C c2 = s;
+  clang_analyzer_eval(c2.getX() == 20);
+#ifdef TEMPORARY_DTORS
+  // expected-warning@-2{{TRUE}}
+#else
+  // expected-warning@-4{{UNKNOWN}}
+#endif
+}
+} // end namespace implicit_constructor_conversion
+
+
Index: test/Analysis/cfg-rich-constructors.cpp
===================================================================
--- test/Analysis/cfg-rich-constructors.cpp
+++ test/Analysis/cfg-rich-constructors.cpp
@@ -498,20 +498,70 @@
   ~B() {}
 };
 
-// FIXME: Find construction context for the implicit constructor conversion.
+// CHECK: void implicitConstructionConversionFromTemporary()
+// CHECK:          1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A)
+// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
+// CHECK-NEXT:     3: [B1.2]
+// CHECK-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.6], [B1.8], class implicit_constructor_conversion::B)
+// CHECK-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
+// CHECK-NEXT:     6: [B1.5] (BindTemporary)
+// CHECK-NEXT:     7: [B1.6] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
+// CHECK-NEXT:     8: [B1.7]
+// CHECK-NEXT:     9: [B1.8] (CXXConstructExpr, [B1.10], class implicit_constructor_conversion::B)
+// CHECK-NEXT:    10: implicit_constructor_conversion::B b = implicit_constructor_conversion::A();
+// CHECK-NEXT:    11: ~implicit_constructor_conversion::B() (Temporary object destructor)
+// CHECK-NEXT:    12: [B1.10].~B() (Implicit destructor)
+void implicitConstructionConversionFromTemporary() {
+  B b = A();
+}
+
 // CHECK: void implicitConstructionConversionFromFunctionValue()
 // CHECK:          1: get
+// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conversion::A (*)(void))
+// CHECK-NEXT:     3: [B1.2]()
+// CHECK-NEXT:     4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
+// CHECK-NEXT:     5: [B1.4]
+// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B)
+// CHECK-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
+// CHECK-NEXT:     8: [B1.7] (BindTemporary)
+// CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
+// CHECK-NEXT:    10: [B1.9]
+// CHECK-NEXT:    11: [B1.10] (CXXConstructExpr, [B1.12], class implicit_constructor_conversion::B)
+// CHECK-NEXT:    12: implicit_constructor_conversion::B b = get();
+// CHECK-NEXT:    13: ~implicit_constructor_conversion::B() (Temporary object destructor)
+// CHECK-NEXT:    14: [B1.12].~B() (Implicit destructor)
+void implicitConstructionConversionFromFunctionValue() {
+  B b = get();
+}
+
+// CHECK: void implicitConstructionConversionFromTemporaryWithLifetimeExtension()
+// CHECK:          1: implicit_constructor_conversion::A() (CXXConstructExpr, [B1.3], class implicit_constructor_conversion::A)
+// CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
+// CHECK-NEXT:     3: [B1.2]
+// CHECK-NEXT:     4: [B1.3] (CXXConstructExpr, [B1.7], class implicit_constructor_conversion::B)
+// CHECK-NEXT:     5: [B1.4] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_conversion::B)
+// CHECK-NEXT:     6: [B1.5] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
+// CHECK-NEXT:     7: [B1.6]
+// CHECK-NEXT:     8: const implicit_constructor_conversion::B &b = implicit_constructor_conversion::A();
+// CHECK-NEXT:     9: [B1.8].~B() (Implicit destructor)
+void implicitConstructionConversionFromTemporaryWithLifetimeExtension() {
+  const B &b = A();
+}
+
+// FIXME: Find construction context for the implicit constructor conversion.
+// CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension()
+// CHECK:          1: get
 // CHECK-NEXT:     2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver
 // CHECK-NEXT:     3: [B1.2]()
 // CHECK-NEXT:     4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A)
 // CHECK-NEXT:     5: [B1.4]
-// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, class implicit_constructor_conversion::B)
+// CHECK-NEXT:     6: [B1.5] (CXXConstructExpr, [B1.9], class implicit_constructor_conversion::B)
 // CHECK-NEXT:     7: [B1.6] (ImplicitCastExpr, ConstructorConversion, class implicit_constructor_convers
 // CHECK-NEXT:     8: [B1.7] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::B)
 // CHECK-NEXT:     9: [B1.8]
 // CHECK-NEXT:    10: const implicit_constructor_conversion::B &b = get();
 // CHECK-NEXT:    11: [B1.10].~B() (Implicit destructor)
-void implicitConstructionConversionFromFunctionValue() {
+void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() {
   const B &b = get(); // no-crash
 }
 
Index: lib/Analysis/ConstructionContext.cpp
===================================================================
--- lib/Analysis/ConstructionContext.cpp
+++ lib/Analysis/ConstructionContext.cpp
@@ -70,8 +70,11 @@
           C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
       return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
     } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
+      // If the object requires destruction and is not lifetime-extended,
+      // then it must have a BTE within its MTE.
       assert(MTE->getType().getCanonicalType()
-                ->getAsCXXRecordDecl()->hasTrivialDestructor());
+                ->getAsCXXRecordDecl()->hasTrivialDestructor() ||
+             MTE->getStorageDuration() != SD_FullExpression);
       assert(TopLayer->isLast());
       auto *CC =
           C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -1201,8 +1201,13 @@
   case Stmt::ImplicitCastExprClass: {
     auto *Cast = cast<ImplicitCastExpr>(Child);
     // TODO: We need to support CK_ConstructorConversion, maybe other kinds?
-    if (Cast->getCastKind() == CK_NoOp)
+    switch (Cast->getCastKind()) {
+    case CK_NoOp:
+    case CK_ConstructorConversion:
       findConstructionContexts(Layer, Cast->getSubExpr());
+    default:
+      break;
+    }
     break;
   }
   case Stmt::CXXBindTemporaryExprClass: {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D44051: [CFG] [ana... Artem Dergachev via Phabricator via cfe-commits

Reply via email to