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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits