Hi! The following patch on top of https://gcc.gnu.org/pipermail/gcc-patches/2025-June/686210.html https://gcc.gnu.org/pipermail/gcc-patches/2025-June/686558.html patches implements the compiler side of the C++26 paper. Based on the https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119064#c3 feedback, the patch enables the new conditional keywords trivially_relocatable_if_eligible and replaceable_if_eligible only for C++26, for older versions those conditional keywords yield -Wc++26-compat warning and are treated as normal identifiers. Plus __trivially_relocatable_if_eligible and __replaceable_if_eligible are handled as conditional keywords always without diagnostics (similarly to __final in C++98). The patch uses __builtin_ prefix on the new traits (but unlike clang which for some weird reason chose to name one __builtin_is_replaceable and another __builtin_is_cpp_trivially_relocatable this one uses __builtin_is_replaceable and __builtin_is_trivially_relocatable. I'll try to convince clang to change, they've only implemented it recently. The patch computes these properties on demand, only when something needs them (at the expense of eating 2 more bits per lang_type, but I've recently saved 64 bits and a patch to save another 64 bits is pending; and even 4 bits wouldn't fit). The patch doesn't add __builtin_trivially_relocate builtin that clang has, std::trivially_relocate is not constexpr and I think we don't need it for now at least until we implement some kind of vtable pointer signing __builtin_memmove should do the job. Especially if libstdc++ will for clang compatibility use the builtin if available and __builtin_memmove otherwise, we can switch any time. I've cross-tested all testcases also against the clang++ trunk implementation, and both compilers agreed in everything except for https://github.com/llvm/llvm-project/issues/143599 where clang++ was changed already and https://github.com/llvm/llvm-project/issues/144232 where I believe clang++ got it wrong too. The first testcase comes from https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html#simple-worked-examples just tweaked so that the classes are named differently each time and that it compiles. There are 3 differences from the paper vs. the g++ as well as clang++ implementation, I've added comments into trivially-relocatable1.C but I think either that part of the paper wasn't updated through the later changes or it got it wrong.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2025-06-16 Jakub Jelinek <ja...@redhat.com> PR c++/119064 gcc/ * doc/invoke.texi (Wc++26-compat): Document. gcc/c-family/ * c.opt (Wc++26-compat): New option. * c.opt.urls: Regenerate. * c-opts.cc (c_common_post_options): Clear warn_cxx26_compat for C++26 or later. * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine __cpp_trivial_relocatability=202502L. gcc/cp/ * cp-tree.h: Implement C++26 P2786R13 - Trivial Relocatability. (struct lang_type): Add trivially_relocatable_if_eligible, replaceable_if_eligible, trivially_relocatable, trivially_relocatable_computed, replaceable and replaceable_computed bitfields. Change width of dummy from 2 to 28. (CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE, CLASSTYPE_REPLACEABLE_IF_ELIGIBLE, CLASSTYPE_TRIVIALLY_RELOCATABLE, CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED, CLASSTYPE_REPLACEABLE, CLASSTYPE_REPLACEABLE_COMPUTED): Define. (enum virt_specifier): Add VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE and VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE enumerators. (trivially_relocatable_type_p, replaceable_type_p): Declare. * cp-trait.def (IS_NOTHROW_RELOCATABLE, IS_REPLACEABLE, IS_TRIVIALLY_RELOCATABLE): New traits. * parser.cc (cp_parser_class_property_specifier_seq_opt): Handle trivially_relocatable_if_eligible, __trivially_relocatable_if_eligible, replaceable_if_eligible and __replaceable_if_eligible. (cp_parser_class_head): Set CLASSTYPE_REPLACEABLE_IF_ELIGIBLE and/or CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE if corresponding conditional keywords were parsed. * pt.cc (instantiate_class_template): Copy over also CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE and CLASSTYPE_REPLACEABLE_IF_ELIGIBLE bits. * semantics.cc (referenceable_type_p): Move definition earlier. (trait_expr_value): Handle CPTK_IS_NOTHROW_RELOCATABLE, CPTK_IS_REPLACEABLE and CPTK_IS_TRIVIALLY_RELOCATABLE. (finish_trait_expr): Likewise. * tree.cc (default_movable_type_p): New function. (union_with_no_declared_special_member_fns): Likewise. (trivially_relocatable_type_p): Likewise. (replaceable_type_p): Likewise. * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_NOTHROW_RELOCATABLE, CPTK_IS_REPLACEABLE and CPTK_IS_TRIVIALLY_RELOCATABLE. gcc/testsuite/ * g++.dg/cpp26/feat-cxx26.C: Add test for __cpp_trivial_relocatability. * g++.dg/cpp26/trivially-relocatable1.C: New test. * g++.dg/cpp26/trivially-relocatable2.C: New test. * g++.dg/cpp26/trivially-relocatable3.C: New test. * g++.dg/cpp26/trivially-relocatable4.C: New test. * g++.dg/cpp26/trivially-relocatable5.C: New test. * g++.dg/cpp26/trivially-relocatable6.C: New test. * g++.dg/cpp26/trivially-relocatable7.C: New test. * g++.dg/cpp26/trivially-relocatable8.C: New test. * g++.dg/cpp26/trivially-relocatable9.C: New test. * g++.dg/cpp26/trivially-relocatable10.C: New test. * g++.dg/cpp26/trivially-relocatable11.C: New test. --- gcc/c-family/c.opt.jj 2025-06-12 09:48:50.064307296 +0200 +++ gcc/c-family/c.opt 2025-06-13 14:50:24.533527899 +0200 @@ -493,6 +493,10 @@ Wc++20-compat C++ ObjC++ Var(warn_cxx20_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Init(0) CPP(cpp_warn_cxx20_compat) CppReason(CPP_W_CXX20_COMPAT) Warn about C++ constructs whose meaning differs between ISO C++ 2017 and ISO C++ 2020. +Wc++26-compat +C++ ObjC++ Var(warn_cxx26_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Init(0) +Warn about C++ constructs whose meaning differs between ISO C++ 2023 and ISO C++ 2026. + Wc++11-extensions C++ ObjC++ Var(warn_cxx11_extensions) Warning Init(1) Warn about C++11 constructs in code compiled with an older standard. --- gcc/c-family/c.opt.urls.jj 2025-06-12 09:48:50.122306526 +0200 +++ gcc/c-family/c.opt.urls 2025-06-13 14:50:24.718525502 +0200 @@ -187,6 +187,9 @@ UrlSuffix(gcc/Warning-Options.html#index Wc++20-compat UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b20-compat) +Wc++26-compat +UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b26-compat) + Wc++11-extensions UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b11-extensions) --- gcc/c-family/c-opts.cc.jj 2025-06-12 09:48:50.000308146 +0200 +++ gcc/c-family/c-opts.cc 2025-06-13 14:50:24.479528598 +0200 @@ -1165,6 +1165,9 @@ c_common_post_options (const char **pfil warn_cxx20_compat = 0; cpp_opts->cpp_warn_cxx20_compat = 0; } + if (cxx_dialect >= cxx26) + /* Don't warn about C++26 compatibility changes in C++26 or later. */ + warn_cxx26_compat = 0; /* C++17 has stricter evaluation order requirements; let's use some of them for earlier C++ as well, so chaining works as expected. */ --- gcc/c-family/c-cppbuiltin.cc.jj 2025-06-12 09:48:49.829310417 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2025-06-13 14:50:24.496528378 +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_trivial_relocatability=202502L"); } if (flag_concepts && cxx_dialect > cxx14) cpp_define (pfile, "__cpp_concepts=202002L"); --- gcc/cp/cp-tree.h.jj 2025-06-12 09:49:19.747913102 +0200 +++ gcc/cp/cp-tree.h 2025-06-13 14:50:24.450528973 +0200 @@ -2492,6 +2492,13 @@ struct GTY(()) lang_type { bool erroneous : 1; bool non_pod_aggregate : 1; bool non_aggregate_pod : 1; + bool trivially_relocatable_if_eligible : 1; + bool replaceable_if_eligible : 1; + + bool trivially_relocatable : 1; + bool trivially_relocatable_computed : 1; + bool replaceable : 1; + bool replaceable_computed : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -2500,7 +2507,7 @@ struct GTY(()) lang_type { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 2; + unsigned dummy : 28; tree primary_base; vec<tree_pair_s, va_gc> *vcall_indices; @@ -2836,6 +2843,34 @@ struct GTY(()) lang_type { above (c++/120012). This could also be a hash_set. */ #define CLASSTYPE_NON_AGGREGATE_POD(NODE) \ (LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate_pod) + +/* True if this class is marked with trivially_relocatable_if_eligible + conditional keyword. */ +#define CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->trivially_relocatable_if_eligible) + +/* True if this class is marked with replaceable_if_eligible conditional + keyword. */ +#define CLASSTYPE_REPLACEABLE_IF_ELIGIBLE(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->replaceable_if_eligible) + +/* True if this class is trivially relocatable. */ +#define CLASSTYPE_TRIVIALLY_RELOCATABLE(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->trivially_relocatable) + +/* True if whether this class is trivially relocatable or not + has been computed already. */ +#define CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->trivially_relocatable_computed) + +/* True if this class is replaceable. */ +#define CLASSTYPE_REPLACEABLE(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->replaceable) + +/* True if whether this class is replaceable or not has been computed + already. */ +#define CLASSTYPE_REPLACEABLE_COMPUTED(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->replaceable_computed) /* Additional macros for inheritance information. */ @@ -6463,7 +6498,9 @@ enum virt_specifier { VIRT_SPEC_UNSPECIFIED = 0x0, VIRT_SPEC_FINAL = 0x1, - VIRT_SPEC_OVERRIDE = 0x2 + VIRT_SPEC_OVERRIDE = 0x2, + VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE = 0x4, + VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE = 0x8 }; /* A type-qualifier, or bitmask therefore, using the VIRT_SPEC @@ -8231,6 +8268,8 @@ extern bool pod_type_p (const_tree); extern bool layout_pod_type_p (const_tree); extern bool std_layout_type_p (const_tree); extern bool trivial_type_p (const_tree); +extern bool trivially_relocatable_type_p (tree); +extern bool replaceable_type_p (tree); extern bool trivially_copyable_p (const_tree); extern bool type_has_unique_obj_representations (const_tree); extern bool scalarish_type_p (const_tree); --- gcc/cp/cp-trait.def.jj 2025-06-12 09:48:50.611300032 +0200 +++ gcc/cp/cp-trait.def 2025-06-13 14:50:24.410529492 +0200 @@ -87,12 +87,14 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2) DEFTRAIT_EXPR (IS_NOTHROW_DESTRUCTIBLE, "__is_nothrow_destructible", 1) DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1) +DEFTRAIT_EXPR (IS_NOTHROW_RELOCATABLE, "__builtin_is_nothrow_relocatable", 1) DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1) DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2) DEFTRAIT_EXPR (IS_POD, "__is_pod", 1) DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1) DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1) DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1) +DEFTRAIT_EXPR (IS_REPLACEABLE, "__builtin_is_replaceable", 1) DEFTRAIT_EXPR (IS_SAME, "__is_same", 2) DEFTRAIT_EXPR (IS_SCOPED_ENUM, "__is_scoped_enum", 1) DEFTRAIT_EXPR (IS_STD_LAYOUT, "__is_standard_layout", 1) @@ -101,6 +103,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1) DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1) DEFTRAIT_EXPR (IS_TRIVIALLY_DESTRUCTIBLE, "__is_trivially_destructible", -1) +DEFTRAIT_EXPR (IS_TRIVIALLY_RELOCATABLE, "__builtin_is_trivially_relocatable", 1) DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1) DEFTRAIT_EXPR (IS_UNION, "__is_union", 1) DEFTRAIT_EXPR (IS_VIRTUAL_BASE_OF, "__builtin_is_virtual_base_of", 2) --- gcc/cp/parser.cc.jj 2025-06-13 14:50:09.256725765 +0200 +++ gcc/cp/parser.cc 2025-06-13 14:50:24.416529414 +0200 @@ -28012,6 +28012,8 @@ cp_parser_class_specifier (cp_parser* pa class-property-specifier: final + trivially_relocatable_if_eligible (C++26) + replaceable_if_eligible (C++26) Returns a bitmask representing the class-property-specifiers. */ @@ -28044,6 +28046,38 @@ cp_parser_class_property_specifier_seq_o } else if (id_equal (token->u.value, "__final")) virt_specifier = VIRT_SPEC_FINAL; + else if (id_equal (token->u.value, "trivially_relocatable_if_eligible")) + { + if (cxx_dialect < cxx26) + { + /* Warn about the C++26 conditional keyword (but don't parse + it). */ + warning_at (token->location, OPT_Wc__26_compat, + "identifier %qE is a conditional keyword in C++26", + token->u.value); + break; + } + virt_specifier = VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE; + } + else if (id_equal (token->u.value, + "__trivially_relocatable_if_eligible")) + virt_specifier = VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE; + else if (id_equal (token->u.value, "replaceable_if_eligible")) + { + if (cxx_dialect < cxx26) + { + /* Warn about the C++26 conditional keyword (but don't parse + it). */ + warning_at (token->location, OPT_Wc__26_compat, + "identifier %qE is a conditional keyword in C++26", + token->u.value); + break; + } + virt_specifier = VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE; + } + else if (id_equal (token->u.value, + "__replaceable_if_eligible")) + virt_specifier = VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE; else break; @@ -28599,6 +28633,10 @@ cp_parser_class_head (cp_parser* parser, DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location; if (type && (virt_specifiers & VIRT_SPEC_FINAL)) CLASSTYPE_FINAL (type) = 1; + if (type && (virt_specifiers & VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE)) + CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE (type) = 1; + if (type && (virt_specifiers & VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE)) + CLASSTYPE_REPLACEABLE_IF_ELIGIBLE (type) = 1; out: parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; return type; --- gcc/cp/pt.cc.jj 2025-06-12 09:48:51.130293140 +0200 +++ gcc/cp/pt.cc 2025-06-13 14:50:24.394529699 +0200 @@ -12700,7 +12700,13 @@ instantiate_class_template (tree type) determine_visibility (TYPE_MAIN_DECL (type)); } if (CLASS_TYPE_P (type)) - CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern); + { + CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern); + CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE (type) + = CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE (pattern); + CLASSTYPE_REPLACEABLE_IF_ELIGIBLE (type) + = CLASSTYPE_REPLACEABLE_IF_ELIGIBLE (pattern); + } pbinfo = TYPE_BINFO (pattern); --- gcc/cp/semantics.cc.jj 2025-06-12 09:48:51.180292476 +0200 +++ gcc/cp/semantics.cc 2025-06-13 14:50:24.368530035 +0200 @@ -13359,6 +13359,18 @@ object_type_p (const_tree type) && !VOID_TYPE_P (type)); } +/* [defns.referenceable] True iff TYPE is a referenceable type. */ + +static bool +referenceable_type_p (const_tree type) +{ + return (TYPE_REF_P (type) + || object_type_p (type) + || (FUNC_OR_METHOD_TYPE_P (type) + && (type_memfn_quals (type) == TYPE_UNQUALIFIED + && type_memfn_rqual (type) == REF_QUAL_NONE))); +} + /* Actually evaluates the trait. */ static bool @@ -13528,6 +13540,19 @@ trait_expr_value (cp_trait_kind kind, tr case CPTK_IS_NOTHROW_INVOCABLE: return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none); + case CPTK_IS_NOTHROW_RELOCATABLE: + if (trivially_relocatable_type_p (type1)) + return true; + else + { + type1 = strip_array_types (type1); + return (referenceable_type_p (type1) + && is_nothrow_xible (INIT_EXPR, type1, + cp_build_reference_type (type1, + /*rval=*/true)) + && is_nothrow_xible (BIT_NOT_EXPR, type1, NULL_TREE)); + } + case CPTK_IS_OBJECT: return object_type_p (type1); @@ -13546,6 +13571,9 @@ trait_expr_value (cp_trait_kind kind, tr case CPTK_IS_REFERENCE: return type_code1 == REFERENCE_TYPE; + case CPTK_IS_REPLACEABLE: + return replaceable_type_p (type1); + case CPTK_IS_SAME: return same_type_p (type1, type2); @@ -13570,6 +13598,9 @@ trait_expr_value (cp_trait_kind kind, tr case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: return is_trivially_xible (BIT_NOT_EXPR, type1, NULL_TREE); + case CPTK_IS_TRIVIALLY_RELOCATABLE: + return trivially_relocatable_type_p (type1); + case CPTK_IS_UNBOUNDED_ARRAY: return array_of_unknown_bound_p (type1); @@ -13698,18 +13729,6 @@ same_type_ref_bind_p (cp_trait_kind kind (non_reference (to), non_reference (from)))); } -/* [defns.referenceable] True iff TYPE is a referenceable type. */ - -static bool -referenceable_type_p (const_tree type) -{ - return (TYPE_REF_P (type) - || object_type_p (type) - || (FUNC_OR_METHOD_TYPE_P (type) - && (type_memfn_quals (type) == TYPE_UNQUALIFIED - && type_memfn_rqual (type) == REF_QUAL_NONE))); -} - /* Process a trait expression. */ tree @@ -13752,8 +13771,11 @@ finish_trait_expr (location_t loc, cp_tr case CPTK_IS_LITERAL_TYPE: case CPTK_IS_POD: case CPTK_IS_STD_LAYOUT: + case CPTK_IS_REPLACEABLE: + case CPTK_IS_NOTHROW_RELOCATABLE: case CPTK_IS_TRIVIAL: case CPTK_IS_TRIVIALLY_COPYABLE: + case CPTK_IS_TRIVIALLY_RELOCATABLE: case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: if (!check_trait_type (type1, /* kind = */ 2)) return error_mark_node; --- gcc/cp/tree.cc.jj 2025-06-12 09:48:51.224291891 +0200 +++ gcc/cp/tree.cc 2025-06-16 12:46:48.217705455 +0200 @@ -4715,6 +4715,292 @@ trivial_type_p (const_tree t) return scalarish_type_p (t); } +/* Returns 1 iff type T is a default-movable type, as defined in + [class.prop]. */ +static bool +default_movable_type_p (tree t) +{ + if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t)) + return false; + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (user_provided_p (dtor) || DECL_DELETED_FN (dtor)) + return false; + + tree copy_ctor = NULL_TREE, move_ctor = NULL_TREE; + tree copy_assign = NULL_TREE, move_assign = NULL_TREE; + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + move_ctor = lazily_declare_fn (sfk_move_constructor, t); + if (CLASSTYPE_LAZY_MOVE_ASSIGN (t)) + move_assign = lazily_declare_fn (sfk_move_assignment, t); + if (!move_ctor) + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_ctor = *iter; + else if (move_fn_p (*iter)) + { + move_ctor = *iter; + break; + } + } + if (!move_assign) + for (ovl_iterator iter (get_class_binding_direct (t, + assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_assign = *iter; + else if (move_fn_p (*iter)) + { + move_assign = *iter; + break; + } + } + if (!move_ctor) + { + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + copy_ctor = lazily_declare_fn (sfk_copy_constructor, t); + if (!copy_ctor) + return false; + if (user_provided_p (copy_ctor) + || DECL_DELETED_FN (copy_ctor) + || DECL_CONTEXT (copy_ctor) != t + || DECL_INHERITED_CTOR (copy_ctor)) + return false; + } + else if (user_provided_p (move_ctor) + || DECL_DELETED_FN (move_ctor) + || DECL_CONTEXT (move_ctor) != t + || DECL_INHERITED_CTOR (move_ctor)) + return false; + if (!move_assign) + { + if (CLASSTYPE_LAZY_COPY_ASSIGN (t)) + copy_assign = lazily_declare_fn (sfk_copy_assignment, t); + if (!copy_assign) + return false; + if (user_provided_p (copy_assign) + || DECL_DELETED_FN (copy_assign) + || DECL_CONTEXT (copy_assign) != t) + return false; + } + else if (user_provided_p (move_assign) + || DECL_DELETED_FN (move_assign) + || DECL_CONTEXT (move_assign) != t) + return false; + return true; +} + +/* Returns 1 iff type T is a union with no user declared special member + functions. */ + +static bool +union_with_no_declared_special_member_fns (tree t) +{ + if (TREE_CODE (t) != UNION_TYPE) + return false; + + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL + && !DECL_ARTIFICIAL (*iter) + && (default_ctor_p (*iter) || copy_fn_p (*iter) || move_fn_p (*iter))) + return false; + + for (ovl_iterator iter (get_class_binding_direct (t, assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL + && !DECL_ARTIFICIAL (*iter) + && (copy_fn_p (*iter) || move_fn_p (*iter))) + return false; + + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (!DECL_ARTIFICIAL (dtor)) + return false; + + return true; +} + +/* Returns 1 iff type T is a trivially relocatable type, as defined in + [basic.types.general] and [class.prop]. */ + +bool +trivially_relocatable_type_p (tree t) +{ + t = strip_array_types (t); + + if (!CLASS_TYPE_P (t)) + return scalarish_type_p (t); + + t = TYPE_MAIN_VARIANT (t); + if (CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t)) + return CLASSTYPE_TRIVIALLY_RELOCATABLE (t); + if (!COMPLETE_TYPE_P (t)) + return false; + + if (!CLASSTYPE_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE (t) + && !union_with_no_declared_special_member_fns (t) + && !default_movable_type_p (t)) + { + nontriv: + CLASSTYPE_TRIVIALLY_RELOCATABLE (t) = 0; + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t) = 1; + return false; + } + + if (CLASSTYPE_VBASECLASSES (t)) + goto nontriv; + + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (DECL_DELETED_FN (dtor)) + goto nontriv; + + tree binfo, base_binfo; + unsigned int i; + for (binfo = TYPE_BINFO (t), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree basetype = TREE_TYPE (base_binfo); + if (!trivially_relocatable_type_p (basetype)) + goto nontriv; + } + + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && !DECL_ARTIFICIAL (field) + && !DECL_UNNAMED_BIT_FIELD (field)) + { + tree type = TREE_TYPE (field); + if (type == error_mark_node) + goto nontriv; + if (!TYPE_REF_P (type) && !trivially_relocatable_type_p (type)) + goto nontriv; + } + + CLASSTYPE_TRIVIALLY_RELOCATABLE (t) = 1; + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t) = 1; + return true; +} + +/* Returns 1 iff type T is a replaceable type, as defined in [basic.types] + and [class]. */ + +bool +replaceable_type_p (tree t) +{ + t = strip_array_types (t); + + if (cv_qualified_p (t)) + return false; + + if (!CLASS_TYPE_P (t)) + return scalarish_type_p (t); + + t = TYPE_MAIN_VARIANT (t); + if (CLASSTYPE_REPLACEABLE_COMPUTED (t)) + return CLASSTYPE_REPLACEABLE (t); + if (!COMPLETE_TYPE_P (t)) + return false; + + if (!CLASSTYPE_REPLACEABLE_IF_ELIGIBLE (t) + && !union_with_no_declared_special_member_fns (t) + && !default_movable_type_p (t)) + { + nonrepl: + CLASSTYPE_REPLACEABLE (t) = 0; + CLASSTYPE_REPLACEABLE_COMPUTED (t) = 1; + return false; + } + + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (DECL_DELETED_FN (dtor)) + goto nonrepl; + + tree copy_ctor = NULL_TREE, move_ctor = NULL_TREE; + tree copy_assign = NULL_TREE, move_assign = NULL_TREE; + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + move_ctor = lazily_declare_fn (sfk_move_constructor, t); + if (CLASSTYPE_LAZY_MOVE_ASSIGN (t)) + move_assign = lazily_declare_fn (sfk_move_assignment, t); + if (!move_ctor) + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_ctor = *iter; + else if (move_fn_p (*iter)) + { + move_ctor = *iter; + break; + } + } + if (!move_assign) + for (ovl_iterator iter (get_class_binding_direct (t, + assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_assign = *iter; + else if (move_fn_p (*iter)) + { + move_assign = *iter; + break; + } + } + if (!move_ctor) + { + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + copy_ctor = lazily_declare_fn (sfk_copy_constructor, t); + if (!copy_ctor || DECL_DELETED_FN (copy_ctor)) + goto nonrepl; + } + else if (DECL_DELETED_FN (move_ctor)) + goto nonrepl; + if (!move_assign) + { + if (CLASSTYPE_LAZY_COPY_ASSIGN (t)) + copy_assign = lazily_declare_fn (sfk_copy_assignment, t); + if (!copy_assign || DECL_DELETED_FN (copy_assign)) + goto nonrepl; + } + else if (DECL_DELETED_FN (move_assign)) + goto nonrepl; + + tree binfo, base_binfo; + unsigned int i; + for (binfo = TYPE_BINFO (t), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree basetype = TREE_TYPE (base_binfo); + if (!replaceable_type_p (basetype)) + goto nonrepl; + } + + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && !DECL_ARTIFICIAL (field) + && !DECL_UNNAMED_BIT_FIELD (field)) + { + tree type = TREE_TYPE (field); + if (type == error_mark_node) + goto nonrepl; + if (!replaceable_type_p (type)) + goto nonrepl; + } + + CLASSTYPE_REPLACEABLE (t) = 1; + CLASSTYPE_REPLACEABLE_COMPUTED (t) = 1; + return true; +} + /* Returns 1 iff type T is a POD type, as defined in [basic.types]. */ bool --- gcc/cp/constraint.cc.jj 2025-06-12 09:48:50.611300032 +0200 +++ gcc/cp/constraint.cc 2025-06-13 14:50:24.425529297 +0200 @@ -3157,6 +3157,9 @@ diagnose_trait_expr (tree expr, tree arg else inform (loc, " %qT is not nothrow invocable by %qE", t1, t2); break; + case CPTK_IS_NOTHROW_RELOCATABLE: + inform (loc, " %qT is not nothrow relocatable", t1); + break; case CPTK_IS_OBJECT: inform (loc, " %qT is not an object type", t1); break; @@ -3176,6 +3179,9 @@ diagnose_trait_expr (tree expr, tree arg case CPTK_IS_REFERENCE: inform (loc, " %qT is not a reference", t1); break; + case CPTK_IS_REPLACEABLE: + inform (loc, " %qT is not replaceable", t1); + break; case CPTK_IS_SAME: inform (loc, " %qT is not the same as %qT", t1, t2); break; @@ -3203,6 +3209,9 @@ diagnose_trait_expr (tree expr, tree arg case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: inform (loc, " %qT is not trivially destructible", t1); break; + case CPTK_IS_TRIVIALLY_RELOCATABLE: + inform (loc, " %qT is not trivially relocatable", t1); + break; case CPTK_IS_UNBOUNDED_ARRAY: inform (loc, " %qT is not an unbounded array", t1); break; --- gcc/doc/invoke.texi.jj 2025-06-12 09:48:51.308290776 +0200 +++ gcc/doc/invoke.texi 2025-06-13 14:50:24.972522213 +0200 @@ -353,7 +353,7 @@ Objective-C and Objective-C++ Dialects}. -Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat -Wc11-c23-compat -Wc23-c2y-compat -Wc++-compat -Wc++11-compat -Wc++14-compat -Wc++17-compat --Wc++20-compat +-Wc++20-compat -Wc++26-compat -Wno-c++11-extensions -Wno-c++14-extensions -Wno-c++17-extensions -Wno-c++20-extensions -Wno-c++23-extensions -Wcalloc-transposed-args @@ -9625,6 +9625,12 @@ and ISO C++ 2017. This warning is enabl Warn about C++ constructs whose meaning differs between ISO C++ 2017 and ISO C++ 2020. This warning is enabled by @option{-Wall}. +@opindex Wc++26-compat +@opindex Wno-c++26-compat +@item -Wc++26-compat @r{(C++ and Objective-C++ only)} +Warn about C++ constructs whose meaning differs between ISO C++ 2023 +and upcoming ISO C++ 2026. This warning is enabled by @option{-Wall}. + @opindex Wc++11-extensions @opindex Wno-c++11-extensions @item -Wno-c++11-extensions @r{(C++ and Objective-C++ only)} --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2025-06-02 11:00:06.220524528 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2025-06-16 14:34:04.665828005 +0200 @@ -634,3 +634,9 @@ #elif __cpp_pp_embed != 202502 # error "__cpp_pp_embed != 202502" #endif + +#ifndef __cpp_trivial_relocatability +# error "__cpp_trivial_relocatability" +#elif __cpp_trivial_relocatability != 202502 +# error "__cpp_trivial_relocatability != 202502" +#endif --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable1.C.jj 2025-06-13 14:50:24.458528870 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable1.C 2025-06-13 18:20:12.558346274 +0200 @@ -0,0 +1,137 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A {}; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { + B (); + ~B (); + B (const B &); + B (B &&); + B &operator= (const B &); + B &operator= (B &&); +}; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +struct C { + C (C &&) = delete; + C &operator= (C &&) = delete; + C () = default; +}; + +// Note, P2786R13 says it is trivially relocatable, but I think +// it isn't default-movable because overload resolution in both +// cases selects a deleted special member fn. +static_assert (!std::is_trivially_relocatable_v <C>, ""); +static_assert (!std::is_replaceable_v <C>, ""); + +struct D : A {}; + +static_assert (std::is_trivially_relocatable_v <D>, ""); +static_assert (std::is_replaceable_v <D>, ""); + +struct E : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +struct F trivially_relocatable_if_eligible : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +struct G { B data; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +struct H { ~H () = default; }; + +static_assert (std::is_trivially_relocatable_v <H>, ""); +static_assert (std::is_replaceable_v <H>, ""); + +struct I { ~I (); }; +I::~I () = default; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +struct J { virtual ~J () = default; }; + +// Note, P2786R13 says otherwise for both, but that looks like +// a bug in the paper, it otherwise says that polymorphic types +// can be both trivially relocatable and replaceable. +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (std::is_replaceable_v <J>, ""); + +struct K { ~K () = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (L &&) = default; }; + +// Note, P2786R13 says otherwise for both, but that looks like +// a bug in the paper to me. While move ctor is trivial here, +// copy assignment operator is implicitly declared as deleted +// and move assignent operator is not declared. +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M (M &&); }; +M::M (M &&) = default; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N (N &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable2.C.jj 2025-06-13 14:56:36.330754007 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable2.C 2025-06-16 11:14:58.076361036 +0200 @@ -0,0 +1,204 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +class A {}; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); +static_assert (std::is_trivially_relocatable <A>::value, ""); +static_assert (std::is_replaceable <A>::value, ""); + +struct B { ~B (); }; +static B z; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); +static_assert (!std::is_trivially_relocatable <B>::value, ""); +static_assert (!std::is_replaceable <B>::value, ""); + +class C trivially_relocatable_if_eligible {}; + +static_assert (std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_replaceable_v <C>, ""); + +class D trivially_relocatable_if_eligible : A {}; + +static_assert (std::is_trivially_relocatable_v <D>, ""); +static_assert (std::is_replaceable_v <D>, ""); + +class E trivially_relocatable_if_eligible { + int a; + void *b; + int c[3]; + A d[3]; + B &e = z; +}; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +class F trivially_relocatable_if_eligible : A {}; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +class G trivially_relocatable_if_eligible : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +class H trivially_relocatable_if_eligible : B {}; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +class I trivially_relocatable_if_eligible { I (I &&); }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +class J trivially_relocatable_if_eligible { ~J (); }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +class K trivially_relocatable_if_eligible { + B a; + B b[1]; + const B c; + const B d[1]; +}; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +class L trivially_relocatable_if_eligible: virtual A, B { B a; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +static_assert (!std::is_trivially_relocatable_v <void>, ""); +static_assert (!std::is_trivially_relocatable_v <const void>, ""); +static_assert (std::is_trivially_relocatable_v <int>, ""); +static_assert (std::is_trivially_relocatable_v <const int>, ""); +static_assert (std::is_trivially_relocatable_v <char>, ""); +static_assert (std::is_trivially_relocatable_v <char const volatile>, ""); +static_assert (std::is_trivially_relocatable_v <unsigned long long>, ""); +static_assert (std::is_trivially_relocatable_v <void *>, ""); +static_assert (std::is_trivially_relocatable_v <const int *>, ""); +static_assert (!std::is_trivially_relocatable_v <int &>, ""); +static_assert (!std::is_trivially_relocatable_v <A &>, ""); +static_assert (std::is_trivially_relocatable_v <const A>, ""); +static_assert (std::is_trivially_relocatable_v <A [1]>, ""); +static_assert (std::is_trivially_relocatable_v <A []>, ""); +static_assert (!std::is_replaceable_v <void>, ""); +static_assert (!std::is_replaceable_v <const void>, ""); +static_assert (std::is_replaceable_v <int>, ""); +static_assert (!std::is_replaceable_v <const int>, ""); +static_assert (std::is_replaceable_v <char>, ""); +static_assert (!std::is_replaceable_v <char const volatile>, ""); +static_assert (std::is_replaceable_v <unsigned long long>, ""); +static_assert (std::is_replaceable_v <void *>, ""); +static_assert (std::is_replaceable_v <const int *>, ""); +static_assert (!std::is_replaceable_v <int &>, ""); +static_assert (!std::is_replaceable_v <A &>, ""); +static_assert (!std::is_replaceable_v <const A>, ""); +static_assert (std::is_replaceable_v <A [1]>, ""); +static_assert (std::is_replaceable_v <A []>, ""); + +struct M { const int i; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N trivially_relocatable_if_eligible { const int i; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); + +struct O { ~O (); }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +struct P { ~P () = default; }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q { Q (Q &&); Q (const Q &) = default; }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R { R (R &&); }; + +static_assert (!std::is_trivially_relocatable_v <R>, ""); +static_assert (!std::is_replaceable_v <R>, ""); + +struct S { S (S &&) = default; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T { T (T &&) = default; T &operator= (T &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <T>, ""); +static_assert (std::is_replaceable_v <T>, ""); + +struct U { U (const U &); }; + +static_assert (!std::is_trivially_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +struct V { V (const V&) = default; }; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); + +struct W { W (W &&) = delete; W (const W &) = default; }; + +static_assert (!std::is_trivially_relocatable_v <W>, ""); +static_assert (!std::is_replaceable_v <W>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable3.C.jj 2025-06-13 15:18:33.829918971 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable3.C 2025-06-13 18:54:12.621912128 +0200 @@ -0,0 +1,213 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +class A {}; + +struct B { ~B (); }; + +class C trivially_relocatable_if_eligible { C (C &&); }; + +template <typename T> +class D trivially_relocatable_if_eligible : T {}; +D<A> a; +D<B> b; + +static_assert (std::is_trivially_relocatable_v <D<A>>, ""); +static_assert (!std::is_trivially_relocatable_v <D<B>>, ""); +static_assert (std::is_replaceable_v <D<A>>, ""); +static_assert (!std::is_replaceable_v <D<B>>, ""); + +struct E { E (E &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +struct F { F (const F &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (!std::is_replaceable_v <F>, ""); + +struct G { G &operator= (G &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +struct H { ~H () = delete; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +union U { C u; }; + +static_assert (std::is_trivially_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +template <typename T> +struct I { int s; T t; }; + +static_assert (std::is_trivially_relocatable_v <I<int>>, ""); +static_assert (std::is_trivially_relocatable_v <I<volatile int>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int &>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<int &>>, ""); +static_assert (std::is_trivially_relocatable_v <I<int [2]>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <I<int []>>, ""); +static_assert (std::is_replaceable_v <I<int>>, ""); +static_assert (!std::is_replaceable_v <I<volatile int>>, ""); +static_assert (!std::is_replaceable_v <I<const int>>, ""); +static_assert (!std::is_replaceable_v <I<const int &>>, ""); +static_assert (!std::is_replaceable_v <I<int &>>, ""); +static_assert (std::is_replaceable_v <I<int [2]>>, ""); +static_assert (!std::is_replaceable_v <I<const int [2]>>, ""); + +template <typename T> +struct J trivially_relocatable_if_eligible { int s; T t; }; + +static_assert (std::is_trivially_relocatable_v <J<int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<volatile int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int &>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int &>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int []>>, ""); +static_assert (std::is_replaceable_v <J<int>>, ""); +static_assert (!std::is_replaceable_v <J<volatile int>>, ""); +static_assert (!std::is_replaceable_v <J<const int>>, ""); +static_assert (!std::is_replaceable_v <J<const int &>>, ""); +static_assert (!std::is_replaceable_v <J<int &>>, ""); +static_assert (std::is_replaceable_v <J<int [2]>>, ""); +static_assert (!std::is_replaceable_v <J<const int [2]>>, ""); +static_assert (std::is_replaceable_v <J<int []>>, ""); + +struct K { K (K &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (const L &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M &operator= (M &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N (N &&) = default; N &operator= (N &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (std::is_replaceable_v <N>, ""); + +struct O { + O (const O &) = default; + O (O &&) = default; + O &operator= (O &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <O>, ""); +static_assert (std::is_replaceable_v <O>, ""); + +struct P { P (P &&) = default; P &operator= (P &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q { Q (Q &&) {} }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R { R (const R &) {} }; + +static_assert (!std::is_trivially_relocatable_v <R>, ""); +static_assert (!std::is_replaceable_v <R>, ""); + +struct S { S &operator= (const S &) { return *this; }; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T {}; + +static_assert (std::is_trivially_relocatable_v <T>, ""); +static_assert (std::is_replaceable_v <T>, ""); + +struct V replaceable_if_eligible {}; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); + +struct W { template <typename U> W (const U &) = delete; }; + +static_assert (std::is_trivially_relocatable_v <W>, ""); +static_assert (std::is_replaceable_v <W>, ""); + +template <typename T> +struct X : T {}; + +static_assert (!std::is_trivially_relocatable_v <X<Q>>, ""); +static_assert (!std::is_replaceable_v <X<Q>>, ""); + +template <typename T> +struct Y : virtual T {}; + +static_assert (!std::is_trivially_relocatable_v <Y<I<int>>>, ""); +static_assert (!std::is_trivially_relocatable_v <Y<I<const int>>>, ""); +static_assert (!std::is_trivially_relocatable_v <Y<Q>>, ""); +static_assert (std::is_replaceable_v <Y<I<int>>>, ""); +static_assert (!std::is_replaceable_v <Y<I<const int>>>, ""); +static_assert (!std::is_replaceable_v <Y<Q>>, ""); + +struct Z { + virtual ~Z () = default; + Z (Z &&) = default; + Z &operator= (Z &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <Z>, ""); +static_assert (std::is_replaceable_v <Z>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable4.C.jj 2025-06-13 18:21:45.710143237 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable4.C 2025-06-14 19:38:23.628353410 +0200 @@ -0,0 +1,128 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A replaceable_if_eligible { + ~A () = delete; + A (A &&) = default; + A &operator= (A &&) = default; +}; + +static_assert (!std::is_trivially_relocatable_v <A>, ""); +static_assert (!std::is_replaceable_v <A>, ""); + +struct B replaceable_if_eligible { B (const B &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +template <typename T> +struct C replaceable_if_eligible : virtual T {}; + +static_assert (!std::is_trivially_relocatable_v <C<A>>, ""); +static_assert (!std::is_trivially_relocatable_v <C<B>>, ""); +static_assert (!std::is_replaceable_v <C<A>>, ""); +static_assert (!std::is_replaceable_v <C<B>>, ""); + +template <typename T> +struct D { int s; T t; }; + +static_assert (!std::is_trivially_relocatable_v <C<D<int>>>, ""); +static_assert (std::is_replaceable_v <C<D<int>>>, ""); + +struct E trivially_relocatable_if_eligible replaceable_if_eligible { + E (E &&); + E &operator= (E &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +struct F trivially_relocatable_if_eligible replaceable_if_eligible { + F (F &&) = default; + F &operator= (F &&); +}; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +struct G replaceable_if_eligible { G (G const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +struct H { H (H const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <H>, ""); +static_assert (std::is_replaceable_v <H>, ""); + +struct I replaceable_if_eligible { I &operator= (const I &) = default; }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); +static_assert (std::is_replaceable_v <I>, ""); + +struct J { J &operator= (J const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (std::is_replaceable_v <J>, ""); + +struct K { K (const K &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (L&&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M operator= (M); }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N operator= (N &&); }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable5.C.jj 2025-06-16 09:33:43.795664691 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable5.C 2025-06-16 10:39:43.901881363 +0200 @@ -0,0 +1,77 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-additional-options "-Wc++26-compat" } + +struct A __trivially_relocatable_if_eligible { A (const A &&); }; +struct B __replaceable_if_eligible { B (const B &&); B &operator= (B &&); }; +struct C __replaceable_if_eligible __final __trivially_relocatable_if_eligible { C (const C &&); C &operator= (C &&); }; +#if __cpp_trivial_relocatability >= 202502L +struct D trivially_relocatable_if_eligible { D (const D &&); }; +struct E replaceable_if_eligible { E (const E &&); E &operator= (E &&); }; +struct F trivially_relocatable_if_eligible replaceable_if_eligible final { F (const F &&); F &operator= (F &&); }; +#else +struct D trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "variable 'D trivially_relocatable_if_eligible' has initializer but incomplete type" "" { target c++23_down } .-1 } +struct E replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "variable 'E replaceable_if_eligible' has initializer but incomplete type" "" { target c++23_down } .-1 } +struct F trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target c++23_down } .-1 } +#endif +#if __cplusplus <= 202302L +struct G {}; +struct G trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct H {}; +struct H replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct I {}; +struct I trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +#endif // { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target c++23_down } .-1 } +struct J {}; +struct J __trivially_relocatable_if_eligible {}; // { dg-error "redefinition of 'struct J'" } +struct K {}; +struct K __replaceable_if_eligible {}; // { dg-error "redefinition of 'struct K'" } +struct L {}; +struct L __trivially_relocatable_if_eligible __replaceable_if_eligible {}; // { dg-error "redefinition of 'struct L'" } +struct M __trivially_relocatable_if_eligible __trivially_relocatable_if_eligible {}; // { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" } +struct N __replaceable_if_eligible __replaceable_if_eligible {}; // { dg-error "duplicate '__replaceable_if_eligible' specifier" } +struct O __trivially_relocatable_if_eligible __replaceable_if_eligible __replaceable_if_eligible __trivially_relocatable_if_eligible final final {}; +// { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target *-*-* } .-1 } +// { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target *-*-* } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target *-*-* } .-3 } +#if __cpp_trivial_relocatability >= 202502L +struct P trivially_relocatable_if_eligible trivially_relocatable_if_eligible {}; // { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct Q replaceable_if_eligible replaceable_if_eligible {}; // { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } } +struct R trivially_relocatable_if_eligible replaceable_if_eligible replaceable_if_eligible trivially_relocatable_if_eligible final final {}; +// { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target c++26 } .-3 } +struct S trivially_relocatable_if_eligible __trivially_relocatable_if_eligible {}; // { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct T replaceable_if_eligible __replaceable_if_eligible {}; // { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target c++26 } } +struct U trivially_relocatable_if_eligible replaceable_if_eligible __replaceable_if_eligible __trivially_relocatable_if_eligible final __final {}; +// { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate '__final' specifier" "" { target c++26 } .-3 } +struct V __trivially_relocatable_if_eligible trivially_relocatable_if_eligible {}; // { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct W __replaceable_if_eligible replaceable_if_eligible {}; // { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } } +struct X __trivially_relocatable_if_eligible __replaceable_if_eligible replaceable_if_eligible trivially_relocatable_if_eligible __final final {}; +// { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target c++26 } .-3 } +#else +struct Y {}; +Y foo (); +struct Y trivially_relocatable_if_eligible = foo (); // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct Z {}; +Z bar (); +struct Z replaceable_if_eligible = bar (); // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +#endif + +static_assert (__builtin_is_trivially_relocatable (A), ""); +static_assert (__builtin_is_replaceable (B), ""); +static_assert (__builtin_is_trivially_relocatable (C), ""); +static_assert (__builtin_is_replaceable (C), ""); +#if __cpp_trivial_relocatability >= 202502L +static_assert (__builtin_is_trivially_relocatable (D), ""); +static_assert (__builtin_is_replaceable (E), ""); +static_assert (__builtin_is_trivially_relocatable (F), ""); +static_assert (__builtin_is_replaceable (F), ""); +#endif --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable6.C.jj 2025-06-16 10:25:27.258067137 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable6.C 2025-06-16 10:40:52.386988182 +0200 @@ -0,0 +1,30 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++98_only } } +// { dg-additional-options "-Wc++26-compat" } + +struct A __trivially_relocatable_if_eligible {}; +struct B __replaceable_if_eligible {}; +struct C __replaceable_if_eligible __final __trivially_relocatable_if_eligible {}; +struct D trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "variable 'D trivially_relocatable_if_eligible' has initializer but incomplete type" "" { target *-*-* } .-1 } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-2 } +struct E replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } +// { dg-error "variable 'E replaceable_if_eligible' has initializer but incomplete type" "" { target *-*-* } .-1 } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-2 } +struct F trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target *-*-* } .-1 } +struct G {}; +struct G trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-1 } +struct H {}; +struct H replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-1 } +struct I {}; +struct I trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target *-*-* } .-1 } +struct J {}; +J foo (); +struct J trivially_relocatable_if_eligible = foo (); // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +struct K {}; +K bar (); +struct K replaceable_if_eligible = bar (); // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable7.C.jj 2025-06-16 11:13:26.503550912 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable7.C 2025-06-16 11:18:34.533548463 +0200 @@ -0,0 +1,27 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + // { dg-error "invalid use of incomplete type 'struct A'" "" { target *-*-* } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} // { dg-error "invalid use of incomplete type 'struct A'" "" { target *-*-* } .-1 } + +struct A; // { dg-message "forward declaration of 'struct A'" } + +auto a = std::is_trivially_relocatable_v <A>; // { dg-message "required from here" } +auto b = std::is_replaceable_v <A>; // { dg-message "required from here" } --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable8.C.jj 2025-06-16 12:13:05.871038846 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable8.C 2025-06-16 13:22:10.685038512 +0200 @@ -0,0 +1,158 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A { A (A &&) = default; A &operator= (A &&) = default; ~A () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&) = default; ~B () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +struct C { C (C &&) = default; C &operator= (C &&); ~C () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <C>, ""); +static_assert (!std::is_replaceable_v <C>, ""); + +struct D { D (D &&) = delete; D &operator= (D &&) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); +static_assert (!std::is_replaceable_v <D>, ""); + +struct E { E (E &&) = default; E &operator= (E &&) = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +struct F { F (F &&) = default; F &operator= (F &&) = default; ~F () = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (!std::is_replaceable_v <F>, ""); + +struct G { G (const G &) = default; G &operator= (const G &) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +struct H { H (const H &); H &operator= (const H &) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +struct I { I (const I &) = default; I &operator= (const I &); ~I () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +struct J { J (const J &) = delete; J &operator= (const J &) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +struct K { K (const K &) = default; K &operator= (const K &) = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct M; +struct L { L (L &&) = default; L (M &&); L &operator= (L &&) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <L>, ""); +static_assert (std::is_replaceable_v <L>, ""); + +struct M : public L { using L::L; M (const M &); M &operator= (M &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct O; +struct N { N (N &&) = default; N &operator= (N &&) = default; N &operator= (O &&); int a; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (std::is_replaceable_v <N>, ""); + +struct O : public N { using N::operator=; O (O &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +struct Q; +struct P { template <typename T> P (T &&) {} }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q : public P { using P::P; Q (const Q &); }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct S; +struct R { R (const R &) = default; R (const M &); R &operator= (R &&) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <R>, ""); +static_assert (std::is_replaceable_v <R>, ""); + +struct S : public R { using R::R; S &operator= (S &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T { T (T &&) = default; T &operator= (T &&) = default; ~T (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <T>, ""); +static_assert (!std::is_replaceable_v <T>, ""); + +struct U { U (const U &) = default; U &operator= (const U &) = default; ~U (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +struct V { public: V (); private: V (V &&) = default; V &operator= (V &&) = default; ~V () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable9.C.jj 2025-06-16 13:11:02.046747323 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable9.C 2025-06-16 14:15:27.319396081 +0200 @@ -0,0 +1,104 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A trivially_relocatable_if_eligible { A (A &&); A &operator= (A &&); ~A (); int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&); ~B (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); + +struct C trivially_relocatable_if_eligible : public A { C (C &&); C &operator= (C &&); ~C (); int a; }; + +static_assert (std::is_trivially_relocatable_v <C>, ""); + +struct D trivially_relocatable_if_eligible : public B { D (D &&); D &operator= (D &&); ~D (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); + +struct E trivially_relocatable_if_eligible { E (E &&); E &operator= (E &&); ~E (); A a; }; + +static_assert (std::is_trivially_relocatable_v <E>, ""); + +struct F trivially_relocatable_if_eligible { F (F &&); F &operator= (F &&); ~F (); B a; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); + +struct G trivially_relocatable_if_eligible { G (G &&); G &operator= (G &&); ~G () = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); + +struct H trivially_relocatable_if_eligible : virtual A { H (H &&); H &operator= (H &&); ~H (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); + +struct I trivially_relocatable_if_eligible { I (I &&); I &operator= (I &&); ~I (); A &a; int &b; }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); + +struct J trivially_relocatable_if_eligible { J (J &&); J &operator= (J &&); ~J (); B &a; int &b; }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); + +struct K trivially_relocatable_if_eligible { K (K &&); K &operator= (K &&); ~K (); union { A a; int b; char c; }; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); + +struct L trivially_relocatable_if_eligible { L (L &&); L &operator= (L &&); ~L (); union { int a; B b; short c; }; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); + +struct M trivially_relocatable_if_eligible { M (M &&); M &operator= (M &&); ~M () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <M>, ""); + +struct N trivially_relocatable_if_eligible { N (N &&); N &operator= (N &&); ~N (); union { M a; int b; char c; }; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); + +struct O trivially_relocatable_if_eligible { O (O &&); O &operator= (O &&); ~O (); union { unsigned long long a; int b; char c; }; }; + +static_assert (std::is_trivially_relocatable_v <O>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable10.C.jj 2025-06-16 13:23:55.929667739 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable10.C 2025-06-16 14:24:51.562039442 +0200 @@ -0,0 +1,135 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A replaceable_if_eligible { A (A &&); A &operator= (A &&); ~A (); int a; }; + +static_assert (std::is_replaceable_v <A>, ""); +static_assert (!std::is_replaceable_v <const A>, ""); +static_assert (!std::is_replaceable_v <A volatile>, ""); +static_assert (!std::is_replaceable_v <const A volatile>, ""); + +struct B { B (B &&); B &operator= (B &&); ~B (); int a; }; + +static_assert (!std::is_replaceable_v <B>, ""); + +struct C replaceable_if_eligible : public A { C (C &&); C &operator= (C &&); ~C (); int a; }; + +static_assert (std::is_replaceable_v <C>, ""); + +struct D replaceable_if_eligible : public B { D (D &&); D &operator= (D &&); ~D (); int a; }; + +static_assert (!std::is_replaceable_v <D>, ""); + +struct E replaceable_if_eligible { E (E &&); E &operator= (E &&); ~E (); A a; }; + +static_assert (std::is_replaceable_v <E>, ""); + +struct F replaceable_if_eligible { F (F &&); F &operator= (F &&); ~F (); B a; }; + +static_assert (!std::is_replaceable_v <F>, ""); + +struct G replaceable_if_eligible { G (G &&); G &operator= (G &&); ~G () = delete; int a; }; + +static_assert (!std::is_replaceable_v <G>, ""); + +struct H replaceable_if_eligible : virtual A { H (H &&); H &operator= (H &&); ~H (); int a; }; + +static_assert (std::is_replaceable_v <H>, ""); + +struct I replaceable_if_eligible { I (I &&) = delete; I &operator= (I &&); ~I (); int a; }; + +static_assert (!std::is_replaceable_v <I>, ""); + +struct J replaceable_if_eligible { J (J &&); J &operator= (J &&) = delete; ~J (); int a; }; + +static_assert (!std::is_replaceable_v <J>, ""); + +struct K replaceable_if_eligible { K (const K &) = delete; K &operator= (K &&); ~K (); int a; }; + +static_assert (!std::is_replaceable_v <K>, ""); + +struct L replaceable_if_eligible { L (L &&); L &operator= (const L &) = delete; ~L (); int a; }; + +static_assert (!std::is_replaceable_v <L>, ""); + +struct M replaceable_if_eligible { M (); private: M (M &&); M &operator= (M &&); ~M (); int a; }; + +static_assert (std::is_replaceable_v <M>, ""); + +struct N replaceable_if_eligible { N (N &&); N &operator= (N &&); ~N (); const A a; }; + +static_assert (!std::is_replaceable_v <N>, ""); + +struct O replaceable_if_eligible { O (O &&); O &operator= (O &&); ~O (); volatile A a; }; + +static_assert (!std::is_replaceable_v <O>, ""); + +struct P replaceable_if_eligible { P (P &&); P &operator= (P &&); ~P (); const volatile A a; }; + +static_assert (!std::is_replaceable_v <P>, ""); + +struct Q replaceable_if_eligible { Q (Q &&); Q &operator= (Q &&); ~Q (); union { A a; int b; char c; }; }; + +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R replaceable_if_eligible { R (R &&); R &operator= (R &&); ~R (); union { int a; B b; short c; }; }; + +static_assert (!std::is_replaceable_v <R>, ""); + +struct S replaceable_if_eligible { S (S &&); S &operator= (S &&); ~S (); union { int a; const int b; short c; }; }; + +static_assert (!std::is_replaceable_v <S>, ""); + +struct T replaceable_if_eligible { T (T &&); T &operator= (T &&); ~T () = default; int a; }; + +static_assert (std::is_replaceable_v <T>, ""); + +struct U replaceable_if_eligible { U (U &&); U &operator= (U &&); ~U (); union { T a; int b; char c; }; }; + +static_assert (!std::is_replaceable_v <U>, ""); + +struct V replaceable_if_eligible { V (V &&); V &operator= (V &&); ~V (); union { unsigned long long a; int b; char c; }; }; + +static_assert (std::is_replaceable_v <V>, ""); --- gcc/testsuite/g++.dg/cpp26/trivially-relocatable11.C.jj 2025-06-16 13:34:18.881555140 +0200 +++ gcc/testsuite/g++.dg/cpp26/trivially-relocatable11.C 2025-06-16 13:59:30.799860570 +0200 @@ -0,0 +1,134 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A { A (A &&) = default; A &operator= (A &&) = default; ~A () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&) = default; ~B () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +union C { int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_replaceable_v <C>, ""); + +union D { int a; A b; B c; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); +static_assert (!std::is_replaceable_v <D>, ""); + +union E { E (); int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +union F { F () = default; int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +union G { G (const G &); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +union H { H (const H &) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +union I { I (I &&); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +union J { J (J &&) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +union K { K &operator= (const K &); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +union L { L &operator= (const L &) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +union M { M &operator= (M &&); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +union N { N &operator= (N &&) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); + +union O { ~O (); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +union P { ~P () = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <P>, ""); +static_assert (!std::is_replaceable_v <P>, ""); + +union Q { int a; const A b; }; + +static_assert (std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +union S { volatile int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); Jakub