From 31f048b3a157815caeebec58e82c323c11694ca6 Mon Sep 17 00:00:00 2001
From: Odysseas Georgoudis <odygrd@hotmail.com>
Date: Fri, 24 Jul 2026 23:18:32 +0100
Subject: [PATCH] c++: Adjust constexpr covariant call results [PR126324]

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

Adjust a successful scalar virtual-call result to the static call type
after caching the underlying function result.  This lets a direct call
reuse the cached result with its declared type.

gcc/cp/ChangeLog:

	PR c++/126324
	* constexpr.cc (cxx_eval_call_expression): Adjust covariant virtual
	call results to the static call type.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Odysseas Georgoudis <odygrd@hotmail.com>
---
 gcc/cp/constexpr.cc                   | 15 ++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/pr126324.C | 30 +++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/pr126324.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index b4db9fd2bae..5f8c64c2bdd 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -4731,6 +4731,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
+
+  /* A virtual call with a covariant return type can evaluate the final
+     overrider directly, whose result has its declared 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
+      && scalarish_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/pr126324.C b/gcc/testsuite/g++.dg/cpp2a/pr126324.C
new file mode 100644
index 00000000000..4122f90467c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/pr126324.C
@@ -0,0 +1,30 @@
+// PR c++/126324
+// { dg-do compile { target c++20 } }
+
+struct B
+{
+  virtual constexpr B *clone(bool null)
+  {
+    return null ? nullptr : this;
+  }
+};
+
+struct D : B
+{
+  constexpr D *clone(bool null) override
+  {
+    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);
+  return direct == &d && nonnull == b && null == nullptr;
+}
+
+static_assert (test ());
-- 
2.43.5

