[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