Hi!

We emit weird diagnostics on the following testcase in C++11.
If it is not a template, maybe_save_constexpr_fundef calls first
  if (!is_valid_constexpr_fn (fun, complain))
    return;
(which doesn't fail) and then
  tree massaged = massage_constexpr_body (fun, DECL_SAVED_TREE (fun));
  if (massaged == NULL_TREE || massaged == error_mark_node)
    {
      if (!DECL_CONSTRUCTOR_P (fun) && complain)
        error ("body of %<constexpr%> function %qD not a return-statement",
               fun);
      return;
    }
which diagnoses it and if even that would succeed, go on with
  bool potential = potential_rvalue_constant_expression (massaged);
  if (!potential && complain)
    require_potential_rvalue_constant_expression_fncheck (massaged);
In templates, maybe_save_constexpr_fundef returns early:
  if (processing_template_decl
      || cp_function_chain->invalid_constexpr
      || (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun)))
    return;
and then it is called again during instantiation.
But in that case DECL_GENERATED_P (fun) is true and so we silently return
on errors without diagnosing them:
  bool complain = !DECL_GENERATED_P (fun) && !implicit;
Now, when we actually try to constexpr evaluate those (if at all), we
emit an error and then
'constexpr ...' is not usable as a 'constexpr' function because:
message and then explain_invalid_constexpr_fn tries to diagnose
the errors by calling is_valid_constexpr_fn (fun, true) and
require_potential_rvalue_constant_expression (massaged).  So it diagnoses
those 2 cases, but misses the one where massaged was NULL or error_mark_node
for a non-constructor, so after the because: there is no reason emitted.

The following patch diagnoses even that.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-02-03  Jakub Jelinek  <[email protected]>

        PR c++/123889
        * constexpr.cc (explain_invalid_constexpr_fn): Diagnose
        NULL or error_mark_node massaged on non-constructor.

        * g++.dg/cpp0x/constexpr-123889.C: New test.

--- gcc/cp/constexpr.cc.jj      2026-01-31 10:16:31.950164802 +0100
+++ gcc/cp/constexpr.cc 2026-02-02 14:35:43.506094992 +0100
@@ -1116,6 +1116,10 @@ explain_invalid_constexpr_fn (tree fun)
          else
            body = DECL_SAVED_TREE (fun);
          tree massaged = massage_constexpr_body (fun, body);
+         if ((massaged == NULL_TREE || massaged == error_mark_node)
+             && !DECL_CONSTRUCTOR_P (fun))
+           error ("body of %<constexpr%> function %qD not a return-statement",
+                  fun);
          require_potential_rvalue_constant_expression (massaged);
          if (DECL_CONSTRUCTOR_P (fun))
            {
--- gcc/testsuite/g++.dg/cpp0x/constexpr-123889.C.jj    2026-02-02 
14:42:30.345152859 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-123889.C       2026-02-02 
14:44:10.958435899 +0100
@@ -0,0 +1,14 @@
+// PR c++/123889
+// { dg-do compile { target c++11 } }
+
+template <class T>
+constexpr int func (T) {
+// { dg-message "'constexpr int func\\\(T\\\) \\\[with T = int\\\]' is not 
usable as a 'constexpr' function because:" "" { target c++11_only } .-1 }
+// { dg-error "body of 'constexpr' function 'constexpr int func\\\(T\\\) 
\\\[with T = int\\\]' not a return-statement" "" { target c++11_only } .-2 }
+  return 1;
+  return 2;
+}
+
+static_assert (func (1) == 1, "");
+// { dg-error "non-constant condition for static assertion" "" { target 
c++11_only } .-1 }
+// { dg-error "'constexpr int func\\\(T\\\) \\\[with T = int\\\]' called in a 
constant expression" "" { target c++11_only } .-2 }

        Jakub

Reply via email to