Author: Richard Smith Date: 2020-10-23T14:29:18-07:00 New Revision: cb9b9842d31d6082c6a2259a64eb5d47bd680d32
URL: https://github.com/llvm/llvm-project/commit/cb9b9842d31d6082c6a2259a64eb5d47bd680d32 DIFF: https://github.com/llvm/llvm-project/commit/cb9b9842d31d6082c6a2259a64eb5d47bd680d32.diff LOG: PR47954 / DR2126: permit temporary objects that are lifetime-extended by variables that are usable in constant expressions to themselves be usable in constant expressions. Added: Modified: clang/include/clang/AST/ExprCXX.h clang/lib/AST/ExprCXX.cpp clang/lib/AST/ExprConstant.cpp clang/test/CXX/drs/dr21xx.cpp clang/test/CodeGenCXX/const-init-cxx11.cpp clang/test/SemaCXX/constant-expression-cxx11.cpp clang/www/cxx_dr_status.html Removed: ################################################################################ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 61a23ddaa368..2144279f132b 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4519,6 +4519,10 @@ class MaterializeTemporaryExpr : public Expr { return getValueKind() == VK_LValue; } + /// Determine whether this temporary object is usable in constant + /// expressions, as specified in C++20 [expr.const]p4. + bool isUsableInConstantExpressions(const ASTContext &Context) const; + SourceLocation getBeginLoc() const LLVM_READONLY { return getSubExpr()->getBeginLoc(); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index c1ec86075772..b7f677051ea2 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1647,6 +1647,20 @@ void MaterializeTemporaryExpr::setExtendingDecl(ValueDecl *ExtendedBy, ES->ManglingNumber = ManglingNumber; } +bool MaterializeTemporaryExpr::isUsableInConstantExpressions( + const ASTContext &Context) const { + // C++20 [expr.const]p4: + // An object or reference is usable in constant expressions if it is [...] + // a temporary object of non-volatile const-qualified literal type + // whose lifetime is extended to that of a variable that is usable + // in constant expressions + auto *VD = dyn_cast_or_null<VarDecl>(getExtendingDecl()); + return VD && getType().isConstant(Context) && + !getType().isVolatileQualified() && + getType()->isLiteralType(Context) && + VD->isUsableInConstantExpressions(Context); +} + TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind, ArrayRef<TypeSourceInfo *> Args, SourceLocation RParenLoc, bool Value) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index b33ce14b3658..fa666930ef1d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -4093,27 +4093,32 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, assert(MTE->getStorageDuration() == SD_Static && "should have a frame for a non-global materialized temporary"); - // Per C++1y [expr.const]p2: + // C++20 [expr.const]p4: [DR2126] + // An object or reference is usable in constant expressions if it is + // - a temporary object of non-volatile const-qualified literal type + // whose lifetime is extended to that of a variable that is usable + // in constant expressions + // + // C++20 [expr.const]p5: // an lvalue-to-rvalue conversion [is not allowed unless it applies to] - // - a [...] glvalue of integral or enumeration type that refers to - // a non-volatile const object [...] - // [...] - // - a [...] glvalue of literal type that refers to a non-volatile - // object whose lifetime began within the evaluation of e. + // - a non-volatile glvalue that refers to an object that is usable + // in constant expressions, or + // - a non-volatile glvalue of literal type that refers to a + // non-volatile object whose lifetime began within the evaluation + // of E; // // C++11 misses the 'began within the evaluation of e' check and // instead allows all temporaries, including things like: // int &&r = 1; // int x = ++r; // constexpr int k = r; - // Therefore we use the C++14 rules in C++11 too. + // Therefore we use the C++14-onwards rules in C++11 too. // // Note that temporaries whose lifetimes began while evaluating a // variable's constructor are not usable while evaluating the // corresponding destructor, not even if they're of const-qualified // types. - if (!(BaseType.isConstQualified() && - BaseType->isIntegralOrEnumerationType()) && + if (!MTE->isUsableInConstantExpressions(Info.Ctx) && !lifetimeStartedInEvaluation(Info, LVal.Base)) { if (!IsAccess) return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp index 7e3ffa057ef7..6810944cde65 100644 --- a/clang/test/CXX/drs/dr21xx.cpp +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -32,6 +32,40 @@ namespace dr2120 { // dr2120: 7 static_assert(!__is_standard_layout(E), ""); } +namespace dr2126 { // dr2126: 12 +#if __cplusplus >= 201103L + struct A { int n; }; + + const A &a = {1}; // const temporary + A &b = (A &)(const A &)A{1}; // const temporary + A &&c = (A &&)(const A &)A{1}; // const temporary + + A &&d = {1}; // non-const temporary expected-note {{here}} + const A &e = (A &)(A &&) A{1}; // non-const temporary expected-note {{here}} + A &&f = (A &&)(A &&) A{1}; // non-const temporary expected-note {{here}} + + constexpr const A &g = {1}; // const temporary + constexpr A &&h = {1}; // non-const temporary expected-note {{here}} + + struct B { const A &a; }; + B i = {{1}}; // extending decl not usable in constant expr expected-note {{here}} + const B j = {{1}}; // extending decl not usable in constant expr expected-note {{here}} + constexpr B k = {{1}}; // extending decl usable in constant expr + + static_assert(a.n == 1, ""); + static_assert(b.n == 1, ""); + static_assert(c.n == 1, ""); + static_assert(d.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}} + static_assert(e.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}} + static_assert(f.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}} + static_assert(g.n == 1, ""); + static_assert(h.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}} + static_assert(i.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of non-constexpr variable}} + static_assert(j.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}} + static_assert(k.a.n == 1, ""); +#endif +} + namespace dr2140 { // dr2140: 9 #if __cplusplus >= 201103L union U { int a; decltype(nullptr) b; }; diff --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp b/clang/test/CodeGenCXX/const-init-cxx11.cpp index 2673ac2baa10..3684ad1c5ff1 100644 --- a/clang/test/CodeGenCXX/const-init-cxx11.cpp +++ b/clang/test/CodeGenCXX/const-init-cxx11.cpp @@ -400,6 +400,22 @@ namespace UnemittedTemporaryDecl { // CHECK: @_ZN22UnemittedTemporaryDecl4ref2E = constant i32* @_ZGRN22UnemittedTemporaryDecl3refE_ } +namespace DR2126 { + struct A { int &&b; }; + constexpr const A &a = {42}; + // CHECK: @_ZGRN6DR21261aE0_ = internal global i32 42 + // FIXME: This is unused and need not be emitted. + // CHECK: @_ZGRN6DR21261aE_ = internal constant {{.*}} { i32* @_ZGRN6DR21261aE0_ } + // CHECK: @_ZN6DR21261rE = constant i32* @_ZGRN6DR21261aE0_ + int &r = a.b; + + // Dynamically initialized: the temporary object bound to 'b' could be + // modified (eg, by placement 'new') before the initializer of 's' runs. + constexpr A &&b = {42}; + // CHECK: @_ZN6DR21261sE = global i32* null + int &s = b.b; +} + // CHECK: @_ZZN12LocalVarInit3aggEvE1a = internal constant {{.*}} i32 101 // CHECK: @_ZZN12LocalVarInit4ctorEvE1a = internal constant {{.*}} i32 102 // CHECK: @__const._ZN12LocalVarInit8mutable_Ev.a = private unnamed_addr constant {{.*}} i32 103 diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 7d51a48eac42..0e97798cc197 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -409,12 +409,23 @@ namespace ConstAddedByReference { const int &r = (0); constexpr int n = r; + int &&r2 = 0; // expected-note {{created here}} + constexpr int n2 = r2; // expected-error {{constant}} expected-note {{read of temporary}} + struct A { constexpr operator int() const { return 0; }}; struct B { constexpr operator const int() const { return 0; }}; const int &ra = A(); const int &rb = B(); constexpr int na = ra; constexpr int nb = rb; + + struct C { int &&r; }; + constexpr C c1 = {1}; + constexpr int &c1r = c1.r; + constexpr const C &c2 = {2}; + constexpr int &c2r = c2.r; + constexpr C &&c3 = {3}; // expected-note {{created here}} + constexpr int &c3r = c3.r; // expected-error {{constant}} expected-note {{read of temporary}} } } @@ -1843,6 +1854,11 @@ namespace InitializerList { static_assert(*std::initializer_list<int>{1, 2, 3}.begin() == 1, ""); static_assert(std::initializer_list<int>{1, 2, 3}.begin()[2] == 3, ""); + + namespace DR2126 { + constexpr std::initializer_list<float> il = {1.0, 2.0, 3.0}; + static_assert(il.begin()[1] == 2.0, ""); + } } namespace StmtExpr { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 74319b138943..89e4fe0f7dc9 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -12571,7 +12571,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg2126">2126</a></td> <td>DRWP</td> <td>Lifetime-extended temporaries in constant expressions</td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 12</td> </tr> <tr class="open" id="2127"> <td><a href="https://wg21.link/cwg2127">2127</a></td> @@ -13843,7 +13843,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://wg21.link/cwg2338">2338</a></td> <td>DRWP</td> <td>Undefined behavior converting to short enums with fixed underlying types</td> - <td class="none" align="center">Unknown</td> + <td class="unreleased" align="center">Clang 12</td> </tr> <tr id="2339"> <td><a href="https://wg21.link/cwg2339">2339</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits