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;

Reply via email to