On Thu, Jun 26, 2025 at 01:33:08PM +0200, Jakub Jelinek wrote: > I get some regressions (which I didn't get with the earlier patch, but > it isn't obvious by what it has been caused):
It ICEs were caused by the canonicalize_obj_off change and indeed > The ICEs are all in the same spot: > 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); fixes that. The remaining FAILs were about constant evaluation of ctors resulting in copies of construction vtables etc. no longer be needed because all the construction was compile time evaluated. Here is what bootstrapped/regtested on x86_64-linux and i686-linux without any regressions (plus your patch is of course desirable and maybe some incremental change to the CALL_EXPR handling of fixed_type_or_null but I don't know what exactly). Ok for trunk? 2025-06-26 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. --- gcc/gimple-fold.cc.jj 2025-06-26 13:49:44.433654295 +0200 +++ gcc/gimple-fold.cc 2025-06-26 13:51:29.230355538 +0200 @@ -10276,13 +10276,12 @@ gimple_get_virt_method_for_vtable (HOST_ 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 --- gcc/c-family/c-cppbuiltin.cc.jj 2025-06-26 13:49:44.125658112 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2025-06-26 13:51:29.230355538 +0200 @@ -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"); --- gcc/cp/cp-tree.h.jj 2025-06-26 13:49:44.329655584 +0200 +++ gcc/cp/cp-tree.h 2025-06-26 13:51:29.232355513 +0200 @@ -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 */ @@ -8912,7 +8913,7 @@ extern tree fold_non_dependent_init (tr 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); --- gcc/cp/method.cc.jj 2025-06-26 13:49:44.379654964 +0200 +++ gcc/cp/method.cc 2025-06-26 13:51:29.232355513 +0200 @@ -3024,7 +3024,7 @@ synthesized_method_walk (tree ctype, spe /* 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) --- gcc/cp/class.cc.jj 2025-06-26 13:49:44.206657109 +0200 +++ gcc/cp/class.cc 2025-06-26 13:51:29.234355488 +0200 @@ -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 + && (!v_binfo || !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 { /* 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); } } } @@ -10620,7 +10627,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) { @@ -10628,11 +10635,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); } } @@ -10970,6 +10977,17 @@ publicly_uniquely_derived_p (tree parent NULL, tf_none); 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. */ --- gcc/cp/constexpr.cc.jj 2025-06-26 13:49:44.246656613 +0200 +++ gcc/cp/constexpr.cc 2025-06-26 14:28:23.492710503 +0200 @@ -303,17 +303,19 @@ is_valid_constexpr_fn (tree fun, bool co } } } - 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 *wa 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 const 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 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 constexp 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 constexp *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 constexp 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 constexp /* 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 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 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_c 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_c { 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 --- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C.jj 2025-06-26 13:51:29.235355476 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C 2025-06-26 13:51:29.235355476 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C.jj 2025-06-26 13:51:29.235355476 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C 2025-06-26 13:51:29.235355476 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C.jj 2025-06-26 13:51:29.236355463 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C 2025-06-26 13:51:29.236355463 +0200 @@ -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, !, !, !) --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2025-06-26 13:49:44.646651656 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2025-06-26 13:51:29.236355463 +0200 @@ -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 --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj 2021-11-16 23:05:09.602778655 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C 2025-06-26 19:24:14.066088217 +0200 @@ -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 --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C.jj 2025-06-26 13:49:44.703650949 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C 2025-06-26 13:51:29.236355463 +0200 @@ -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 } --- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C.jj 2025-06-26 13:49:44.735650553 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 2025-06-26 13:51:29.236355463 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C.jj 2025-06-26 13:49:44.572652573 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 2025-06-26 13:51:29.236355463 +0200 @@ -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 } } } }; } --- gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C.jj 2025-06-26 13:49:44.612652077 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C 2025-06-26 13:51:29.236355463 +0200 @@ -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 } } }; --- gcc/testsuite/g++.dg/abi/mangle1.C.jj 2023-12-22 12:26:14.843894677 +0100 +++ gcc/testsuite/g++.dg/abi/mangle1.C 2025-06-26 16:02:49.872413187 +0200 @@ -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 { --- gcc/testsuite/g++.dg/abi/mangle81.C.jj 2025-06-26 16:03:18.259060825 +0200 +++ gcc/testsuite/g++.dg/abi/mangle81.C 2025-06-26 16:03:31.931891108 +0200 @@ -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\]" } } --- gcc/testsuite/g++.dg/ipa/ipa-icf-4.C.jj 2021-12-30 15:12:43.295149424 +0100 +++ gcc/testsuite/g++.dg/ipa/ipa-icf-4.C 2025-06-26 16:09:13.490640546 +0200 @@ -5,6 +5,9 @@ namespace { struct A { virtual void foo(void) {} +#if __cpp_constexpr_virtual_inheritance >= 202506L + A() {} +#endif }; struct B: virtual A { Jakub