On Wed, Jun 25, 2025 at 04:29:41PM -0400, Jason Merrill wrote: > > (and > > whether perhaps cp_build_addr_expr isn't undesirable for that case, because > > that can make vars odr-used etc.; are are odr uses in unevaluated context > > also supposed to make vars odr-used?). > > That's fine, mark_used handles not actually odr-using things in unevaluated > context.
Thanks for the patch. Here is an updated patch, interdiff from the last posted patch attached (except for testsuite changes). Had to add some tweak in the dynamic_cast handling because the code wasn't expecting obj like (struct S0 *)(&v0 + 16) which is only folded into v0.D.1234 if dereferenced and the code expects obj of the &v0.D.1234 form. Also had to revert Marek's patch to add indexes to some vtable CONSTRUCTORs, it added them only to some CONSTRUCTOR_ELTs and not to others; at that time find_array_ctor_elt wasn't able to deal with index-less CONSTRUCTOR_ELTs, now it can deal with them but only if the CONSTRUCTOR is consistent, either all indexes or none or at least first part without indexes, then with indexes up to the end. Now the vtables consistently don't have any indexes, which is something the code handles well, it can do just direct access and for flag_checking it verifies if last elt has no index, then none of them have and there is no RAW_DATA_CST. I get some regressions (which I didn't get with the earlier patch, but it isn't obvious by what it has been caused): +FAIL: g++.dg/abi/mangle1.C -std=gnu++26 scan-assembler \\n_?_ZN1AC2Ev[: \\t\\n] +FAIL: g++.dg/abi/mangle1.C -std=gnu++26 scan-assembler \\n_?_ZN1BC2Ev[: \\t\\n] +FAIL: g++.dg/abi/mangle1.C -std=gnu++26 scan-assembler \\n_?_ZN1CC1Ev[: \\t\\n] +FAIL: g++.dg/abi/mangle1.C -std=gnu++26 scan-assembler \\n_?_ZTV1A[: \\t\\n] +FAIL: g++.dg/abi/vbase15.C -std=c++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.dg/abi/vbase15.C -std=c++26 (test for excess errors) +UNRESOLVED: g++.dg/abi/vbase15.C -std=c++26 compilation failed to produce executable +FAIL: g++.dg/abi/vbase8-10.C -std=gnu++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.dg/abi/vbase8-10.C -std=gnu++26 (test for excess errors) +UNRESOLVED: g++.dg/abi/vbase8-10.C -std=gnu++26 compilation failed to produce executable +FAIL: g++.dg/abi/vbase8-21.C -std=gnu++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.dg/abi/vbase8-21.C -std=gnu++26 (test for excess errors) +UNRESOLVED: g++.dg/abi/vbase8-21.C -std=gnu++26 compilation failed to produce executable +FAIL: g++.dg/abi/vbase8-22.C -std=gnu++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.dg/abi/vbase8-22.C -std=gnu++26 (test for excess errors) +UNRESOLVED: g++.dg/abi/vbase8-22.C -std=gnu++26 compilation failed to produce executable +FAIL: g++.dg/cpp2a/constexpr-dtor3.C -std=c++26 (test for errors, line 26) +FAIL: g++.dg/ipa/ipa-icf-4.C -std=gnu++26 scan-ipa-dump icf "(Unified; Variable alias has been created)|(Symbol aliases are not supported by target)" +FAIL: g++.dg/ipa/ipa-icf-4.C -std=gnu++26 scan-ipa-dump icf "Equal symbols: [67]" +FAIL: g++.old-deja/g++.abi/primary3.C -std=c++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.old-deja/g++.abi/primary3.C -std=c++26 (test for excess errors) +UNRESOLVED: g++.old-deja/g++.abi/primary3.C -std=c++26 compilation failed to produce executable +FAIL: g++.old-deja/g++.abi/primary4.C -std=c++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.old-deja/g++.abi/primary4.C -std=c++26 (test for excess errors) +UNRESOLVED: g++.old-deja/g++.abi/primary4.C -std=c++26 compilation failed to produce executable +FAIL: g++.old-deja/g++.abi/vbase8-5.C -std=gnu++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.old-deja/g++.abi/vbase8-5.C -std=gnu++26 (test for excess errors) +UNRESOLVED: g++.old-deja/g++.abi/vbase8-5.C -std=gnu++26 compilation failed to produce executable +FAIL: g++.old-deja/g++.abi/vtable2.C -std=gnu++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.old-deja/g++.abi/vtable2.C -std=gnu++26 (test for excess errors) +UNRESOLVED: g++.old-deja/g++.abi/vtable2.C -std=gnu++26 compilation failed to produce executable +FAIL: g++.old-deja/g++.pt/mi1.C -std=c++26 (internal compiler error: in cxx_fold_indirect_ref, at cp/constexpr.cc:6154) +FAIL: g++.old-deja/g++.pt/mi1.C -std=c++26 (test for excess errors) +UNRESOLVED: g++.old-deja/g++.pt/mi1.C -std=c++26 compilation failed to produce executable 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); maybe will just need to remove the assert and replace 0 with tree_to_uhwi (off) The rest perhaps the classes now being constexpr result in some symbols being optimized away. Oh, and I've added a constexpr testcase for all the 64 possibilities of private/public keywords in the earlier posted testcase. Some of the subtests at constexpr evaluation differ from when the the vN variables are just const rather than constexpr and don't have constexpr ctors, see attached variant of the testcase which evaluates it at runtime. On N1, i.e. dynamic_cast<const V##N *> (v##N.foo ()) the dynamic and constexpr evaluation always matches, but there are some differences for N2 (i.e. dynamic_cast<const T##N *> (v##N.foo ())) and/or N3 (i.e. dynamic_cast<const U##N *> (v##N.foo ())): N2: 3, 5, 7, 13, 19, 20, 22, 28, 30, 35, 39, 54, 62 N3: 32, 34, 36, 40, 41, 42, 44, 45, 49, 53, 57, 61 N2+N3: 33, 37, 48, 50, 52, 56, 58, 60 Dunno if all those are bugs in libsupc++ or what we emit in the vtables or if there aren't some constexpr dynamic_cast bugs too. 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. * 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-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. --- gcc/gimple-fold.cc.jj 2025-06-09 14:15:20.247332867 +0200 +++ gcc/gimple-fold.cc 2025-06-25 23:27:19.759277006 +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-25 10:48:40.500289873 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2025-06-25 19:17:36.807964819 +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-25 19:11:13.814664025 +0200 +++ gcc/cp/cp-tree.h 2025-06-25 19:26:07.951693249 +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-25 19:11:13.885663153 +0200 +++ gcc/cp/method.cc 2025-06-25 19:17:36.810964782 +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-25 10:48:40.547289264 +0200 +++ gcc/cp/class.cc 2025-06-26 08:34:29.590084337 +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-25 10:48:40.550289225 +0200 +++ gcc/cp/constexpr.cc 2025-06-26 09:01:11.368368740 +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. */ --- gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C.jj 2025-06-25 19:17:36.811964770 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit1.C 2025-06-25 19:17:36.811964770 +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-25 19:17:36.811964770 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit2.C 2025-06-25 19:17:36.811964770 +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-25 19:47:48.553879158 +0200 +++ gcc/testsuite/g++.dg/cpp26/constexpr-virt-inherit3.C 2025-06-26 09:17:40.858583985 +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-25 10:48:40.608288473 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2025-06-25 19:17:36.826964586 +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-dtor16.C.jj 2025-06-25 10:48:40.608288473 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C 2025-06-25 19:17:36.831964525 +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-25 10:48:40.608288473 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 2025-06-25 19:44:12.900467305 +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-25 10:48:40.608288473 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 2025-06-26 09:19:52.815882016 +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-25 10:48:40.608288473 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice4.C 2025-06-25 19:17:36.842964390 +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 } } }; Jakub
--- gcc/cp/cp-tree.h 2025-06-23 21:17:58.569652869 +0200 +++ gcc/cp/cp-tree.h 2025-06-25 19:26:07.951693249 +0200 @@ -7104,6 +7104,7 @@ 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 */ --- gcc/cp/constexpr.cc 2025-06-24 12:28:49.561105734 +0200 +++ gcc/cp/constexpr.cc 2025-06-26 09:01:11.368368740 +0200 @@ -2599,6 +2599,22 @@ 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. */ --- gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 2025-06-24 12:32:35.299213775 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 2025-06-25 19:44:12.900467305 +0200 @@ -9,4 +9,4 @@ constexpr A a; // { dg-error "call" "" { target c++23_down } } -constexpr bool b1 = (dynamic_cast<C&>((B&)a), false); // { dg-error "reference 'dynamic_cast' failed" "" { target c++26 } } +constexpr bool b1 = (dynamic_cast<C&>((B&)a), false); --- gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 2025-06-24 10:16:01.438914543 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 2025-06-26 09:19:52.815882016 +0200 @@ -11,7 +11,7 @@ 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/gimple-fold.cc.jj 2025-06-09 14:15:20.247332867 +0200 +++ gcc/gimple-fold.cc 2025-06-25 23:27:19.759277006 +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/cp/class.cc.jj 2025-06-25 10:48:40.547289264 +0200 +++ gcc/cp/class.cc 2025-06-26 08:34:29.590084337 +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. */
// C++26 P3533R2 - constexpr virtual inheritance // { dg-do compile { target c++26 } } int main () { #define constexpr #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; } \ }; \ const V##N v##N; \ if (N1 !!dynamic_cast<const V##N *> (v##N.foo ())) ; else __builtin_printf ("%d N1\n", N); \ if (N2 !!dynamic_cast<const T##N *> (v##N.foo ())) ; else __builtin_printf ("%d N2\n", N); \ if (N3 !!dynamic_cast<const U##N *> (v##N.foo ())) ; else __builtin_printf ("%d N3\n", N); \ 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, !, !, !) }