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