On 3/14/25 1:34 PM, Patrick Palka wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for stage 1?
OK.
-- >8 -- When diagnosing a non-constexpr constructor call during constexpr evaluation, explain_invalid_constexpr_fn was passing the genericized body to require_potential_constant_expression rather than the saved non-genericized one. This meant for the below testcase (reduced from PR libstdc++/119282) in which B::B() is deemed non-constexpr due to the local variable having a non-constexpr destructor we would then issue the cryptic diagnostic: constexpr-nonlit19.C:17:16: error: non-constant condition for static assertion 17 | static_assert(f()); | ~^~ constexpr-nonlit19.C:17:16: in ‘constexpr’ expansion of ‘f()’ constexpr-nonlit19.C:13:5: error: ‘constexpr B::B()’ called in a constant expression 13 | B b; | ^ constexpr-nonlit19.C:6:13: note: ‘constexpr B::B()’ is not usable as a ‘constexpr’ function because: 6 | constexpr B() { | ^ constexpr-nonlit19.C:8:5: error: ‘goto’ is not a constant expression 8 | for (int i = 0; i < 10; i++) { } | ^~~ This patch makes us pass the non-genericized body to require_potential_constant_expression, and so we now emit: ... constexpr-nonlit19.C:6:13: note: ‘constexpr B::B()’ is not usable as a ‘constexpr’ function because: 6 | constexpr B() { | ^ constexpr-nonlit19.C:9:3: error: call to non-‘constexpr’ function ‘A::~A()’ 9 | } | ^ constexpr-nonlit19.C:3:12: note: ‘A::~A()’ declared here 3 | struct A { ~A() { } }; | ^ gcc/cp/ChangeLog: * constexpr.cc (explain_invalid_constexpr_fn): In the DECL_CONSTRUCTOR_P branch pass the non-gimplified body to require_potential_constant_expression. gcc/testsuite/ChangeLog: * g++.dg/cpp23/constexpr-nonlit19.C: New test. --- gcc/cp/constexpr.cc | 13 +++++-------- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 4820bcc84aa..5b2ae2cbe0a 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1097,17 +1097,14 @@ explain_invalid_constexpr_fn (tree fun) body = fd->body; else body = DECL_SAVED_TREE (fun); - body = massage_constexpr_body (fun, body); - require_potential_rvalue_constant_expression (body); + tree massaged = massage_constexpr_body (fun, body); + require_potential_rvalue_constant_expression (massaged); if (DECL_CONSTRUCTOR_P (fun)) { - cx_check_missing_mem_inits (DECL_CONTEXT (fun), body, true); + cx_check_missing_mem_inits (DECL_CONTEXT (fun), massaged, true); if (cxx_dialect > cxx11) - { - /* Also check the body, not just the ctor-initializer. */ - body = DECL_SAVED_TREE (fun); - require_potential_rvalue_constant_expression (body); - } + /* Also check the body, not just the ctor-initializer. */ + require_potential_rvalue_constant_expression (body); } } } diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C new file mode 100644 index 00000000000..1b73e2d8209 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C @@ -0,0 +1,17 @@ +// { dg-do compile { target c++23 } } + +struct A { ~A() { } }; + +struct B { + constexpr B() { + A a; + for (int i = 0; i < 10; i++) { } + } // { dg-error "call to non-'constexpr' function 'A::~A..'" } +}; + +constexpr bool f() { + B b; // { dg-error "B::B..' called in a constant expression" } + return true; +} + +static_assert(f()); // { dg-error "non-constant" }