]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement C++26 P3533R2 - constexpr virtual inheritance [PR120777]
authorJakub Jelinek <jakub@redhat.com>
Fri, 27 Jun 2025 21:39:30 +0000 (23:39 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 27 Jun 2025 21:39:30 +0000 (23:39 +0200)
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  <jakub@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.

18 files changed:
gcc/c-family/c-cppbuiltin.cc
gcc/cp/class.cc
gcc/cp/constexpr.cc
gcc/cp/cp-tree.h
gcc/cp/method.cc
gcc/gimple-fold.cc
gcc/testsuite/g++.dg/abi/mangle1.C
gcc/testsuite/g++.dg/abi/mangle81.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C
gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
gcc/testsuite/g++.dg/ipa/ipa-icf-4.C

index 4589ee4a3a62eb8f5b95544008da64cca1d16702..459fd86bb39d8a5e2adcebb6c26b0ce2ea9ae441 100644 (file)
@@ -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");
index 6cd6e8f1bfcf23b6a37758deb2a0c18b85530b29..9a41c00788a83743a11ac149145ddd889b3d289a 100644 (file)
@@ -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.  */
 
index 1dd8371de2514d1f177c324872bd12d8a2fa0e70..704d936f2ec318eeff8b93943a90e4e733fdadb3 100644 (file)
@@ -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
index 8f81fe034e9649f721538d73a287d5189a7e7be2..1b893e23543dd060bec82b37611e75843deec6be 100644 (file)
@@ -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);
index 1b9a1c8c9b1dae66975358b99b292f9336b62ffd..a4089c53c67bcaa1506673cb7f31b32611338472 100644 (file)
@@ -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)
index 729080ad6e5b4e53131b897edb20ec40ea092a71..2507e23f2d7cbde42beb3b0c5079e716d30790df 100644 (file)
@@ -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
index 40bb9a9e2fc6c6e5665fbd205a2eb5dafff3d430..d01e574cb9d30c0b1ab181c8ad741bd5563ee71f 100644 (file)
@@ -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 (file)
index 0000000..1046a40
--- /dev/null
@@ -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\]" } }
index dcc404489bec9a7fe801bb64c5653a0916ce2fb1..d4e03b80304330dfee4f0692da2a54cbc7b91055 100644 (file)
@@ -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 } }
     }
   };
 }
index a083c85e752b667f1b1efdcbb4836fa9062ae594..5688848e2253050f697f3889092d862278474b6d 100644 (file)
@@ -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 (file)
index 0000000..a71a66f
--- /dev/null
@@ -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 (file)
index 0000000..3e4702d
--- /dev/null
@@ -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 (file)
index 0000000..d23bf89
--- /dev/null
@@ -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, !, !, !)
index 66e2d85ece6bde932a8cd570e9ca2d4140a027b1..e4ffc357fcead09f85c4212bf6cc8b48f934a9c0 100644 (file)
 #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
index 99d1307057137c7e3a1d04b37c95421833e3fca7..90d973fe958f4cdbfbca11ed24be23d8ca769a85 100644 (file)
@@ -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 }
index a68a6b4af8bc517e7dbc3444509109e01b869f02..b14bacea7c34b01237c59bafac497a4c64e8a310 100644 (file)
@@ -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
index e543ce46b5182d303a8449bc8d121369d788115c..6d840f09246544e2935c6a159d496452d0afabe1 100644 (file)
@@ -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);
index b4e1958284c26143ea986dae0df2ae276fdf24a8..723285d1ae94316c338840cb3a09b643a69946af 100644 (file)
@@ -5,6 +5,9 @@ namespace {
 struct A
 {
   virtual void foo(void) {}
+#if __cpp_constexpr_virtual_inheritance >= 202506L
+  A() {}
+#endif
 };
 struct B: virtual A
 {