https://gcc.gnu.org/g:4073b1bae63cf4f0bf834fefff8e02085b606e8f
commit r16-6955-g4073b1bae63cf4f0bf834fefff8e02085b606e8f Author: Jakub Jelinek <[email protected]> Date: Wed Jan 21 14:32:26 2026 +0100 c++: Fix ICE in build_base_path -> resolves_to_fixed_type_p -> fixed_type_or_null [PR123692] The move of the resolves_to_fixed_type_p call earlier in build_base_path for constexpr virtual inheritance caused the following ICE. What changed is that in an unevaluated context expr can be a CALL_EXPR with class type and when it is not a ctor, resolves_to_fixed_type_p -> fixed_type_or_null ICEs on it: if (CLASS_TYPE_P (TREE_TYPE (instance))) { /* We missed a build_cplus_new somewhere, likely due to tf_decltype mishandling. */ gcc_checking_assert (false); Now, the reason why that worked fine when resolves_to_fixed_type_p was later in the function is that there was /* This must happen before the call to save_expr. */ expr = cp_build_addr_expr (expr, complain); in between the new and old calls to resolves_to_fixed_type_p, and for the uneval case like that cp_build_addr_expr -> unary_complex_lvalue -> build_cplus_new wraps the CALL_EXPR into a TARGET_EXPR already, so the later call was happy. Now, none of fixed_type_p, virtual_access or nonnull values are ever used in the build_base_path uneval path. if (code == PLUS_EXPR && !want_pointer && !has_empty && !uneval && !virtual_access) return build_simple_base_path (expr, binfo); doesn't look at it, if (!want_pointer) { rvalue = !lvalue_p (expr); /* This must happen before the call to save_expr. */ expr = cp_build_addr_expr (expr, complain); } else expr = mark_rvalue_use (expr); neither, nothing soon after it either and very soon we if (uneval) { expr = build_nop (ptr_target_type, expr); goto indout; } and indout: doesn't use it either. So, if only uneval expressions have problems with moving the resolves_to_fixed_type_p call earlier, the following patch fixes it by just not calling that function at all in the uneval case because we will not care about the result anyway. 2026-01-21 Jakub Jelinek <[email protected]> PR c++/123692 * class.cc (build_base_path): Don't call resolves_to_fixed_type_p if uneval. * g++.dg/cpp0x/pr123692.C: New test. Diff: --- gcc/cp/class.cc | 5 ++++- gcc/testsuite/g++.dg/cpp0x/pr123692.C | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index faf42c7979d4..c610236f2601 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -347,7 +347,10 @@ build_base_path (enum tree_code code, || processing_template_decl || in_template_context); - fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull); + if (!uneval) + fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull); + else + fixed_type_p = 0; /* Do we need to look in the vtable for the real offset? */ virtual_access = (v_binfo && fixed_type_p <= 0); diff --git a/gcc/testsuite/g++.dg/cpp0x/pr123692.C b/gcc/testsuite/g++.dg/cpp0x/pr123692.C new file mode 100644 index 000000000000..4d6723c2d76c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr123692.C @@ -0,0 +1,9 @@ +// PR c++/123692 +// { dg-do compile { target c++11 } } + +struct A { A (int); bool operator == (A); }; +struct B : A { using A::A; }; +B operator - (B, B); +extern B c; +extern A d; +decltype (d == c - 1) x;
