See <https://wg21.link/P3533R2>.
.
Created attachment 61688 [details] gcc16-pr120777-wip.patch Started playing with this and made some progess, but 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 () {} 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; return true; } static_assert (qux ()); still doesn't work yet.
Created attachment 61695 [details] gcc16-pr120777-wip.patch Some further progress, the testcase now passes. Except uncommenting the last two lines still results in errors. I think there are 2 problems. One is that at the end of the complete constructor for E we get something like {.D.3067={._vptr.C=&_ZTV1E + 24, .c=47}, .D.3068={._vptr.D=&_ZTV1E + 72, .d=48, .D.2967={.D.2757={._vptr.A=&_ZTV1E + 128}}}, .e=49, .D.3073={.D.2757={._vptr.A=&_ZTV1B + 16, .a=45}, .b=46}} The problem is the D.2967= initialization there, I bet we need it during the construction, but maybe at the end of the complete ctor for a class with virtual bases we should remove all initializers in the CONSTRUCTOR which are beyond DECL_SIZE of the containing FIELD_DECL for bases. And the other are the abcu.C:183:13: error: ‘E{C{((& E::_ZTV1E) + 24), 45}, D{((& E::_ZTV1E) + 72), 45}, B{A{((& E::_ZTV1E) + 128), 43}, 42}}’ is not a constant expression 183 | constexpr E a; | ^ abcu.C:183:13: error: ‘((E*)(& a))->E::E()’ is not a constant expression because it refers to an incompletely initialized variable abcu.C:184:34: error: ‘E{C{((& E::_ZTV1E) + 24), 47}, D{((& E::_ZTV1E) + 72), 48}, 49, B{A{((& E::_ZTV1E) + 128), 45}, 46}}’ is not a constant expression 184 | constexpr E b (45, 46, 47, 48, 49); | ^ abcu.C:184:34: error: ‘((E*)(& b))->E::E(45, 46, 47, 48, 49)’ is not a constant expression because it refers to an incompletely initialized variable errors, perhaps on the other side we want to verify the bits beyond DECL_SIZE of FIELD_DECLs aren't initialized rather than flagging them as incompletely initialized.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:f86ebb00406dafed3b8ebeae6e9e9d613abdf2ec commit r16-1751-gf86ebb00406dafed3b8ebeae6e9e9d613abdf2ec Author: Jakub Jelinek <jakub@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 <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.
Implemented for 16.1+.