[PATCH v3] c++: Adjust constexpr covariant call results [PR126324]

Odysseas Georgoudis <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <FRWP195MB2864A1BE3A35F04B6656BA29CCDA2@FRWP195MB2864.EURP195.PROD.OUTLOOK.COM>
  Hi Jason,

  Thanks. Attached is v3 with the requested changes: the commit message is
  wrapped at 76 columns, the test is renamed, and the type check now uses
  INDIRECT_TYPE_P with the additional parentheses.

  I kept the type guard because this adjustment is specifically for covariant
  pointer or reference returns.  I also rebased onto current master.

  Tested on x86_64-pc-linux-gnu with no unexpected constexpr or consteval
  test failures.

  Thanks,
  Odysseas
________________________________
From: Jason Merrill <[email protected]>
Sent: 14 August 2026 18:57
To: Odysseas Georgoudis <[email protected]>; [email protected] <[email protected]>
Subject: Re: [PATCH v2] c++: Adjust constexpr covariant call results [PR126324]

On 7/26/26 5:15 PM, Odysseas Georgoudis wrote:
> Hi Jason,
>
> Thanks for catching the non-zero-offset case.
> In v2, the result-thunk path now preserves null pointers,
> evaluates the call only once, and adjusts the result to
> the thunk’s static return type.
>
> The test covers both zero- and non-zero-offset bases.
Looks good, just a few tweaks.

> pointers, evaluate the call only once, and give the result the thunk's static
>        * constexpr.cc (cxx_eval_thunk_call): Preserve null pointer results and
These lines are too long; the commit message should wrap at column 76 to
accommodate 'git log' adding 4 spaces on the left.

>        * g++.dg/cpp2a/pr126324.C: New test.

Let's add "constexpr-virtual" to the filename, i.e.
constexpr-virtual-pr126324.C

> +      && scalarish_type_p (TREE_TYPE (t))

Let's use INDIRECT_TYPE_P since a covariant return type will be pointer
or reference.  Or drop this line entirely; is it needed?

> +      && !same_type_ignoring_top_level_qualifiers_p
> +        (TREE_TYPE (result), TREE_TYPE (t)))

This needs another set of parens starting either before or after the !
to prevent emacs from moving the args to line up with the &&.

Jason
PR126324-v3.patch (application/octet-stream, 5.1 KB)
From d538304712da3bd2a493ddeb3e8c2e659a5fba1c Mon Sep 17 00:00:00 2001
From: Odysseas Georgoudis <[email protected]>
Date: Fri, 24 Jul 2026 23:18:32 +0100
Subject: [PATCH v3] c++: Adjust constexpr covariant call results [PR126324]

Constant evaluation of a virtual call can evaluate the final overrider
directly.  For a zero-offset covariant override, the result then has the
overrider's declared type rather than the static type of the call.  Storing
that result can make cxx_eval_store_expression mistake the scalar type
mismatch for an empty-base initialization and ICE.

For a nonzero covariant adjustment, virtual lookup uses a result thunk.
cxx_eval_thunk_call applied the fixed offset even to a null result and left
the adjusted result with the overrider's return type, causing the same type
mismatch.

Adjust successful virtual-call results to the static call type after
caching the underlying function result.  Make result-thunk evaluation
preserve null pointers, evaluate the call only once, and give the result
the thunk's static return type.

gcc/cp/ChangeLog:

	PR c++/126324
	* constexpr.cc (cxx_eval_thunk_call): Preserve null pointer
	results and adjust the result to the thunk's return type.
	(cxx_eval_call_expression): Adjust covariant virtual-call results
	to the static call type.

gcc/testsuite/ChangeLog:

	PR c++/126324
	* g++.dg/cpp2a/constexpr-virtual-pr126324.C: New test.

Signed-off-by: Odysseas Georgoudis <[email protected]>
---
 gcc/cp/constexpr.cc                           | 44 ++++++++++++---
 .../g++.dg/cpp2a/constexpr-virtual-pr126324.C | 53 +++++++++++++++++++
 2 files changed, 91 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-virtual-pr126324.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index c9ebc0027b7..30c46b7b6a0 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3908,13 +3908,29 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
       CALL_EXPR_ARG (new_call, 0) = this_arg;
     }
   else
-    /* Return-adjusting thunk.  */
-    new_call = build2 (POINTER_PLUS_EXPR, TREE_TYPE (new_call),
-		       new_call, offset);
+    {
+      /* Return-adjusting thunk.  As in expand_thunk, adjust a pointer only
+	 if it is non-null.  */
+      tree call = new_call;
+      if (TYPE_PTR_P (TREE_TYPE (call)))
+	call = save_expr (call);
+      new_call = build2 (POINTER_PLUS_EXPR, TREE_TYPE (call), call, offset);
+      if (TYPE_PTR_P (TREE_TYPE (call)))
+	new_call = build_if_nonnull (call, new_call, tf_none);
+    }
 
-  return cxx_eval_constant_expression (ctx, new_call, lval,
-				       non_constant_p, overflow_p,
-				       jump_target);
+  tree result = cxx_eval_constant_expression (ctx, new_call, lval,
+					       non_constant_p, overflow_p,
+					       jump_target);
+  if (!*non_constant_p
+      && !*overflow_p
+      && !*jump_target
+      && result != void_node
+      && INDIRECT_TYPE_P (TREE_TYPE (t))
+      && !(same_type_ignoring_top_level_qualifiers_p
+	   (TREE_TYPE (result), TREE_TYPE (t))))
+    result = adjust_temp_type (TREE_TYPE (t), result);
+  return result;
 }
 
 /* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set
@@ -4746,6 +4762,22 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
+
+  /* A virtual call with a zero-offset covariant return needs no thunk, so
+     constant evaluation can evaluate the final overrider directly.  The
+     result then has the overrider's return type rather than the static type
+     of the call.  Adjust after caching so a direct call can reuse the result
+     with its original type.  */
+  if (!*non_constant_p
+      && !*overflow_p
+      && !*jump_target
+      && DECL_VIRTUAL_P (fun)
+      && result != void_node
+      && INDIRECT_TYPE_P (TREE_TYPE (t))
+      && !(same_type_ignoring_top_level_qualifiers_p
+	   (TREE_TYPE (result), TREE_TYPE (t))))
+    result = adjust_temp_type (TREE_TYPE (t), result);
+
   return result;
 }
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual-pr126324.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual-pr126324.C
new file mode 100644
index 00000000000..39837da75aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual-pr126324.C
@@ -0,0 +1,53 @@
+// PR c++/126324
+// { dg-do compile { target c++20 } }
+
+struct B
+{
+  virtual constexpr B *clone(bool null)
+  {
+    return null ? nullptr : this;
+  }
+};
+
+struct C
+{
+  virtual void dummy() { }
+};
+
+struct D : B
+{
+  constexpr D *clone(bool null) override
+  {
+    return null ? nullptr : this;
+  }
+};
+
+struct E : C, B
+{
+  int calls = 0;
+
+  constexpr E *clone(bool null) override
+  {
+    ++calls;
+    return null ? nullptr : this;
+  }
+};
+
+constexpr bool test()
+{
+  D d;
+  B *b = &d;
+  D *direct = d.clone (false);
+  B *nonnull = b->clone (false);
+  B *null = b->clone (true);
+
+  E e;
+  B *eb = &e;
+  B *enonnull = eb->clone (false);
+  B *enull = eb->clone (true);
+
+  return (direct == &d && nonnull == b && null == nullptr
+	  && enonnull == eb && enull == nullptr && e.calls == 2);
+}
+
+static_assert (test ());
-- 
2.43.5
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.