[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