https://gcc.gnu.org/g:ca6cd7cb48512d60c28024174b319df4580498ce
commit r17-671-gca6cd7cb48512d60c28024174b319df4580498ce Author: Jakub Jelinek <[email protected]> Date: Fri May 22 10:15:45 2026 +0200 c++, libstdc++: Implement C++26 P3074R7 and CWG3189 - trivial unions [PR119059] The following patch attempts to implement the C++26 P3074R7 - trivial unions (was std::uninitialized<T>) paperand proposed resolution of CWG3189 - Implicitly deleted destructors for union-like classes with the exception of the #define __cpp_lib_constexpr_inplace_vector 2025XXL // also in <inplace_vector> addition and possibly needed <inplace_vector> changes (will defer that to Jonathan / Tomasz) and except for the changes in [class.default.ctor]/4 which were reverted by P3726R2 later on . There is one change which doesn't affect just C++26 but also older versions of the standard, https://eel.is/c++draft/class.default.ctor#2.2 or its older counterparts, e.g. C++11 had in [class.ctor]/5 "any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor" but we've been ignoring the "non-variant" part thereof and diagnosing it for variant members too. Note, this is related to the other unimplemented rule I've posted a patch earlier for that was dismissed (reject all variant members const before C++26), so some cases which we've rejected for a wrong reason will now be accepted when they are still invalid before C++26. 2026-05-22 Jakub Jelinek <[email protected]> PR c++/119059 gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine __cpp_trivial_union to 202502L. gcc/cp/ * method.cc: Implement C++26 P3074R7 - trivial unions (was std::uninitialized<T>) (except the sentence removed again in P3726R2) and proposed resolution of CWG3189 - Implicitly deleted destructors for union-like classes. (walk_field_subobs): Don't do default_init_uninitialized_part checks for variant members. Don't check subobject ctor/dtor for variant members for ctor/inheriting ctor or when subobject doesn't have member initializer for dtor and it is either the dtor_from_ctor case or the current class doesn't have user provided ctors. * class.cc (check_field_decl): Don't or in TYPE_HAS_NONTRIVIAL_DESTRUCTOR or TYPE_HAS_DEFAULT_CONSTRUCTOR of variant subobjects for C++26. gcc/testsuite/ * g++.dg/DRs/dr2581-1.C: Expect warning for __cpp_trivial_union. * g++.dg/DRs/dr2581-2.C: Expect error for __cpp_trivial_union. * g++.dg/cpp26/feat-cxx26.C: Add __cpp_trivial_union checking. * g++.dg/cpp26/trivial-union1.C: New test. * g++.dg/cpp26/trivial-union2.C: New test. * g++.dg/reflect/trivial-union1.C: New test. * g++.dg/reflect/type_trait6.C: Adjust expected result of one is_destructible_type and two is_nothrow_destructible_type calls. * g++.dg/reflect/is_constructible_type1.C: Adjust expected result of one is_constructible_type call. * g++.dg/init/pr43719.C: Don't expect one error. * g++.dg/init/pr25811.C: Don't expect 3 diagnostic messages, instead expect a different one for C++98 only. * g++.dg/other/anon-union2.C: Only expect one diagnostic for C++23 and older. * g++.dg/cpp0x/union1.C: Only expect 6 diagnostic messages for C++23 and older. * g++.dg/cpp0x/union4.C: Only expect 3 diagnostic messages for C++23 and older. * g++.dg/cpp0x/defaulted2.C: Only expect 2 diagnostic messages for C++23 and older. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/c-family/c-cppbuiltin.cc | 1 + gcc/cp/class.cc | 21 +- gcc/cp/method.cc | 54 ++++++ gcc/testsuite/g++.dg/DRs/dr2581-1.C | 2 +- gcc/testsuite/g++.dg/DRs/dr2581-2.C | 2 +- gcc/testsuite/g++.dg/cpp0x/defaulted2.C | 4 +- gcc/testsuite/g++.dg/cpp0x/union1.C | 12 +- gcc/testsuite/g++.dg/cpp0x/union4.C | 6 +- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 6 + gcc/testsuite/g++.dg/cpp26/trivial-union1.C | 133 +++++++++++++ gcc/testsuite/g++.dg/cpp26/trivial-union2.C | 7 + gcc/testsuite/g++.dg/init/pr25811.C | 7 +- gcc/testsuite/g++.dg/init/pr43719.C | 2 +- gcc/testsuite/g++.dg/other/anon-union2.C | 2 +- .../g++.dg/reflect/is_constructible_type1.C | 2 +- gcc/testsuite/g++.dg/reflect/trivial-union1.C | 214 +++++++++++++++++++++ gcc/testsuite/g++.dg/reflect/type_trait6.C | 6 +- 17 files changed, 453 insertions(+), 28 deletions(-) diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 74cbeecb165e..608b6ba5774b 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1122,6 +1122,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_impl_reflection=202603L"); else cpp_warn (pfile, "__cpp_impl_reflection"); + cpp_define (pfile, "__cpp_trivial_union=202502L"); } 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 c9a7eb5c76ec..6e4dc3cb0930 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -3903,17 +3903,28 @@ check_field_decl (tree field, else { TYPE_NEEDS_CONSTRUCTING (t) |= TYPE_NEEDS_CONSTRUCTING (type); - TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) - |= TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type); TYPE_HAS_COMPLEX_COPY_ASSIGN (t) |= (TYPE_HAS_COMPLEX_COPY_ASSIGN (type) || !TYPE_HAS_COPY_ASSIGN (type)); TYPE_HAS_COMPLEX_COPY_CTOR (t) |= (TYPE_HAS_COMPLEX_COPY_CTOR (type) || !TYPE_HAS_COPY_CTOR (type)); - TYPE_HAS_COMPLEX_MOVE_ASSIGN (t) |= TYPE_HAS_COMPLEX_MOVE_ASSIGN (type); + TYPE_HAS_COMPLEX_MOVE_ASSIGN (t) + |= TYPE_HAS_COMPLEX_MOVE_ASSIGN (type); TYPE_HAS_COMPLEX_MOVE_CTOR (t) |= TYPE_HAS_COMPLEX_MOVE_CTOR (type); - TYPE_HAS_COMPLEX_DFLT (t) |= (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type) - || TYPE_HAS_COMPLEX_DFLT (type)); + /* In C++26, triviality of default ctor or dtor of a variant member + doesn't matter for triviality of the t's default ctor or dtor. + Before C++26, non-trivial default ctor or dtor of a variant + member makes it deleted with the exception of default ctor + when DMI is present, but in that case default ctor is + non-trivial. */ + if (TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE) + { + TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) + |= TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type); + TYPE_HAS_COMPLEX_DFLT (t) + |= (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type) + || TYPE_HAS_COMPLEX_DFLT (type)); + } } if (TYPE_HAS_COPY_CTOR (type) diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index fe26b5130ff2..ccfcfb202aa4 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -2692,6 +2692,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, enum { unknown, no, yes } only_dmi_mem = (sfk == sfk_constructor && TREE_CODE (ctx) == UNION_TYPE ? unknown : no); + int has_user_provided_ctor = -1; again: for (tree field = fields; field; field = DECL_CHAIN (field)) @@ -2771,6 +2772,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, bad = false; if (CP_TYPE_CONST_P (mem_type) + && TREE_CODE (ctx) != UNION_TYPE && default_init_uninitialized_part (mem_type)) { if (diag) @@ -2847,6 +2849,58 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, else argtype = NULL_TREE; + if (cxx_dialect >= cxx26 && TREE_CODE (ctx) == UNION_TYPE) + { + /* C++26 [class.default.ctor]/2: + A defaulted default constructor for class X is defined as deleted + if + ... + - any non-variant potentially constructed subobject, except for + a non-static data member with a brace-or-equal-initializer, has + class type M (or possibly multidimensional array thereof) and + overload resolution as applied to find M's corresponding + constructor does not result in a usable candidate, + So, for C++26 this ignores default constructors of variant + members. */ + if (sfk == sfk_constructor || sfk == sfk_inheriting_constructor) + continue; + + /* C++26 [class.default.ctor]/2: + ... + - any potentially constructed subobject S has class type M (or + possibly multidimensional array thereof), M has a destructor + that is deleted or inaccessible from the defaulted default + constructor, and either S is non-variant or S has a default + member initializer. + This is the dtor_from_ctor case, so ignore destructors of + variant members unless they have a DMI. + C++26 with CWG3189 [class.dtor]/4: + A defaulted destructor for a class X is defined as deleted if + ... + - X is has a non-union class and any non-variant potentially + constructed subobject has S of class type M (or possibly + multidimensional array thereof) where either + - S is not a variant member and M has a destructor that is + deleted or is inaccessible from the defaulted destructor, or + - S is a variant member, M has a destructor that is deleted, + inaccessible from the defaulted destructor, or non-trivial, + and either + - V S has a default member initializer or + - X has a user-provided constructor. + This is the !dtor_from_ctor case, so ignore destructors of + variant members unless they have a DMI or X has user-provided + constructor. */ + if (sfk == sfk_destructor) + { + if (!dtor_from_ctor && has_user_provided_ctor == -1) + has_user_provided_ctor + = type_has_user_provided_constructor (current_class_type); + if (DECL_INITIAL (field) == NULL_TREE + && (dtor_from_ctor || !has_user_provided_ctor)) + continue; + } + } + rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); process_subob_fn (rval, sfk, spec_p, trivial_p, deleted_p, diff --git a/gcc/testsuite/g++.dg/DRs/dr2581-1.C b/gcc/testsuite/g++.dg/DRs/dr2581-1.C index cb761b7378ec..7298f3162b5b 100644 --- a/gcc/testsuite/g++.dg/DRs/dr2581-1.C +++ b/gcc/testsuite/g++.dg/DRs/dr2581-1.C @@ -94,7 +94,7 @@ #undef __cpp_template_parameters #undef __cpp_template_template_args // { dg-warning "undefining '__cpp_template_template_args'" "" { target c++20 } } #undef __cpp_threadsafe_static_init // { dg-warning "undefining '__cpp_threadsafe_static_init'" "" { target c++20 } } -#undef __cpp_trivial_union +#undef __cpp_trivial_union // { dg-warning "undefining '__cpp_trivial_union'" "" { target c++26 } } #undef __cpp_unicode_characters // { dg-warning "undefining '__cpp_unicode_characters'" "" { target c++20 } } #undef __cpp_unicode_literals // { dg-warning "undefining '__cpp_unicode_literals'" "" { target c++20 } } #undef __cpp_user_defined_literals // { dg-warning "undefining '__cpp_user_defined_literals'" "" { target c++20 } } diff --git a/gcc/testsuite/g++.dg/DRs/dr2581-2.C b/gcc/testsuite/g++.dg/DRs/dr2581-2.C index 0f2aec0e4255..74041c8c3cb3 100644 --- a/gcc/testsuite/g++.dg/DRs/dr2581-2.C +++ b/gcc/testsuite/g++.dg/DRs/dr2581-2.C @@ -95,7 +95,7 @@ #define __cpp_template_parameters 202502L #define __cpp_template_template_args 201611L // { dg-error "'__cpp_template_template_args' redefined" "" { target c++20 } } #define __cpp_threadsafe_static_init 200806L // { dg-error "'__cpp_threadsafe_static_init' redefined" "" { target c++20 } } -#define __cpp_trivial_union 202502L +#define __cpp_trivial_union 202502L // { dg-error "'__cpp_trivial_union' redefined" "" { target c++26 } } #define __cpp_unicode_characters 200704L // { dg-error "'__cpp_unicode_characters' redefined" "" { target c++17 } } #define __cpp_unicode_literals 200710L // { dg-error "'__cpp_unicode_literals' redefined" "" { target c++20 } } #define __cpp_user_defined_literals 200809L // { dg-error "'__cpp_user_defined_literals' redefined" "" { target c++20 } } diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted2.C b/gcc/testsuite/g++.dg/cpp0x/defaulted2.C index b7b31438491c..da1e77e1212d 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted2.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted2.C @@ -55,7 +55,7 @@ G::G() = default; union U { - G g; // { dg-error "union member.*non-trivial" } + G g; // { dg-error "union member.*non-trivial" "" { target c++23_down } } }; int main() @@ -63,7 +63,7 @@ int main() F f; F f2(f); // { dg-error "use" } const B* b = new const B; // { dg-error "uninitialized const" } - U u; // { dg-error "deleted" } + U u; // { dg-error "deleted" "" { target c++23_down } } } // { dg-prune-output "implicitly deleted because" } diff --git a/gcc/testsuite/g++.dg/cpp0x/union1.C b/gcc/testsuite/g++.dg/cpp0x/union1.C index ff415fc57369..fa13c18c69e9 100644 --- a/gcc/testsuite/g++.dg/cpp0x/union1.C +++ b/gcc/testsuite/g++.dg/cpp0x/union1.C @@ -14,7 +14,7 @@ union B A a; // { dg-error "union member" } }; -B b; // { dg-error "B::B\\(\\)" "B::B" } +B b; // { dg-error "B::B\\(\\)" "B::B" { target c++23_down } } B b2(b); // { dg-error "B::B\\(const B&\\)" "B::B" } struct C @@ -25,10 +25,10 @@ struct C }; }; -C c; // { dg-error "C::C\\(\\)" "C::C" } +C c; // { dg-error "C::C\\(\\)" "C::C" { target c++23_down } } C c2(c); // { dg-error "C::C\\(const C&\\)" "C::C" } -// { dg-error "B::~B" "B::~B" { target *-*-* } 17 } -// { dg-error "B::~B" "B::~B" { target *-*-* } 18 } -// { dg-error "C::~C" "C::~C" { target *-*-* } 28 } -// { dg-error "C::~C" "C::~C" { target *-*-* } 29 } +// { dg-error "B::~B" "B::~B" { target c++23_down } 17 } +// { dg-error "B::~B" "B::~B" { target c++23_down } 18 } +// { dg-error "C::~C" "C::~C" { target c++23_down } 28 } +// { dg-error "C::~C" "C::~C" { target c++23_down } 29 } diff --git a/gcc/testsuite/g++.dg/cpp0x/union4.C b/gcc/testsuite/g++.dg/cpp0x/union4.C index cf9916dc3673..45be643b2e39 100644 --- a/gcc/testsuite/g++.dg/cpp0x/union4.C +++ b/gcc/testsuite/g++.dg/cpp0x/union4.C @@ -3,15 +3,15 @@ struct SFoo { - SFoo() =delete; // { dg-message "declared" } + SFoo() =delete; // { dg-message "declared" "" { target c++23_down } } }; -union UFoo // { dg-error "deleted" } +union UFoo // { dg-error "deleted" "" { target c++23_down } } { SFoo foo; }; int main() { - UFoo(); // { dg-error "deleted" } + UFoo(); // { dg-error "deleted" "" { target c++23_down } } } diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 248116699617..d3ab72901240 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -652,3 +652,9 @@ #elif __cpp_expansion_statements != 202506 # error "__cpp_expansion_statements != 202506" #endif + +#ifndef __cpp_trivial_union +# error "__cpp_trivial_union" +#elif __cpp_trivial_union != 202502 +# error "__cpp_trivial_union != 202502" +#endif diff --git a/gcc/testsuite/g++.dg/cpp26/trivial-union1.C b/gcc/testsuite/g++.dg/cpp26/trivial-union1.C new file mode 100644 index 000000000000..7f1e75ed167b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union1.C @@ -0,0 +1,133 @@ +// P3074R7 - trivial unions (was std::uninitialized<T>) +// { dg-do compile { target c++11 } } + +#include <type_traits> + +// These two were incorrectly deleted. +union A { int a; const int b; }; +static_assert (std::is_default_constructible <A>::value, ""); +static_assert (std::is_trivially_default_constructible <A>::value, ""); +static_assert (std::is_destructible <A>::value, ""); +static_assert (std::is_trivially_destructible <A>::value, ""); +struct B { int a; union { int b; const int c; }; }; +static_assert (std::is_default_constructible <B>::value, ""); +static_assert (std::is_trivially_default_constructible <B>::value, ""); +static_assert (std::is_destructible <B>::value, ""); +static_assert (std::is_trivially_destructible <B>::value, ""); +// C::C() is incorrectly not deleted in C++11 to 23, but in C++26 it should +// not be deleted. +union C { const int a = 42; const long b; ~C (); }; +#if __cpp_trivial_union >= 202502L +static_assert (std::is_default_constructible <C>::value, ""); +static_assert (!std::is_trivially_default_constructible <C>::value, ""); +static_assert (std::is_destructible <C>::value, ""); +static_assert (!std::is_trivially_destructible <C>::value, ""); +#endif +struct D { D () = delete; D (int); ~D () = default; }; +union E { D a = 42; D b; ~E (); }; +static_assert (std::is_default_constructible <E>::value, ""); +static_assert (std::is_destructible <E>::value, ""); +struct F { int a; union { D b = 42; D c; }; }; +static_assert (std::is_default_constructible <F>::value, ""); +static_assert (std::is_destructible <F>::value, ""); +struct G { G (); ~G (); }; +union I { int a; const int b; ~I (); }; +static_assert (std::is_default_constructible <I>::value, ""); +static_assert (!std::is_trivially_default_constructible <I>::value, ""); +static_assert (std::is_destructible <I>::value, ""); +static_assert (!std::is_trivially_destructible <I>::value, ""); +union J { D a; int b; }; +#if __cpp_trivial_union >= 202502L +static_assert (std::is_default_constructible <J>::value, ""); +static_assert (std::is_trivially_default_constructible <J>::value, ""); +#else +static_assert (!std::is_default_constructible <J>::value, ""); +static_assert (!std::is_trivially_default_constructible <J>::value, ""); +#endif +static_assert (std::is_destructible <J>::value, ""); +static_assert (std::is_trivially_destructible <J>::value, ""); +union K { G a; int b; }; +#if __cpp_trivial_union >= 202502L +static_assert (std::is_default_constructible <K>::value, ""); +static_assert (std::is_trivially_default_constructible <K>::value, ""); +static_assert (std::is_destructible <K>::value, ""); +static_assert (std::is_trivially_destructible <K>::value, ""); +#else +static_assert (!std::is_default_constructible <K>::value, ""); +static_assert (!std::is_trivially_default_constructible <K>::value, ""); +static_assert (!std::is_destructible <K>::value, ""); +static_assert (!std::is_trivially_destructible <K>::value, ""); +#endif +struct L { int a; union { G b; int c; }; }; +#if __cpp_trivial_union >= 202502L +static_assert (std::is_default_constructible <L>::value, ""); +static_assert (std::is_trivially_default_constructible <L>::value, ""); +static_assert (std::is_destructible <L>::value, ""); +static_assert (std::is_trivially_destructible <L>::value, ""); +#else +static_assert (!std::is_default_constructible <L>::value, ""); +static_assert (!std::is_trivially_default_constructible <L>::value, ""); +static_assert (!std::is_destructible <L>::value, ""); +static_assert (!std::is_trivially_destructible <L>::value, ""); +#endif +union M { M (); int a; int b; }; +static_assert (std::is_default_constructible <M>::value, ""); +static_assert (!std::is_trivially_default_constructible <M>::value, ""); +static_assert (std::is_destructible <M>::value, ""); +static_assert (std::is_trivially_destructible <M>::value, ""); +union N { N (); int a; G b; }; +static_assert (!std::is_default_constructible <N>::value, ""); +static_assert (!std::is_trivially_default_constructible <N>::value, ""); +static_assert (!std::is_destructible <N>::value, ""); +static_assert (!std::is_trivially_destructible <N>::value, ""); +struct O { O (); union { int a; int b; }; }; +static_assert (std::is_default_constructible <O>::value, ""); +static_assert (!std::is_trivially_default_constructible <O>::value, ""); +static_assert (std::is_destructible <O>::value, ""); +static_assert (std::is_trivially_destructible <O>::value, ""); +struct P { P (); union { int a; G b; }; }; +static_assert (!std::is_default_constructible <P>::value, ""); +static_assert (!std::is_trivially_default_constructible <P>::value, ""); +static_assert (!std::is_destructible <P>::value, ""); +static_assert (!std::is_trivially_destructible <P>::value, ""); +struct Q { Q (int); union { int a; G b; }; }; +static_assert (!std::is_default_constructible <Q>::value, ""); +static_assert (!std::is_trivially_default_constructible <Q>::value, ""); +static_assert (!std::is_destructible <Q>::value, ""); +static_assert (!std::is_trivially_destructible <Q>::value, ""); +struct R { R () = default; R (int); union { int a; G b; }; }; +static_assert (!std::is_default_constructible <R>::value, ""); +static_assert (!std::is_trivially_default_constructible <R>::value, ""); +static_assert (!std::is_destructible <R>::value, ""); +static_assert (!std::is_trivially_destructible <R>::value, ""); +struct S { S (int); ~S (); }; +union T { T (); int a; int b = 42; }; +static_assert (std::is_default_constructible <T>::value, ""); +static_assert (!std::is_trivially_default_constructible <T>::value, ""); +static_assert (std::is_destructible <T>::value, ""); +static_assert (std::is_trivially_destructible <T>::value, ""); +union U { U (); int a; S b = 42; }; +static_assert (!std::is_default_constructible <U>::value, ""); +static_assert (!std::is_trivially_default_constructible <U>::value, ""); +static_assert (!std::is_destructible <U>::value, ""); +static_assert (!std::is_trivially_destructible <U>::value, ""); +struct V { V (); union { int a; int b = 42; }; }; +static_assert (std::is_default_constructible <V>::value, ""); +static_assert (!std::is_trivially_default_constructible <V>::value, ""); +static_assert (std::is_destructible <V>::value, ""); +static_assert (std::is_trivially_destructible <V>::value, ""); +struct W { W (); union { int a; S b = 42; }; }; +static_assert (!std::is_default_constructible <W>::value, ""); +static_assert (!std::is_trivially_default_constructible <W>::value, ""); +static_assert (!std::is_destructible <W>::value, ""); +static_assert (!std::is_trivially_destructible <W>::value, ""); +struct X { X (int); union { int a; S b = 42; }; }; +static_assert (!std::is_default_constructible <X>::value, ""); +static_assert (!std::is_trivially_default_constructible <X>::value, ""); +static_assert (!std::is_destructible <X>::value, ""); +static_assert (!std::is_trivially_destructible <X>::value, ""); +struct Y { Y () = default; Y (int); union { int a; S b = 42; }; }; +static_assert (!std::is_default_constructible <Y>::value, ""); +static_assert (!std::is_trivially_default_constructible <Y>::value, ""); +static_assert (!std::is_destructible <Y>::value, ""); +static_assert (!std::is_trivially_destructible <Y>::value, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivial-union2.C b/gcc/testsuite/g++.dg/cpp26/trivial-union2.C new file mode 100644 index 000000000000..c1a64dac7377 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivial-union2.C @@ -0,0 +1,7 @@ +// P3074R7 - trivial unions (was std::uninitialized<T>) +// { dg-do compile { target c++20 } } + +union U { int a, b; }; +template<U u> class X {}; +constexpr U make() { U u; return u; } +void f(X<make()>) {} diff --git a/gcc/testsuite/g++.dg/init/pr25811.C b/gcc/testsuite/g++.dg/init/pr25811.C index 853eeae3feb6..8864f7a1a6fe 100644 --- a/gcc/testsuite/g++.dg/init/pr25811.C +++ b/gcc/testsuite/g++.dg/init/pr25811.C @@ -124,10 +124,9 @@ struct Z // { dg-error "deleted" "" { target c++11 } } Z5 z5; }; -union U // { dg-message "implicitly deleted" "" { target c++11 } } - // { dg-error "uninitialized" "" { target c++11 } .-1 } +union U { - int const i; // { dg-message "should be initialized" } + int const i; }; void f1 () @@ -207,5 +206,5 @@ void f15 () void f16 () { - new U; // { dg-error "deleted|uninitialized const member" } + new U; // { dg-error "uninitialized const member in 'union U' using 'new' without new-initializer" "" { target c++98_only } } } diff --git a/gcc/testsuite/g++.dg/init/pr43719.C b/gcc/testsuite/g++.dg/init/pr43719.C index c8cebc2de153..522a8cc52f70 100644 --- a/gcc/testsuite/g++.dg/init/pr43719.C +++ b/gcc/testsuite/g++.dg/init/pr43719.C @@ -109,7 +109,7 @@ struct Z // { dg-error "deleted" "" { target c++11 } } Z5 z5; }; -union U // { dg-error "uninitialized" "" { target c++11 } } +union U { int const i; // { dg-message "should be initialized" } }; diff --git a/gcc/testsuite/g++.dg/other/anon-union2.C b/gcc/testsuite/g++.dg/other/anon-union2.C index 31bb74fa99c3..6f4842688a66 100644 --- a/gcc/testsuite/g++.dg/other/anon-union2.C +++ b/gcc/testsuite/g++.dg/other/anon-union2.C @@ -6,5 +6,5 @@ struct S { }; void f() { - union { S a; }; // { dg-error "constructor|no match" } + union { S a; }; // { dg-error "constructor|no match" "" { target c++23_down } } } diff --git a/gcc/testsuite/g++.dg/reflect/is_constructible_type1.C b/gcc/testsuite/g++.dg/reflect/is_constructible_type1.C index 0b9d3e047793..36e13b3614fe 100644 --- a/gcc/testsuite/g++.dg/reflect/is_constructible_type1.C +++ b/gcc/testsuite/g++.dg/reflect/is_constructible_type1.C @@ -603,7 +603,7 @@ static_assert (!is_constructible_type (^^DelnAny, { ^^int, ^^void * })); static_assert (!is_constructible_type (^^const DelnAny, { ^^int, ^^void * })); static_assert (!is_constructible_type (^^DelnAny, { ^^Empty, ^^B, ^^D })); static_assert (!is_constructible_type (^^const DelnAny, { ^^Empty, ^^B, ^^D })); -static_assert (!is_constructible_type (^^NontrivialUnion, {})); +static_assert (is_constructible_type (^^NontrivialUnion, {})); static_assert (!is_constructible_type (^^NontrivialUnion, { ^^const NontrivialUnion & })); static_assert (!is_constructible_type (^^UnusualCopy, {})); static_assert (!is_constructible_type (^^UnusualCopy, { ^^UnusualCopy })); diff --git a/gcc/testsuite/g++.dg/reflect/trivial-union1.C b/gcc/testsuite/g++.dg/reflect/trivial-union1.C new file mode 100644 index 000000000000..6c9282a2ccf4 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/trivial-union1.C @@ -0,0 +1,214 @@ +// P3074R7 - trivial unions (was std::uninitialized<T>) +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> +#include <ranges> + +using namespace std::meta; +constexpr auto ctx = std::meta::access_context::unchecked (); +union A { int a; const int b; }; +static_assert (is_default_constructible_type (^^A)); +static_assert (is_trivially_default_constructible_type (^^A)); +static_assert (is_destructible_type (^^A)); +static_assert (is_trivially_destructible_type (^^A)); +constexpr auto Actor = (members_of (^^A, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Adtor = (members_of (^^A, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Actor) && !is_deleted (Actor)); +static_assert (is_defaulted (Adtor) && !is_deleted (Adtor)); +struct B { int a; union { int b; const int c; }; }; +static_assert (is_default_constructible_type (^^B)); +static_assert (is_trivially_default_constructible_type (^^B)); +static_assert (is_destructible_type (^^B)); +static_assert (is_trivially_destructible_type (^^B)); +constexpr auto Bctor = (members_of (^^B, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Bdtor = (members_of (^^B, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Bctor) && !is_deleted (Bctor)); +static_assert (is_defaulted (Bdtor) && !is_deleted (Bdtor)); +union C { const int a = 42; const long b; ~C (); }; +static_assert (is_default_constructible_type (^^C)); +static_assert (!is_trivially_default_constructible_type (^^C)); +static_assert (is_destructible_type (^^C)); +static_assert (!is_trivially_destructible_type (^^C)); +constexpr auto Cctor = (members_of (^^C, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Cdtor = (members_of (^^C, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Cctor) && !is_deleted (Cctor)); +static_assert (!is_defaulted (Cdtor) && !is_deleted (Cdtor)); +struct D { D () = delete; D (int); ~D () = default; }; +union E { D a = 42; D b; ~E (); }; +static_assert (is_default_constructible_type (^^E)); +static_assert (is_destructible_type (^^E)); +constexpr auto Ector = (members_of (^^E, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Edtor = (members_of (^^E, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Ector) && !is_deleted (Ector)); +static_assert (!is_defaulted (Edtor) && !is_deleted (Edtor)); +struct F { int a; union { D b = 42; D c; }; }; +static_assert (is_default_constructible_type (^^F)); +static_assert (is_destructible_type (^^F)); +constexpr auto Fctor = (members_of (^^F, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Fdtor = (members_of (^^F, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Fctor) && !is_deleted (Fctor)); +static_assert (is_defaulted (Fdtor) && !is_deleted (Fdtor)); +struct G { G (); ~G (); }; +union I { int a; const int b; ~I (); }; +static_assert (is_default_constructible_type (^^I)); +static_assert (!is_trivially_default_constructible_type (^^I)); +static_assert (is_destructible_type (^^I)); +static_assert (!is_trivially_destructible_type (^^I)); +constexpr auto Ictor = (members_of (^^I, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Idtor = (members_of (^^I, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Ictor) && !is_deleted (Ictor)); +static_assert (!is_defaulted (Idtor) && !is_deleted (Idtor)); +union J { D a; int b; }; +static_assert (is_default_constructible_type (^^J)); +static_assert (is_trivially_default_constructible_type (^^J)); +static_assert (is_destructible_type (^^J)); +static_assert (is_trivially_destructible_type (^^J)); +constexpr auto Jctor = (members_of (^^J, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Jdtor = (members_of (^^J, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Jctor) && !is_deleted (Jctor)); +static_assert (is_defaulted (Jdtor) && !is_deleted (Jdtor)); +union K { G a; int b; }; +static_assert (is_default_constructible_type (^^K)); +static_assert (is_trivially_default_constructible_type (^^K)); +static_assert (is_destructible_type (^^K)); +static_assert (is_trivially_destructible_type (^^K)); +constexpr auto Kctor = (members_of (^^K, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Kdtor = (members_of (^^K, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Kctor) && !is_deleted (Kctor)); +static_assert (is_defaulted (Kdtor) && !is_deleted (Kdtor)); +struct L { int a; union { G b; int c; }; }; +static_assert (is_default_constructible_type (^^L)); +static_assert (is_trivially_default_constructible_type (^^L)); +static_assert (is_destructible_type (^^L)); +static_assert (is_trivially_destructible_type (^^L)); +constexpr auto Lctor = (members_of (^^L, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Ldtor = (members_of (^^L, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Lctor) && !is_deleted (Lctor)); +static_assert (is_defaulted (Ldtor) && !is_deleted (Ldtor)); +union M { M (); int a; int b; }; +static_assert (is_default_constructible_type (^^M)); +static_assert (!is_trivially_default_constructible_type (^^M)); +static_assert (is_destructible_type (^^M)); +static_assert (is_trivially_destructible_type (^^M)); +constexpr auto Mctor = (members_of (^^M, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Mdtor = (members_of (^^M, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Mctor) && !is_deleted (Mctor)); +static_assert (is_defaulted (Mdtor) && !is_deleted (Mdtor)); +union N { N (); int a; G b; }; +static_assert (!is_default_constructible_type (^^N)); +static_assert (!is_trivially_default_constructible_type (^^N)); +static_assert (!is_destructible_type (^^N)); +static_assert (!is_trivially_destructible_type (^^N)); +constexpr auto Nctor = (members_of (^^N, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Ndtor = (members_of (^^N, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Nctor) && !is_deleted (Nctor)); +static_assert (is_defaulted (Ndtor) && is_deleted (Ndtor)); +struct O { O (); union { int a; int b; }; }; +static_assert (is_default_constructible_type (^^O)); +static_assert (!is_trivially_default_constructible_type (^^O)); +static_assert (is_destructible_type (^^O)); +static_assert (is_trivially_destructible_type (^^O)); +constexpr auto Octor = (members_of (^^O, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Odtor = (members_of (^^O, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Octor) && !is_deleted (Octor)); +static_assert (is_defaulted (Odtor) && !is_deleted (Odtor)); +struct P { P (); union { int a; G b; }; }; +static_assert (!is_default_constructible_type (^^P)); +static_assert (!is_trivially_default_constructible_type (^^P)); +static_assert (!is_destructible_type (^^P)); +static_assert (!is_trivially_destructible_type (^^P)); +constexpr auto Pctor = (members_of (^^P, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Pdtor = (members_of (^^P, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Pctor) && !is_deleted (Pctor)); +static_assert (is_defaulted (Pdtor) && is_deleted (Pdtor)); +struct Q { Q (int); union { int a; G b; }; }; +static_assert (!is_default_constructible_type (^^Q)); +static_assert (!is_trivially_default_constructible_type (^^Q)); +static_assert (!is_destructible_type (^^Q)); +static_assert (!is_trivially_destructible_type (^^Q)); +static_assert ((members_of (^^Q, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ()).size () == 0); +constexpr auto Qdtor = (members_of (^^Q, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Qdtor) && is_deleted (Qdtor)); +struct R { R () = default; R (int); union { int a; G b; }; }; +static_assert (!is_default_constructible_type (^^R)); +static_assert (!is_trivially_default_constructible_type (^^R)); +static_assert (!is_destructible_type (^^R)); +static_assert (!is_trivially_destructible_type (^^R)); +constexpr auto Rctor = (members_of (^^R, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Rdtor = (members_of (^^R, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Rctor) && !is_deleted (Rctor)); +static_assert (is_defaulted (Rdtor) && is_deleted (Rdtor)); +struct S { S (int); ~S (); }; +union T { T (); int a; int b = 42; }; +static_assert (is_default_constructible_type (^^T)); +static_assert (!is_trivially_default_constructible_type (^^T)); +static_assert (is_destructible_type (^^T)); +static_assert (is_trivially_destructible_type (^^T)); +constexpr auto Tctor = (members_of (^^T, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Tdtor = (members_of (^^T, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Tctor) && !is_deleted (Tctor)); +static_assert (is_defaulted (Tdtor) && !is_deleted (Tdtor)); +union U { U (); int a; S b = 42; }; +static_assert (!is_default_constructible_type (^^U)); +static_assert (!is_trivially_default_constructible_type (^^U)); +static_assert (!is_destructible_type (^^U)); +static_assert (!is_trivially_destructible_type (^^U)); +constexpr auto Uctor = (members_of (^^U, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Udtor = (members_of (^^U, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Uctor) && !is_deleted (Uctor)); +static_assert (is_defaulted (Udtor) && is_deleted (Udtor)); +struct V { V (); union { int a; int b = 42; }; }; +static_assert (is_default_constructible_type (^^V)); +static_assert (!is_trivially_default_constructible_type (^^V)); +static_assert (is_destructible_type (^^V)); +static_assert (is_trivially_destructible_type (^^V)); +constexpr auto Vctor = (members_of (^^V, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Vdtor = (members_of (^^V, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Vctor) && !is_deleted (Vctor)); +static_assert (is_defaulted (Vdtor) && !is_deleted (Vdtor)); +struct W { W (); union { int a; S b = 42; }; }; +static_assert (!is_default_constructible_type (^^W)); +static_assert (!is_trivially_default_constructible_type (^^W)); +static_assert (!is_destructible_type (^^W)); +static_assert (!is_trivially_destructible_type (^^W)); +constexpr auto Wctor = (members_of (^^W, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Wdtor = (members_of (^^W, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (Wctor) && !is_deleted (Wctor)); +static_assert (is_defaulted (Wdtor) && is_deleted (Wdtor)); +struct X { X (int); union { int a; S b = 42; }; }; +static_assert (!is_default_constructible_type (^^X)); +static_assert (!is_trivially_default_constructible_type (^^X)); +static_assert (!is_destructible_type (^^X)); +static_assert (!is_trivially_destructible_type (^^X)); +static_assert ((members_of (^^X, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ()).size () == 0); +constexpr auto Xdtor = (members_of (^^X, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Xdtor) && is_deleted (Xdtor)); +struct Y { Y () = default; Y (int); union { int a; S b = 42; }; }; +static_assert (!is_default_constructible_type (^^Y)); +static_assert (!is_trivially_default_constructible_type (^^Y)); +static_assert (!is_destructible_type (^^Y)); +static_assert (!is_trivially_destructible_type (^^Y)); +constexpr auto Yctor = (members_of (^^Y, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto Ydtor = (members_of (^^Y, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (is_defaulted (Yctor) && !is_deleted (Yctor)); +static_assert (is_defaulted (Ydtor) && is_deleted (Ydtor)); +union AA { AA (); int a; long b; }; +static_assert (is_default_constructible_type (^^AA)); +static_assert (!is_trivially_default_constructible_type (^^AA)); +static_assert (is_destructible_type (^^AA)); +static_assert (is_trivially_destructible_type (^^AA)); +constexpr auto AActor = (members_of (^^AA, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto AAdtor = (members_of (^^AA, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (AActor) && !is_deleted (AActor)); +static_assert (is_defaulted (AAdtor) && !is_deleted (AAdtor)); +struct AB { AB () = default; AB (const AB &) = default; int a; ~AB (); }; +union AC { AC (); int a; AB b; }; +static_assert (!is_default_constructible_type (^^AC)); +static_assert (!is_trivially_default_constructible_type (^^AC)); +static_assert (!is_destructible_type (^^AC)); +static_assert (!is_trivially_destructible_type (^^AC)); +constexpr auto ACctor = (members_of (^^AC, ctx) | std::views::filter (is_default_constructor) | std::ranges::to <std::vector> ())[0]; +constexpr auto ACdtor = (members_of (^^AC, ctx) | std::views::filter (is_destructor) | std::ranges::to <std::vector> ())[0]; +static_assert (!is_defaulted (ACctor) && !is_deleted (ACctor)); +static_assert (is_defaulted (ACdtor) && is_deleted (ACdtor)); diff --git a/gcc/testsuite/g++.dg/reflect/type_trait6.C b/gcc/testsuite/g++.dg/reflect/type_trait6.C index e1b466f3cb60..2f3527c14103 100644 --- a/gcc/testsuite/g++.dg/reflect/type_trait6.C +++ b/gcc/testsuite/g++.dg/reflect/type_trait6.C @@ -985,7 +985,7 @@ static_assert (!is_destructible_type (^^N2::Del [1])); static_assert (!is_destructible_type (^^const N2::Del [1])); static_assert (!is_destructible_type (^^N2::Del [])); static_assert (!is_destructible_type (^^const N2::Del [])); -static_assert (!is_destructible_type (^^N2::NontrivialUnion)); +static_assert (is_destructible_type (^^N2::NontrivialUnion)); static_assert (is_destructible_type (^^N2::UnusualCopy)); static_assert (is_trivially_default_constructible_type (^^int)); @@ -1367,8 +1367,8 @@ static_assert (!is_nothrow_destructible_type (^^N2::TD2)); static_assert (!is_nothrow_destructible_type (^^N2::Aggr2)); static_assert (!is_nothrow_destructible_type (^^N2::Aggr2 [1])); static_assert (!is_nothrow_destructible_type (^^N2::TD1 [1][2])); -static_assert (!is_nothrow_destructible_type (^^N2::Ut)); -static_assert (!is_nothrow_destructible_type (^^N2::Ut [3])); +static_assert (is_nothrow_destructible_type (^^N2::Ut)); +static_assert (is_nothrow_destructible_type (^^N2::Ut [3])); static_assert (!is_nothrow_destructible_type (^^N2::AbstractDelDtor)); static_assert (!is_nothrow_destructible_type (^^N2::Abstract2)); static_assert (!is_nothrow_destructible_type (^^N2::Abstract3));
