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" }