The combination of DR 2481 and DR 2126 should allow us to do void f() { constexpr const int &r = 42; static_assert(r == 42); }
because [expr.const]/4.7 now says that "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" is usable in a constant expression. I think the temporary is supposed to be const-qualified, because Core 2481 says so. I was happy to find out that we already mark the temporary as const + constexpr in set_up_extended_ref_temp. But that wasn't enough to make the test above work: references are traditionally implemented as pointers, so the temporary object will be (const int &)&D.1234, and verify_constant -> reduced_constant_expression_p -> initializer_constant_valid_p_1 doesn't think that's OK -- and rightly so -- the address of a local variable certainly isn't constant. Therefore I'm skipping the verify_constant check in cxx_eval_outermost_constant_expr. (DECL_INITIAL isn't checked because maybe we are still waiting for initialize_local_var to set it.) Then we need to be able to evaluate such a reference. This I do by seeing through the reference in cxx_eval_constant_expression. I can't rely on decl_constant_value to pull out DECL_INITIAL, because the VAR_DECL isn't DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P, and I think we don't need to mess with that if we're keeping this purely in constexpr. I wonder if we should accept void f2() { constexpr int &&r = 42; static_assert(r == 42); } Currently we don't -- CP_TYPE_CONST_NON_VOLATILE_P (type) is false in set_up_extended_ref_temp. Does this make sense? Bootstrapped/regtested on x86_64-pc-linux-gnu. PR c++/100976 DR 2481 gcc/cp/ChangeLog: * constexpr.c (cxx_eval_constant_expression): For a constexpr reference, return its DECL_INITIAL. (cxx_eval_outermost_constant_expr): Don't verify the initializer for a constexpr variable of reference type. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ref2.C: Remove dg-error. * g++.dg/cpp0x/constexpr-temp2.C: New test. * g++.dg/cpp23/constexpr-temp1.C: New test. * g++.dg/cpp23/constexpr-temp2.C: New test. --- gcc/cp/constexpr.c | 29 +++++++++++++-- gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C | 5 ++- gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C | 15 ++++++++ gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C | 39 ++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C | 23 ++++++++++++ 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 31fa5b66865..80b4985d055 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -6180,6 +6180,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p); } + /* DR 2126 amended [expr.const]/4.7 to say that "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" is usable in a constant expression. Along with + DR 2481 this means that we should accept + + constexpr const int &r = 42; + static_assert (r == 42); + + Take a shortcut here rather than using decl_constant_value. The + temporary was marked constexpr in set_up_extended_ref_temp. */ + else if (TYPE_REF_P (TREE_TYPE (t)) + && DECL_DECLARED_CONSTEXPR_P (t) + && DECL_INITIAL (t)) + return cxx_eval_constant_expression (ctx, DECL_INITIAL (t), lval, + non_constant_p, overflow_p); /* fall through */ case CONST_DECL: /* We used to not check lval for CONST_DECL, but darwin.c uses @@ -7289,10 +7305,17 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); - if (!constexpr_dtor) - verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); - else + if (object && VAR_P (object) + && DECL_DECLARED_CONSTEXPR_P (object) + && TYPE_REF_P (TREE_TYPE (object))) + /* Circumvent verify_constant, because it ends up calling + initializer_constant_valid_p which doesn't like taking + the address of a local variable. But that's OK since + DR 2126 + DR 2481, at least in a constexpr context. */; + else if (constexpr_dtor) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true; + else + verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); unsigned int i; tree cleanup; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C index 76973638d5f..b7701b849df 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C @@ -9,9 +9,10 @@ constexpr int& ri2 = er; // { dg-error "er" } void f(int j) { + // Used to be erroneous, but allowed since DR 2481. constexpr int i = 42; - constexpr int const& ri = i; // { dg-error "" } + constexpr int const& ri = i; - constexpr int& rj = j; // { dg-error "" } + constexpr int& rj = j; } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C new file mode 100644 index 00000000000..e396264cff3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C @@ -0,0 +1,15 @@ +// DR 2126 - Lifetime-extended temporaries in constant expressions +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +typedef const int CI[3]; +constexpr CI &ci = CI{1, 2, 3}; +SA(ci[1] == 2); + +void +g () +{ + constexpr CI &r = CI{1, 2, 3}; + SA(r[1] == 2); +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C b/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C new file mode 100644 index 00000000000..0d3f4972563 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C @@ -0,0 +1,39 @@ +// PR c++/100976 +// DR 2481 - Cv-qualification of temporary to which a reference is bound +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +struct literal { + int m; + constexpr literal() : m() { } + constexpr literal(int n) : m(n) { } +}; + +void +g () +{ + constexpr const int &r = 42; + constexpr const int &&rr = 42; + constexpr int x = 42; + constexpr static int sx = 42; + constexpr const auto &rx = x; + constexpr const auto &rsx = sx; + SA(r == 42); + SA(rr == 42); + SA(rx == 42); + SA(rsx == 42); + SA(&rx == &x); + SA(&rsx == &sx); + const int *pr = &r; + const int *prr = &rr; +} + +void +f () +{ + constexpr const literal &r = { 42 }; + SA(r.m == 42); + constexpr const literal &&rr = { 42 }; + SA(rr.m == 42); +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C b/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C new file mode 100644 index 00000000000..1e1169ac5e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C @@ -0,0 +1,23 @@ +// PR c++/100976 +// DR 2481 - Cv-qualification of temporary to which a reference is bound +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +int +main () +{ + constexpr const volatile int &r = 42; // { dg-error "cannot bind" } + constexpr const volatile int &&rr = 42; + constexpr int x = 42; + constexpr const volatile auto &rx = x; + SA(rr == 42); // { dg-error "non-constant|lvalue-to-rvalue" } + SA(rx == 42); // { dg-error "non-constant|lvalue-to-rvalue" } + + int i = 42; + constexpr const auto &ri = i; + SA(ri == 42); // { dg-error "non-constant|not usable" } + + constexpr const int &missing; // { dg-error "not initialized" } + SA ((bool(missing), true)); +} base-commit: f364cdffa47af574f90f671b2dcf5afa91442741 -- 2.31.1