[gcc r17-3321] c++: Adjust constexpr covariant call results [PR126324]
Jason Merrill via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:eb54aa2d961ec016d15bd39ea62579af4ad25710 commit r17-3321-geb54aa2d961ec016d15bd39ea62579af4ad25710 Author: Odysseas Georgoudis <[email protected]> Date: Fri Jul 24 23:18:32 2026 +0100 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]> Diff: --- gcc/cp/constexpr.cc | 44 +++++++++++++++--- .../g++.dg/cpp2a/constexpr-virtual-pr126324.C | 53 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index c9ebc0027b7d..30c46b7b6a0a 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 000000000000..39837da75aa4 --- /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 ());