[clang] 061f3a5 - P0593R6: Pseudo-destructor expressions end object lifetimes.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 18 18:41:16 PST 2020


Author: Richard Smith
Date: 2020-02-18T18:41:03-08:00
New Revision: 061f3a50dd824f1eb2394d0f699f3f2ee374b21a

URL: https://github.com/llvm/llvm-project/commit/061f3a50dd824f1eb2394d0f699f3f2ee374b21a
DIFF: https://github.com/llvm/llvm-project/commit/061f3a50dd824f1eb2394d0f699f3f2ee374b21a.diff

LOG: P0593R6: Pseudo-destructor expressions end object lifetimes.

This only has an observable effect on constant evaluation.

Added: 
    

Modified: 
    clang/lib/AST/ExprConstant.cpp
    clang/test/CXX/expr/expr.const/p2-0x.cpp
    clang/test/SemaCXX/constant-expression-cxx2a.cpp
    clang/www/cxx_status.html

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 641368ebfdd9..9a31b64eedda 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1417,6 +1417,31 @@ static bool isFormalAccess(AccessKinds AK) {
   return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy;
 }
 
+/// Is this kind of axcess valid on an indeterminate object value?
+static bool isValidIndeterminateAccess(AccessKinds AK) {
+  switch (AK) {
+  case AK_Read:
+  case AK_Increment:
+  case AK_Decrement:
+    // These need the object's value.
+    return false;
+
+  case AK_ReadObjectRepresentation:
+  case AK_Assign:
+  case AK_Construct:
+  case AK_Destroy:
+    // Construction and destruction don't need the value.
+    return true;
+
+  case AK_MemberCall:
+  case AK_DynamicCast:
+  case AK_TypeId:
+    // These aren't really meaningful on scalars.
+    return true;
+  }
+  llvm_unreachable("unknown access kind");
+}
+
 namespace {
   struct ComplexValue {
   private:
@@ -3201,9 +3226,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
   for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
     // Reading an indeterminate value is undefined, but assigning over one is OK.
     if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) ||
-        (O->isIndeterminate() && handler.AccessKind != AK_Construct &&
-         handler.AccessKind != AK_Assign &&
-         handler.AccessKind != AK_ReadObjectRepresentation)) {
+        (O->isIndeterminate() &&
+         !isValidIndeterminateAccess(handler.AccessKind))) {
       if (!Info.checkingPotentialConstantExpression())
         Info.FFDiag(E, diag::note_constexpr_access_uninit)
             << handler.AccessKind << O->isIndeterminate();
@@ -5476,6 +5500,8 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, ArgVector &ArgValues,
         }
     }
   }
+  // FIXME: This is the wrong evaluation order for an assignment operator
+  // called via operator syntax.
   for (unsigned Idx = 0; Idx < Args.size(); Idx++) {
     if (!Evaluate(ArgValues[Idx], Info, Args[Idx])) {
       // If we're checking for a potential constant expression, evaluate all
@@ -6936,10 +6962,8 @@ class ExprEvaluatorBase
       } else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) {
         if (!Info.getLangOpts().CPlusPlus2a)
           Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor);
-        // FIXME: If pseudo-destructor calls ever start ending the lifetime of
-        // their callee, we should start calling HandleDestruction here.
-        // For now, we just evaluate the object argument and discard it.
-        return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal);
+        return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal) &&
+               HandleDestruction(Info, PDE, ThisVal, PDE->getDestroyedType());
       } else
         return Error(Callee);
       FD = Member;

diff  --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp
index cc6380e044fb..c418767f8d12 100644
--- a/clang/test/CXX/expr/expr.const/p2-0x.cpp
+++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp
@@ -424,8 +424,26 @@ namespace PseudoDtor {
   int k;
   typedef int I;
   struct T {
-    int n : (k.~I(), 1); // cxx11-warning {{constant expression}} cxx11-note {{pseudo-destructor}}
+    int n : (k.~I(), 1); // expected-error {{constant expression}} expected-note {{visible outside that expression}}
   };
+
+  // FIXME: It's unclear whether this should be accepted in C++20 mode. The parameter is destroyed twice here.
+  constexpr int f(int a = 1) { // cxx11-error {{constant expression}}
+    return (
+        a.~I(), // cxx11-note 2{{pseudo-destructor}}
+        0);
+  }
+  static_assert(f() == 0, ""); // cxx11-error {{constant expression}} cxx11-note {{in call}}
+
+  // This is OK in C++20: the union has no active member after the
+  // pseudo-destructor call, so the union destructor has no effect.
+  union U { int x; };
+  constexpr int g(U u = {1}) { // cxx11-error {{constant expression}}
+    return (
+        u.x.~I(), // cxx11-note 2{{pseudo-destructor}}
+        0);
+  }
+  static_assert(g() == 0, ""); // cxx11-error {{constant expression}} cxx11-note {{in call}}
 }
 
 // - increment or decrement operations (5.2.6, 5.3.2);

diff  --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
index 3d5172619dbf..01d2a7f58d48 100644
--- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
@@ -1047,6 +1047,11 @@ namespace memory_leaks {
   static_assert(h({new bool(true)})); // ok
 }
 
+void *operator new(std::size_t, void*);
+namespace std {
+  template<typename T> constexpr T *construct(T *p) { return new (p) T; }
+}
+
 namespace dtor_call {
   struct A { int n; };
   constexpr void f() { // expected-error {{never produces a constant expression}}
@@ -1065,15 +1070,22 @@ namespace dtor_call {
   }
   static_assert((g(), true));
 
-  constexpr bool pseudo() {
+  constexpr bool pseudo(bool read, bool recreate) {
     using T = bool;
-    bool b = false;
-    // This does evaluate the store to 'b'...
+    bool b = false; // expected-note {{lifetime has already ended}}
+    // This evaluates the store to 'b'...
     (b = true).~T();
-    // ... but does not end the lifetime of the object.
-    return b;
-  }
-  static_assert(pseudo());
+    // ... and ends the lifetime of the object.
+    return (read
+            ? b // expected-note {{read of object outside its lifetime}}
+            : true) +
+           (recreate
+            ? (std::construct(&b), true)
+            : true);
+  }
+  static_assert(pseudo(false, false)); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(pseudo(true, false)); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(pseudo(false, true));
 
   constexpr void use_after_destroy() {
     A a;
@@ -1248,6 +1260,8 @@ namespace dtor_call {
     // We used to think this was an -> member access because its left-hand side
     // is a pointer. Ensure we don't crash.
     p.~T();
+    // Put a T back so we can destroy it again.
+    std::construct(&p);
   }
   static_assert((destroy_pointer(), true));
 }
@@ -1301,13 +1315,12 @@ namespace mutable_subobjects {
   constexpr void destroy1() { // expected-error {{constexpr}}
     a.~A(); // expected-note {{cannot modify an object that is visible outside}}
   }
-  // FIXME: These should both be rejected once P0593 is implemented.
   using T = int;
-  constexpr void destroy2() {
-    a.m.~T();
+  constexpr void destroy2() { // expected-error {{constexpr}}
+    a.m.~T(); // expected-note {{cannot modify an object that is visible outside}}
   }
-  constexpr void destroy3() {
-    a.n.~T();
+  constexpr void destroy3() { // expected-error {{constexpr}}
+    a.n.~T(); // expected-note {{cannot modify an object that is visible outside}}
   }
 
   struct X {

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index d75d5b9c354b..58d9364192d6 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1199,7 +1199,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
     <tr>
       <td>Pseudo-destructors end object lifetimes</td>
       <td><a href="https://wg21.link/p0593r6">P0593R6</a> (<a href="#dr">DR</a>)</td>
-      <td class="partial" align="center">Partial</td>
+      <td class="unreleased" align="center">Clang 11</td>
     </tr>
 </table>
 


        


More information about the cfe-commits mailing list