https://gcc.gnu.org/g:f86ebb00406dafed3b8ebeae6e9e9d613abdf2ec

commit r16-1751-gf86ebb00406dafed3b8ebeae6e9e9d613abdf2ec
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Fri Jun 27 23:39:30 2025 +0200

    c++: Implement C++26 P3533R2 - constexpr virtual inheritance [PR120777]
    
    The following patch implements the C++26
    P3533R2 - constexpr virtual inheritance
    paper.
    The changes include not rejecting it for C++26, tweaking the
    error wording to show that it is valid in C++26, adjusting
    synthesized_method_walk not to make synthetized cdtors non-constexpr
    just because of virtual base classes in C++26 and various tweaks in
    constexpr.cc so that it can deal with the expressions used for
    virtual base member accesses or cdtor calls which need __in_chrg
    and/or __vtt_parm arguments to be passed in some cases implicitly when
    they aren't passed explicitly.  And dynamic_cast constant evaluation
    tweaks so that it handles also expressions with types with virtual bases.
    
    2025-06-27  Jakub Jelinek  <ja...@redhat.com>
    
            PR c++/120777
    gcc/
            * gimple-fold.cc (gimple_get_virt_method_for_vtable): Revert
            2018-09-18 changes.
    gcc/c-family/
            * c-cppbuiltin.cc (c_cpp_builtins): Predefine
            __cpp_constexpr_virtual_inheritance=202506L for C++26.
    gcc/cp/
            * constexpr.cc: Implement C++26 P3533R2 - constexpr virtual
            inheritance.
            (is_valid_constexpr_fn): Don't reject constexpr cdtors in classes
            with virtual bases for C++26, adjust error wording.
            (cxx_bind_parameters_in_call): Add ORIG_FUN argument, add
            values for __in_chrg and __vtt_parm arguments when needed.
            (cxx_eval_dynamic_cast_fn): Adjust function comment, HINT -1
            should be possible.  For C++26 if obj is cast from 
POINTER_PLUS_EXPR,
            attempt to use cxx_fold_indirect_ref to simplify it and if 
successful,
            build ADDR_EXPR of that.
            (cxx_eval_call_expression): Add orig_fun variable, set it to
            fun before looking through clones, pass it to
            cxx_bind_parameters_in_call.
            (reduced_constant_expression_p): Add SZ argument, pass DECL_SIZE
            of FIELD_DECL e.index to recursive calls and don't return false
            if SZ is non-NULL and there are unfilled fields with bit position
            at or above SZ.
            (cxx_fold_indirect_ref_1): Handle reading of vtables using
            ptrdiff_t dynamic type instead of some pointer type.  Set el_sz
            to DECL_SIZE_UNIT value rather than TYPE_SIZE_UNIT of
            DECL_FIELD_IS_BASE fields in classes with virtual bases.
            (cxx_fold_indirect_ref): In canonicalize_obj_off lambda look
            through COMPONENT_REFs with DECL_FIELD_IS_BASE in classes with
            virtual bases and adjust off correspondingly.  Remove assertion that
            off is integer_zerop, pass tree_to_uhwi (off) instead of 0 to the
            cxx_fold_indirect_ref_1 call.
            * cp-tree.h (publicly_virtually_derived_p): Declare.
            (reduced_constant_expression_p): Add another tree argument defaulted
            to NULL_TREE.
            * method.cc (synthesized_method_walk): Don't clear *constexpr_p
            if there are virtual bases for C++26.
            * class.cc (build_base_path): Compute fixed_type_p and
            virtual_access before checks for build_simple_base_path instead of
            after that and conditional cp_build_addr_expr.  Use 
build_simple_path
            if !virtual_access even when v_binfo is non-NULL.
            (layout_virtual_bases): For build_base_field calls use
            access_public_node rather than access_private_node if
            publicly_virtually_derived_p.
            (build_vtbl_initializer): Revert 2018-09-18 and 2018-12-11 changes.
            (publicly_virtually_derived_p): New function.
    gcc/testsuite/
            * g++.dg/cpp26/constexpr-virt-inherit1.C: New test.
            * g++.dg/cpp26/constexpr-virt-inherit2.C: New test.
            * g++.dg/cpp26/constexpr-virt-inherit3.C: New test.
            * g++.dg/cpp26/feat-cxx26.C: Add __cpp_constexpr_virtual_inheritance
            tersts.
            * g++.dg/cpp2a/constexpr-dtor3.C: Don't expect one error for C++26.
            * g++.dg/cpp2a/constexpr-dtor16.C: Don't expect errors for C++26.
            * g++.dg/cpp2a/constexpr-dynamic10.C: Likewise.
            * g++.dg/cpp0x/constexpr-ice21.C: Likewise.
            * g++.dg/cpp0x/constexpr-ice4.C: Likewise.
            * g++.dg/abi/mangle1.C: Guard the test on c++23_down.
            * g++.dg/abi/mangle81.C: New test.
            * g++.dg/ipa/ipa-icf-4.C (A::A): For
            __cpp_constexpr_virtual_inheritance >= 202506L add user provided
            non-constexpr constructor.

Diff:
---
 gcc/c-family/c-cppbuiltin.cc                       |   1 +
 gcc/cp/class.cc                                    |  40 +++--
 gcc/cp/constexpr.cc                                | 146 +++++++++++++---
 gcc/cp/cp-tree.h                                   |   3 +-
 gcc/cp/method.cc                                   |   2 +-
 gcc/gimple-fold.cc                                 |   7 +-
 gcc/testsuite/g++.dg/abi/mangle1.C                 |   4 +-
 gcc/testsuite/g++.dg/abi/mangle81.C                |  29 ++++
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C       |   6 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C        |   2 +-
 .../g++.dg/cpp26/constexpr-virt-inherit1.C         | 189 +++++++++++++++++++++
 .../g++.dg/cpp26/constexpr-virt-inherit2.C         |  18 ++
 .../g++.dg/cpp26/constexpr-virt-inherit3.C         |  91 ++++++++++
 gcc/testsuite/g++.dg/cpp26/feat-cxx26.C            |   6 +
 gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C      |   2 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C       |   2 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C   |   4 +-
 gcc/testsuite/g++.dg/ipa/ipa-icf-4.C               |   3 +
 18 files changed, 506 insertions(+), 49 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 4589ee4a3a62..459fd86bb39d 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1094,6 +1094,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_variadic_friend=202403L");
          cpp_define (pfile, "__cpp_pack_indexing=202311L");
          cpp_define (pfile, "__cpp_pp_embed=202502L");
+         cpp_define (pfile, "__cpp_constexpr_virtual_inheritance=202506L");
        }
       if (flag_concepts && cxx_dialect > cxx14)
        cpp_define (pfile, "__cpp_concepts=202002L");
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6cd6e8f1bfcf..9a41c00788a8 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -347,9 +347,18 @@ build_base_path (enum tree_code code,
                 || processing_template_decl
                 || in_template_context);
 
+  fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
+
+  /* Do we need to look in the vtable for the real offset?  */
+  virtual_access = (v_binfo && fixed_type_p <= 0);
+
   /* For a non-pointer simple base reference, express it as a COMPONENT_REF
      without taking its address (and so causing lambda capture, 91933).  */
-  if (code == PLUS_EXPR && !v_binfo && !want_pointer && !has_empty && !uneval)
+  if (code == PLUS_EXPR
+      && !want_pointer
+      && !has_empty
+      && !uneval
+      && !virtual_access)
     return build_simple_base_path (expr, binfo);
 
   if (!want_pointer)
@@ -362,7 +371,6 @@ build_base_path (enum tree_code code,
     expr = mark_rvalue_use (expr);
 
   offset = BINFO_OFFSET (binfo);
-  fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
   target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo);
   /* TARGET_TYPE has been extracted from BINFO, and, is therefore always
      cv-unqualified.  Extract the cv-qualifiers from EXPR so that the
@@ -371,9 +379,6 @@ build_base_path (enum tree_code code,
     (target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr))));
   ptr_target_type = build_pointer_type (target_type);
 
-  /* Do we need to look in the vtable for the real offset?  */
-  virtual_access = (v_binfo && fixed_type_p <= 0);
-
   /* Don't bother with the calculations inside sizeof; they'll ICE if the
      source type is incomplete and the pointer value doesn't matter.  In a
      template (even in instantiate_non_dependent_expr), we don't have vtables
@@ -6754,9 +6759,11 @@ layout_virtual_bases (record_layout_info rli, splay_tree 
offsets)
        {
          /* This virtual base is not a primary base of any class in the
             hierarchy, so we have to add space for it.  */
-         next_field = build_base_field (rli, vbase,
-                                        access_private_node,
-                                        offsets, next_field);
+         tree access = access_private_node;
+         if (publicly_virtually_derived_p (BINFO_TYPE (vbase), t))
+           access = access_public_node;
+         next_field = build_base_field (rli, vbase, access, offsets,
+                                        next_field);
        }
     }
 }
@@ -10629,7 +10636,7 @@ build_vtbl_initializer (tree binfo,
          int i;
          if (init == size_zero_node)
            for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
-             CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
+             CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
          else
            for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
              {
@@ -10637,11 +10644,11 @@ build_vtbl_initializer (tree binfo,
                                     fn, build_int_cst (NULL_TREE, i));
                TREE_CONSTANT (fdesc) = 1;
 
-               CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc);
+               CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc);
              }
        }
       else
-       CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init);
+       CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init);
     }
 }
 
@@ -10980,6 +10987,17 @@ publicly_uniquely_derived_p (tree parent, tree type)
   return base && base != error_mark_node;
 }
 
+/* TRUE iff TYPE is publicly & virtually derived from PARENT.  */
+
+bool
+publicly_virtually_derived_p (tree parent, tree type)
+{
+  tree base = lookup_base (type, parent,
+                          ba_ignore_scope | ba_check | ba_require_virtual,
+                          NULL, tf_none);
+  return base && base != error_mark_node;
+}
+
 /* CTX1 and CTX2 are declaration contexts.  Return the innermost common
    class between them, if any.  */
 
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 1dd8371de251..704d936f2ec3 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -303,17 +303,19 @@ is_valid_constexpr_fn (tree fun, bool complain)
            }
        }
     }
-  else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)))
+  else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)) && cxx_dialect < cxx26)
     {
       ret = false;
       if (complain)
        {
          if (DECL_CONSTRUCTOR_P (fun))
            error ("%<constexpr%> constructor in %q#T that has "
-                  "virtual base classes", DECL_CONTEXT (fun));
+                  "virtual base classes only available with "
+                  "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
          else
            error ("%<constexpr%> destructor in %q#T that has "
-                  "virtual base classes", DECL_CONTEXT (fun));
+                  "virtual base classes only available with "
+                  "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun));
        }
     }
 
@@ -1879,20 +1881,28 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, 
void *data)
 
 static tree
 cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
-                            bool *non_constant_p, bool *overflow_p,
-                            bool *non_constant_args)
+                            tree orig_fun, bool *non_constant_p,
+                            bool *overflow_p, bool *non_constant_args)
 {
-  const int nargs = call_expr_nargs (t);
+  int nargs = call_expr_nargs (t);
   tree parms = DECL_ARGUMENTS (fun);
-  int i;
+  int i, j = 0;
+  if (DECL_HAS_IN_CHARGE_PARM_P (fun) && fun != orig_fun)
+    ++nargs;
+  if (DECL_HAS_VTT_PARM_P (fun)
+      && fun != orig_fun
+      && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+         || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+    ++nargs;
   /* We don't record ellipsis args below.  */
   int nparms = list_length (parms);
   int nbinds = nargs < nparms ? nargs : nparms;
   tree binds = make_tree_vec (nbinds);
 
   /* The call is not a constant expression if it involves the cdtor for a type
-     with virtual bases.  */
-  if (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun))
+     with virtual bases before C++26.  */
+  if (cxx_dialect < cxx26
+      && (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun)))
     {
       if (!ctx->quiet)
        {
@@ -1910,7 +1920,30 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, 
tree t, tree fun,
       tree type = parms ? TREE_TYPE (parms) : void_type_node;
       if (parms && DECL_BY_REFERENCE (parms))
        type = TREE_TYPE (type);
-      x = get_nth_callarg (t, i);
+      if (i == 1
+         && j == 0
+         && DECL_HAS_IN_CHARGE_PARM_P (fun)
+         && orig_fun != fun)
+       {
+         if (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+             || DECL_COMPLETE_DESTRUCTOR_P (orig_fun))
+           x = boolean_true_node;
+         else
+           x = boolean_false_node;
+         j = -1;
+       }
+      else if (i == 2
+              && j == -1
+              && DECL_HAS_VTT_PARM_P (fun)
+              && orig_fun != fun
+              && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun)
+                  || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)))
+       {
+         x = build_zero_cst (type);
+         j = -2;
+       }
+      else
+       x = get_nth_callarg (t, i + j);
       /* For member function, the first argument is a pointer to the implied
          object.  For a constructor, it might still be a dummy object, in
         which case we get the real argument from ctx.  */
@@ -2529,10 +2562,7 @@ get_component_with_type (tree path, tree type, tree stop)
        dst_ptr + src2dst == src_ptr
    -1: unspecified relationship
    -2: src_type is not a public base of dst_type
-   -3: src_type is a multiple public non-virtual base of dst_type
-
-  Since literal types can't have virtual bases, we only expect hint >=0,
-  -2, or -3.  */
+   -3: src_type is a multiple public non-virtual base of dst_type  */
 
 static tree
 cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
@@ -2569,6 +2599,22 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree 
call,
   if (*non_constant_p)
     return call;
 
+  /* For dynamic_cast from classes with virtual bases we can get something
+     like (virt_base *)(&d + 16) as OBJ.  Try to convert that into
+     d.D.1234 using cxx_fold_indirect_ref.  */
+  if (cxx_dialect >= cxx26 && CONVERT_EXPR_P (obj))
+    {
+      tree objo = obj;
+      STRIP_NOPS (objo);
+      if (TREE_CODE (objo) == POINTER_PLUS_EXPR)
+       {
+         objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)),
+                                       obj);
+         if (objo)
+           obj = build_fold_addr_expr (objo);
+       }
+    }
+
   /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
      but when HINT is > 0, it can also be something like
      &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET.  */
@@ -2916,6 +2962,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree 
t,
       *non_constant_p = true;
       return t;
     }
+  tree orig_fun = fun;
   if (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
 
@@ -3110,7 +3157,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree 
t,
   bool non_constant_args = false;
   constexpr_call new_call;
   new_call.bindings
-    = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p,
+    = cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p,
                                   overflow_p, &non_constant_args);
 
   /* We build up the bindings list before we know whether we already have this
@@ -3514,11 +3561,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, 
tree t,
 
 /* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
    initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
-   cleared.
+   cleared.  If called recursively on a FIELD_DECL's CONSTRUCTOR, SZ
+   is DECL_SIZE of the FIELD_DECL, otherwise NULL.
    FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
 
 bool
-reduced_constant_expression_p (tree t)
+reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */)
 {
   if (t == NULL_TREE)
     return false;
@@ -3586,7 +3634,12 @@ reduced_constant_expression_p (tree t)
        {
          /* If VAL is null, we're in the middle of initializing this
             element.  */
-         if (!reduced_constant_expression_p (e.value))
+         if (!reduced_constant_expression_p (e.value,
+                                             (e.index
+                                              && (TREE_CODE (e.index)
+                                                  == FIELD_DECL))
+                                             ? DECL_SIZE (e.index)
+                                             : NULL_TREE))
            return false;
          /* We want to remove initializers for empty fields in a struct to
             avoid confusing output_constructor.  */
@@ -3606,7 +3659,16 @@ reduced_constant_expression_p (tree t)
       /* There could be a non-empty field at the end.  */
       for (; field; field = next_subobject_field (DECL_CHAIN (field)))
        if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
-         return false;
+         {
+           /* Ignore FIELD_DECLs with bit positions beyond DECL_SIZE of
+              the parent FIELD_DECL (if any) for classes with virtual
+              bases.  */
+           if (cxx_dialect >= cxx26
+               && sz
+               && tree_int_cst_le (sz, bit_position (field)))
+             break;
+           return false;
+         }
 ok:
       if (CONSTRUCTOR_NO_CLEARING (t))
        /* All the fields are initialized.  */
@@ -5868,6 +5930,20 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, 
location_t loc, tree type,
   unsigned HOST_WIDE_INT const_nunits;
   if (off == 0 && similar_type_p (optype, type))
     return op;
+  else if (cxx_dialect >= cxx26
+          && VAR_P (op)
+          && DECL_VTABLE_OR_VTT_P (op)
+          && same_type_ignoring_top_level_qualifiers_p (type,
+                                                        ptrdiff_type_node)
+          && POINTER_TYPE_P (strip_array_types (optype)))
+    {
+      /* We often read some virtual table elements using ptrdiff_t rather
+        than pointer type.  */
+      if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc,
+                                             strip_array_types (optype),
+                                             op, off, empty_base))
+       return fold_convert (type, ret);
+    }
   else if (TREE_CODE (optype) == COMPLEX_TYPE
           && similar_type_p (type, TREE_TYPE (optype)))
     {
@@ -5961,8 +6037,13 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, 
location_t loc, tree type,
            if (!tree_fits_uhwi_p (pos))
              continue;
            unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
-           unsigned HOST_WIDE_INT el_sz
-             = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
+           unsigned HOST_WIDE_INT el_sz;
+           if (DECL_FIELD_IS_BASE (field)
+               && CLASS_TYPE_P (optype)
+               && CLASSTYPE_VBASECLASSES (optype))
+             el_sz = tree_to_uhwi (DECL_SIZE_UNIT (field));
+           else
+             el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
            if (upos <= off && off < upos + el_sz)
              {
                tree cop = build3 (COMPONENT_REF, TREE_TYPE (field),
@@ -6013,6 +6094,25 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, 
location_t loc, tree type,
      offset positive, so that cxx_fold_indirect_ref_1 can identify
      more folding opportunities.  */
   auto canonicalize_obj_off = [] (tree& obj, tree& off) {
+    if (cxx_dialect >= cxx26)
+      {
+       /* For C++26, we need to fold *(B *)(&x.D.1234 + 32) used
+          to access virtual base members.  */
+       tree nobj = obj;
+       while (TREE_CODE (nobj) == COMPONENT_REF
+              && DECL_FIELD_IS_BASE (TREE_OPERAND (nobj, 1)))
+         nobj = TREE_OPERAND (nobj, 0);
+       if (nobj != obj
+           && CLASS_TYPE_P (TREE_TYPE (nobj))
+           && CLASSTYPE_VBASECLASSES (TREE_TYPE (nobj)))
+         while (obj != nobj)
+           {
+             tree field = TREE_OPERAND (obj, 1);
+             tree pos = byte_position (field);
+             off = int_const_binop (PLUS_EXPR, off, pos);
+             obj = TREE_OPERAND (obj, 0);
+           }
+      }
     while (TREE_CODE (obj) == COMPONENT_REF
           /* We need to preserve union member accesses so that we can
              later properly diagnose accessing the wrong member.  */
@@ -6051,8 +6151,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, 
location_t loc, tree type,
        {
          tree off = integer_zero_node;
          canonicalize_obj_off (op, off);
-         gcc_assert (integer_zerop (off));
-         return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
+         return cxx_fold_indirect_ref_1 (ctx, loc, type, op,
+                                         tree_to_uhwi (off), empty_base);
        }
     }
   else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8f81fe034e96..1b893e23543d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7104,6 +7104,7 @@ extern void adjust_clone_args                     (tree);
 extern void deduce_noexcept_on_destructor       (tree);
 extern bool uniquely_derived_from_p             (tree, tree);
 extern bool publicly_uniquely_derived_p         (tree, tree);
+extern bool publicly_virtually_derived_p        (tree, tree);
 extern tree common_enclosing_class             (tree, tree);
 
 /* in cvt.cc */
@@ -8914,7 +8915,7 @@ extern tree fold_non_dependent_init               (tree,
                                                 bool = false, tree = 
NULL_TREE);
 extern tree fold_simple                                (tree);
 extern tree fold_to_constant                   (tree);
-extern bool reduced_constant_expression_p       (tree);
+extern bool reduced_constant_expression_p       (tree, tree = NULL_TREE);
 extern bool is_instantiation_of_constexpr       (tree);
 extern bool var_in_constexpr_fn                 (tree);
 extern bool var_in_maybe_constexpr_fn           (tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 1b9a1c8c9b1d..a4089c53c67b 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3024,7 +3024,7 @@ synthesized_method_walk (tree ctype, 
special_function_kind sfk, bool const_p,
     /* Vbase cdtors are not relevant.  */;
   else
     {
-      if (constexpr_p)
+      if (constexpr_p && cxx_dialect < cxx26)
        *constexpr_p = false;
 
       FOR_EACH_VEC_ELT (*vbases, i, base_binfo)
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 729080ad6e5b..2507e23f2d7c 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -10276,13 +10276,12 @@ gimple_get_virt_method_for_vtable (HOST_WIDE_INT 
token,
   access_index = offset / BITS_PER_UNIT / elt_size;
   gcc_checking_assert (offset % (elt_size * BITS_PER_UNIT) == 0);
 
-  /* The C++ FE can now produce indexed fields, and we check if the indexes
-     match.  */
+  /* This code makes an assumption that there are no
+     indexed fileds produced by C++ FE, so we can directly index the array.  */
   if (access_index < CONSTRUCTOR_NELTS (init))
     {
       fn = CONSTRUCTOR_ELT (init, access_index)->value;
-      tree idx = CONSTRUCTOR_ELT (init, access_index)->index;
-      gcc_checking_assert (!idx || tree_to_uhwi (idx) == access_index);
+      gcc_checking_assert (!CONSTRUCTOR_ELT (init, access_index)->index);
       STRIP_NOPS (fn);
     }
   else
diff --git a/gcc/testsuite/g++.dg/abi/mangle1.C 
b/gcc/testsuite/g++.dg/abi/mangle1.C
index 40bb9a9e2fc6..d01e574cb9d3 100644
--- a/gcc/testsuite/g++.dg/abi/mangle1.C
+++ b/gcc/testsuite/g++.dg/abi/mangle1.C
@@ -1,6 +1,8 @@
 // Test for mangling of simple testcase involving construction vtables.
 
-// { dg-do compile }
+// For C++26, the ctor is constant evaluated and so construction vtables
+// aren't needed.
+// { dg-do compile { target c++23_down } }
 // { dg-options "-fno-inline -fabi-compat-version=0" }
 
 struct A {
diff --git a/gcc/testsuite/g++.dg/abi/mangle81.C 
b/gcc/testsuite/g++.dg/abi/mangle81.C
new file mode 100644
index 000000000000..1046a406fe19
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/mangle81.C
@@ -0,0 +1,29 @@
+// Test for mangling of simple testcase involving construction vtables.
+
+// { dg-do compile }
+// { dg-options "-fno-inline -fabi-compat-version=0" }
+
+struct A {
+  virtual void f () { }
+  A () {}
+};
+
+struct B: public virtual A { };
+struct C: public B { };
+
+C c;
+
+// { dg-final { scan-assembler "\n_?_ZN1A1fEv\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1AC2Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1BC2Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZN1CC1Ev\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTC1C0_1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTI1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1B\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTS1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTT1C\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTV1A\[: \t\n\]" } }
+// { dg-final { scan-assembler "\n_?_ZTV1C\[: \t\n\]" } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
index dcc404489bec..d4e03b803043 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
@@ -5,13 +5,13 @@ struct NoMut1 { int a, b; };
 struct NoMut3 : virtual NoMut1 {
   constexpr NoMut3(int a, int b)
     : NoMut1{a, b}
-  {} // { dg-error "virtual base" }
+  {} // { dg-error "virtual base" "" { target c++23_down } }
 };
 void mutable_subobjects() {
-  constexpr NoMut3 nm3 = {1, 2}; // { dg-error "call to non" }
+  constexpr NoMut3 nm3 = {1, 2}; // { dg-error "call to non" "" { target 
c++23_down } }
   struct A {
     void f() {
-      static_assert(nm3.a == 1, ""); // { dg-error "local variable" }
+      static_assert(nm3.a == 1, ""); // { dg-error "local variable" "" { 
target c++23_down } }
     }
   };
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C
index a083c85e752b..5688848e2253 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C
@@ -5,5 +5,5 @@ struct A {};
 
 struct B : virtual A
 {
-  constexpr B() { } // { dg-error "has virtual base classes" }
+  constexpr B() { } // { dg-error "has virtual base classes" "" { target 
c++23_down } }
 };
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C 
b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C
new file mode 100644
index 000000000000..a71a66ff99df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C
@@ -0,0 +1,189 @@
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+struct A {
+  int a;
+  constexpr virtual int foo () { return a; };
+  constexpr A () : a (42) {}
+  constexpr A (int x) : a (x) {}
+  constexpr virtual ~A () { if (a < 42 || a > 62) asm (""); }
+};
+struct B : public A {
+  int b;
+  constexpr virtual int foo () { return a + b; }
+  constexpr B () : A (43), b (42) {}
+  constexpr B (int x, int y) : A (x), b (y) {}
+  constexpr virtual ~B () { if (b < 42 || b > 62) asm (""); }
+};
+struct C : virtual public B {
+  int c;
+  constexpr C () : B (44, 43), c (45) {}
+  constexpr C (int x) : B (44, 43), c (x) {}
+  constexpr virtual int bar () { return a + b + c; }
+  constexpr virtual ~C () { if (c < 42 || c > 62) asm (""); }
+};
+struct D : virtual public B {
+  int d;
+  constexpr D () : B (44, 43), d (45) {}
+  constexpr D (int x) : B (44, 43), d (x) {}
+  constexpr virtual int baz () { return a + b + d; }
+  constexpr virtual ~D () { if (d < 42 || d > 62) asm (""); }
+};
+struct E : public C, D {
+  int e;
+  constexpr E () : B (), C (), D (), e (58) {}
+  constexpr E (int x, int y, int z, int w, int v) : B (x, y), C (z), D (w), e 
(v) {}
+  constexpr virtual ~E () { if (e < 42 || e > 62) asm (""); }
+};
+
+constexpr bool
+qux ()
+{
+  E f (45, 46, 47, 48, 49);
+  f.a++;
+  f.b++;
+  f.c++;
+  f.d++;
+  f.e++;
+  C *c = static_cast <C *> (&f);
+  D *d = static_cast <D *> (&f);
+  B *b = static_cast <B *> (&f);
+  A *a = static_cast <A *> (&f);
+  if (f.foo () != 46 + 47)
+    return false;
+  if (f.bar () != 46 + 47 + 48)
+    return false;
+  if (f.baz () != 46 + 47 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  c->a += 5;
+  d->d += 6;
+  d->a += 7;
+  if (c->foo () != 60 + 50)
+    return false;
+  c->b -= 3;
+  if (d->foo () != 60 + 47)
+    return false;
+  if (f.a != 60 || f.b != 47 || f.c != 52 || f.d != 55 || f.e != 50)
+    return false;
+  C g (48);
+  c = static_cast <C *> (&g);
+  b = static_cast <B *> (&g);
+  a = static_cast <A *> (&g);
+  g.a++;
+  g.b++;
+  g.c++;
+  if (g.foo () != 45 + 44)
+    return false;
+  if (g.bar () != 45 + 44 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  if (c->foo () != 47 + 47)
+    return false;
+  if (g.a != 47 || g.b != 47 || g.c != 53)
+    return false;
+  D h (49);
+  d = static_cast <D *> (&h);
+  b = static_cast <B *> (&h);
+  a = static_cast <A *> (&h);
+  h.a++;
+  h.b++;
+  h.d++;
+  if (h.foo () != 45 + 44)
+    return false;
+  if (h.baz () != 45 + 44 + 50)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  d->d += 4;
+  if (d->foo () != 47 + 47)
+    return false;
+  if (h.a != 47 || h.b != 47 || h.d != 54)
+    return false;
+  return true;
+}
+
+constexpr bool
+corge ()
+{
+  E *f = new E (45, 46, 47, 48, 49);
+  f->a++;
+  f->b++;
+  f->c++;
+  f->d++;
+  f->e++;
+  C *c = static_cast <C *> (f);
+  D *d = static_cast <D *> (f);
+  B *b = static_cast <B *> (f);
+  A *a = static_cast <A *> (f);
+  if (f->foo () != 46 + 47)
+    return false;
+  if (f->bar () != 46 + 47 + 48)
+    return false;
+  if (f->baz () != 46 + 47 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  c->a += 5;
+  d->d += 6;
+  d->a += 7;
+  if (c->foo () != 60 + 50)
+    return false;
+  c->b -= 3;
+  if (d->foo () != 60 + 47)
+    return false;
+  if (f->a != 60 || f->b != 47 || f->c != 52 || f->d != 55 || f->e != 50)
+    return false;
+  C *g = new C (48);
+  c = static_cast <C *> (g);
+  b = static_cast <B *> (g);
+  a = static_cast <A *> (g);
+  g->a++;
+  g->b++;
+  g->c++;
+  if (g->foo () != 45 + 44)
+    return false;
+  if (g->bar () != 45 + 44 + 49)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  c->c += 4;
+  if (c->foo () != 47 + 47)
+    return false;
+  if (g->a != 47 || g->b != 47 || g->c != 53)
+    return false;
+  D *h = new D (49);
+  d = static_cast <D *> (h);
+  b = static_cast <B *> (h);
+  a = static_cast <A *> (h);
+  h->a++;
+  h->b++;
+  h->d++;
+  if (h->foo () != 45 + 44)
+    return false;
+  if (h->baz () != 45 + 44 + 50)
+    return false;
+  a->a += 2;
+  b->b += 3;
+  d->d += 4;
+  if (d->foo () != 47 + 47)
+    return false;
+  if (h->a != 47 || h->b != 47 || h->d != 54)
+    return false;
+  delete h;
+  delete g;
+  delete f;
+  return true;
+}
+
+static_assert (qux ());
+static_assert (corge ());
+constexpr E a;
+constexpr E b (45, 46, 47, 48, 49);
+constexpr C c;
+constexpr C d (50);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C 
b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C
new file mode 100644
index 000000000000..3e4702da3dff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C
@@ -0,0 +1,18 @@
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+struct A { int a; };
+struct B { int b; };
+struct C : virtual public A, B { int c; };
+
+constexpr C
+foo ()
+{
+  C c;
+  c.a = 1;
+  c.b = 2;
+  c.c = 3;
+  return c;
+}
+
+static_assert (foo ().a == 1 && foo ().b == 2 && foo ().c == 3);
diff --git a/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C 
b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C
new file mode 100644
index 000000000000..d23bf896964e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C
@@ -0,0 +1,91 @@
+// C++26 P3533R2 - constexpr virtual inheritance
+// { dg-do compile { target c++26 } }
+
+#define M(N, P1, P2, P3, P4, P5, P6, N1, N2, N3) \
+struct S##N {                                                          \
+  int a, b;                                                            \
+  constexpr S##N () : a (0), b (0) {}                                  \
+  constexpr virtual int bar (int) { return 0; }                                
\
+};                                                                     \
+struct T##N : virtual P1 S##N {                                                
\
+  int c, d;                                                            \
+  constexpr T##N () : c (0), d (0) {}                                  \
+};                                                                     \
+struct U##N : virtual P2 S##N, virtual P3 T##N {                       \
+  int e;                                                               \
+  constexpr U##N () : e (0) {}                                         \
+};                                                                     \
+struct V##N : virtual P4 S##N, virtual P5 T##N, virtual P6 U##N {      \
+  int f;                                                               \
+  constexpr V##N () : f (0) {}                                         \
+  constexpr const S##N *foo () const { return (const S##N *)this; }    \
+};                                                                     \
+constexpr V##N v##N;                                                   \
+static_assert (N1 !!dynamic_cast<const V##N *> (v##N.foo ()));         \
+static_assert (N2 !!dynamic_cast<const T##N *> (v##N.foo ()));         \
+static_assert (N3 !!dynamic_cast<const U##N *> (v##N.foo ()));
+
+M(0, public, public, public, public, public, public, , , )
+M(1, private, public, public, public, public, public, , , )
+M(2, public, private, public, public, public, public, , , )
+M(3, private, private, public, public, public, public, , , )
+M(4, public, public, private, public, public, public, , , )
+M(5, private, public, private, public, public, public, , , )
+M(6, public, private, private, public, public, public, , , )
+M(7, private, private, private, public, public, public, , , )
+M(8, public, public, public, private, public, public, , , )
+M(9, private, public, public, private, public, public, , , )
+M(10, public, private, public, private, public, public, , , )
+M(11, private, private, public, private, public, public, !, !, !)
+M(12, public, public, private, private, public, public, , , )
+M(13, private, public, private, private, public, public, , , )
+M(14, public, private, private, private, public, public, , , )
+M(15, private, private, private, private, public, public, !, !, !)
+M(16, public, public, public, public, private, public, , , )
+M(17, private, public, public, public, private, public, , , )
+M(18, public, private, public, public, private, public, , , )
+M(19, private, private, public, public, private, public, , , )
+M(20, public, public, private, public, private, public, , !, )
+M(21, private, public, private, public, private, public, , !, )
+M(22, public, private, private, public, private, public, , !, )
+M(23, private, private, private, public, private, public, , !, )
+M(24, public, public, public, private, private, public, , , )
+M(25, private, public, public, private, private, public, , , )
+M(26, public, private, public, private, private, public, , , )
+M(27, private, private, public, private, private, public, !, !, !)
+M(28, public, public, private, private, private, public, , !, )
+M(29, private, public, private, private, private, public, , !, )
+M(30, public, private, private, private, private, public, !, !, !)
+M(31, private, private, private, private, private, public, !, !, !)
+M(32, public, public, public, public, public, private, , , !)
+M(33, private, public, public, public, public, private, , , !)
+M(34, public, private, public, public, public, private, , , !)
+M(35, private, private, public, public, public, private, , , !)
+M(36, public, public, private, public, public, private, , , !)
+M(37, private, public, private, public, public, private, , , !)
+M(38, public, private, private, public, public, private, , , !)
+M(39, private, private, private, public, public, private, , , !)
+M(40, public, public, public, private, public, private, , , !)
+M(41, private, public, public, private, public, private, !, !, !)
+M(42, public, private, public, private, public, private, , , !)
+M(43, private, private, public, private, public, private, !, !, !)
+M(44, public, public, private, private, public, private, , , !)
+M(45, private, public, private, private, public, private, !, !, !)
+M(46, public, private, private, private, public, private, , , !)
+M(47, private, private, private, private, public, private, !, !, !)
+M(48, public, public, public, public, private, private, , !, !)
+M(49, private, public, public, public, private, private, , !, !)
+M(50, public, private, public, public, private, private, , !, !)
+M(51, private, private, public, public, private, private, , !, !)
+M(52, public, public, private, public, private, private, , !, !)
+M(53, private, public, private, public, private, private, , !, !)
+M(54, public, private, private, public, private, private, , !, !)
+M(55, private, private, private, public, private, private, , !, !)
+M(56, public, public, public, private, private, private, !, !, !)
+M(57, private, public, public, private, private, private, !, !, !)
+M(58, public, private, public, private, private, private, !, !, !)
+M(59, private, private, public, private, private, private, !, !, !)
+M(60, public, public, private, private, private, private, !, !, !)
+M(61, private, public, private, private, private, private, !, !, !)
+M(62, public, private, private, private, private, private, !, !, !)
+M(63, private, private, private, private, private, private, !, !, !)
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 
b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 66e2d85ece6b..e4ffc357fcea 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -634,3 +634,9 @@
 #elif __cpp_pp_embed != 202502
 #  error "__cpp_pp_embed != 202502"
 #endif
+
+#ifndef __cpp_constexpr_virtual_inheritance
+# error "__cpp_constexpr_virtual_inheritance"
+#elif __cpp_constexpr_virtual_inheritance != 202506
+#  error "__cpp_constexpr_virtual_inheritance != 202506"
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C
index 99d130705713..90d973fe958f 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C
@@ -3,5 +3,5 @@
 
 struct A { virtual ~A (); };
 struct B : virtual A { constexpr ~B () {} };
-// { dg-error "'constexpr' destructor in 'struct B' that has virtual base 
classes" "" { target c++20 } .-1 }
+// { dg-error "'constexpr' destructor in 'struct B' that has virtual base 
classes" "" { target { c++20 && c++23_down } } .-1 }
 // { dg-error "'constexpr' destructors only available with" "" { target 
c++17_down } .-2 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C
index a68a6b4af8bc..b14bacea7c34 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C
@@ -23,7 +23,7 @@ struct U : public S
 struct V : virtual public S
 {
   V () : v (0) {}
-  constexpr ~V () = default;   // { dg-error "explicitly defaulted function 
'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit 
declaration is not 'constexpr'" }
+  constexpr ~V () = default;   // { dg-error "explicitly defaulted function 
'constexpr V::~V\\(\\)' cannot be declared 'constexpr' because the implicit 
declaration is not 'constexpr'" "" { target c++23_down } }
   int v;
 };
 struct W0
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
index e543ce46b518..6d840f092465 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
@@ -5,8 +5,8 @@
 
 struct C { virtual void a(); };
 struct B { virtual void b(); };
-struct A : virtual B, C { virtual void c(); }; // { dg-error "virtual base 
classes" }
+struct A : virtual B, C { virtual void c(); }; // { dg-error "virtual base 
classes" "" { target c++23_down } }
 
-constexpr A a; // { dg-error "call" }
+constexpr A a; // { dg-error "call" "" { target c++23_down } }
 
 constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
diff --git a/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C 
b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
index b4e1958284c2..723285d1ae94 100644
--- a/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
+++ b/gcc/testsuite/g++.dg/ipa/ipa-icf-4.C
@@ -5,6 +5,9 @@ namespace {
 struct A
 {
   virtual void foo(void) {}
+#if __cpp_constexpr_virtual_inheritance >= 202506L
+  A() {}
+#endif
 };
 struct B: virtual A
 {

Reply via email to