]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: poor diag w/ non-constexpr dtor called from constexpr ctor
authorPatrick Palka <ppalka@redhat.com>
Thu, 1 May 2025 15:40:44 +0000 (11:40 -0400)
committerPatrick Palka <ppalka@redhat.com>
Thu, 1 May 2025 15:40:44 +0000 (11:40 -0400)
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-genericized body to
require_potential_constant_expression.

gcc/testsuite/ChangeLog:

* g++.dg/cpp23/constexpr-nonlit19.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/constexpr.cc
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C [new file with mode: 0644]

index 5b7b70f7e65738d64d22f3c352609b1da6aaebb8..fa754b9a176a7b8068992fedbfe4ee2acce0d386 100644 (file)
@@ -1110,17 +1110,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 (file)
index 0000000..1b73e2d
--- /dev/null
@@ -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" }