From: jason Date: Wed, 14 Jul 2010 17:00:51 +0000 (+0000) Subject: Implement C++0x unrestricted unions (N2544) X-Git-Tag: upstream/4.9.2~27890 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=80e5473297f3b9131a8e689f9a60714e486c72fb;p=platform%2Fupstream%2Flinaro-gcc.git Implement C++0x unrestricted unions (N2544) * class.c (check_field_decl): Loosen union handling in C++0x. * method.c (walk_field_subobs): Split out from... (synthesized_method_walk): ...here. Set msg before loops. (process_subob_fn): Check for triviality in union members. * init.c (sort_mem_initializers): Splice out uninitialized anonymous unions and union members. (push_base_cleanups): Don't automatically destroy anonymous unions and union members. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@162187 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 98c6220..05a4dfa 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,15 @@ +2010-07-14 Jason Merrill + + Implement C++0x unrestricted unions (N2544) + * class.c (check_field_decl): Loosen union handling in C++0x. + * method.c (walk_field_subobs): Split out from... + (synthesized_method_walk): ...here. Set msg before loops. + (process_subob_fn): Check for triviality in union members. + * init.c (sort_mem_initializers): Splice out uninitialized + anonymous unions and union members. + (push_base_cleanups): Don't automatically destroy anonymous unions + and union members. + 2010-07-13 Jason Merrill PR c++/44909 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index ed7367c..a572af8 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -2864,9 +2864,9 @@ check_field_decl (tree field, { tree type = strip_array_types (TREE_TYPE (field)); - /* An anonymous union cannot contain any fields which would change + /* In C++98 an anonymous union cannot contain any fields which would change the settings of CANT_HAVE_CONST_CTOR and friends. */ - if (ANON_UNION_TYPE_P (type)) + if (ANON_UNION_TYPE_P (type) && cxx_dialect < cxx0x) ; /* And, we don't set TYPE_HAS_CONST_COPY_CTOR, etc., for anonymous structs. So, we recurse through their fields here. */ @@ -2888,8 +2888,10 @@ check_field_decl (tree field, make it through without complaint. */ abstract_virtuals_error (field, type); - if (TREE_CODE (t) == UNION_TYPE) + if (TREE_CODE (t) == UNION_TYPE && cxx_dialect < cxx0x) { + static bool warned; + int oldcount = errorcount; if (TYPE_NEEDS_CONSTRUCTING (type)) error ("member %q+#D with constructor not allowed in union", field); @@ -2898,8 +2900,12 @@ check_field_decl (tree field, if (TYPE_HAS_COMPLEX_COPY_ASSIGN (type)) error ("member %q+#D with copy assignment operator not allowed in union", field); - /* Don't bother diagnosing move assop now; C++0x has more - flexible unions. */ + if (!warned && errorcount > oldcount) + { + inform (DECL_SOURCE_LOCATION (field), "unrestricted unions " + "only available with -std=c++0x or -std=gnu++0x"); + warned = true; + } } else { diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 20f921d..4e7cab3 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -708,23 +708,34 @@ sort_mem_initializers (tree t, tree mem_inits) If a ctor-initializer specifies more than one mem-initializer for multiple members of the same union (including members of - anonymous unions), the ctor-initializer is ill-formed. */ + anonymous unions), the ctor-initializer is ill-formed. + + Here we also splice out uninitialized union members. */ if (uses_unions_p) { tree last_field = NULL_TREE; - for (init = sorted_inits; init; init = TREE_CHAIN (init)) + tree *p; + for (p = &sorted_inits; *p; ) { tree field; tree field_type; int done; - /* Skip uninitialized members and base classes. */ - if (!TREE_VALUE (init) - || TREE_CODE (TREE_PURPOSE (init)) != FIELD_DECL) - continue; + init = *p; + + field = TREE_PURPOSE (init); + + /* Skip base classes. */ + if (TREE_CODE (field) != FIELD_DECL) + goto next; + + /* If this is an anonymous union with no explicit initializer, + splice it out. */ + if (!TREE_VALUE (init) && ANON_UNION_TYPE_P (TREE_TYPE (field))) + goto splice; + /* See if this field is a member of a union, or a member of a structure contained in a union, etc. */ - field = TREE_PURPOSE (init); for (field_type = DECL_CONTEXT (field); !same_type_p (field_type, t); field_type = TYPE_CONTEXT (field_type)) @@ -732,14 +743,19 @@ sort_mem_initializers (tree t, tree mem_inits) break; /* If this field is not a member of a union, skip it. */ if (TREE_CODE (field_type) != UNION_TYPE) - continue; + goto next; + + /* If this union member has no explicit initializer, splice + it out. */ + if (!TREE_VALUE (init)) + goto splice; /* It's only an error if we have two initializers for the same union type. */ if (!last_field) { last_field = field; - continue; + goto next; } /* See if LAST_FIELD and the field initialized by INIT are @@ -785,6 +801,13 @@ sort_mem_initializers (tree t, tree mem_inits) while (!done); last_field = field; + + next: + p = &TREE_CHAIN (*p); + continue; + splice: + *p = TREE_CHAIN (*p); + continue; } } @@ -3353,21 +3376,27 @@ push_base_cleanups (void) finish_decl_cleanup (NULL_TREE, expr); } + /* Don't automatically destroy union members. */ + if (TREE_CODE (current_class_type) == UNION_TYPE) + return; + for (member = TYPE_FIELDS (current_class_type); member; member = TREE_CHAIN (member)) { - if (TREE_TYPE (member) == error_mark_node + tree this_type = TREE_TYPE (member); + if (this_type == error_mark_node || TREE_CODE (member) != FIELD_DECL || DECL_ARTIFICIAL (member)) continue; - if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (member))) + if (ANON_UNION_TYPE_P (this_type)) + continue; + if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (this_type)) { tree this_member = (build_class_member_access_expr (current_class_ref, member, /*access_path=*/NULL_TREE, /*preserve_reference=*/false, tf_warning_or_error)); - tree this_type = TREE_TYPE (member); expr = build_delete (this_type, this_member, sfk_complete_destructor, LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_NORMAL, diff --git a/gcc/cp/method.c b/gcc/cp/method.c index b09064b..9876af2 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -545,7 +545,8 @@ do_build_copy_constructor (tree fndecl) } else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type)) /* Just use the field; anonymous types can't have - nontrivial copy ctors or assignment ops. */; + nontrivial copy ctors or assignment ops or this + function would be deleted. */; else continue; @@ -663,7 +664,8 @@ do_build_copy_assign (tree fndecl) else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type) != NULL_TREE) /* Just use the field; anonymous types can't have - nontrivial copy ctors or assignment ops. */; + nontrivial copy ctors or assignment ops or this + function would be deleted. */; else continue; @@ -912,8 +914,19 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, *spec_p = merge_exception_specifiers (*spec_p, raises); } - if (trivial_p && !trivial_fn_p (fn)) - *trivial_p = false; + if (!trivial_fn_p (fn)) + { + if (trivial_p) + *trivial_p = false; + if (TREE_CODE (arg) == FIELD_DECL + && TREE_CODE (DECL_CONTEXT (arg)) == UNION_TYPE) + { + if (deleted_p) + *deleted_p = true; + if (msg) + error ("union member %q+D with non-trivial %qD", arg, fn); + } + } if (move_p && !move_fn_p (fn) && !trivial_fn_p (fn)) { @@ -929,6 +942,99 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p, *deleted_p = true; } +/* Subroutine of synthesized_method_walk to allow recursion into anonymous + aggregates. */ + +static void +walk_field_subobs (tree fields, tree fnname, special_function_kind sfk, + int quals, bool copy_arg_p, bool move_p, + bool assign_p, tree *spec_p, bool *trivial_p, + bool *deleted_p, const char *msg, + int flags, tsubst_flags_t complain) +{ + tree field; + for (field = fields; field; field = TREE_CHAIN (field)) + { + tree mem_type, argtype, rval; + + if (TREE_CODE (field) != FIELD_DECL + || DECL_ARTIFICIAL (field)) + continue; + + mem_type = strip_array_types (TREE_TYPE (field)); + if (assign_p) + { + bool bad = true; + if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type)) + { + if (msg) + error ("non-static const member %q#D, can't use default " + "assignment operator", field); + } + else if (TREE_CODE (mem_type) == REFERENCE_TYPE) + { + if (msg) + error ("non-static reference member %q#D, can't use " + "default assignment operator", field); + } + else + bad = false; + + if (bad && deleted_p) + *deleted_p = true; + } + else if (sfk == sfk_constructor) + { + bool bad = true; + if (CP_TYPE_CONST_P (mem_type) + && (!CLASS_TYPE_P (mem_type) + || !type_has_user_provided_default_constructor (mem_type))) + { + if (msg) + error ("uninitialized non-static const member %q#D", + field); + } + else if (TREE_CODE (mem_type) == REFERENCE_TYPE) + { + if (msg) + error ("uninitialized non-static reference member %q#D", + field); + } + else + bad = false; + + if (bad && deleted_p) + *deleted_p = true; + } + + if (!CLASS_TYPE_P (mem_type)) + continue; + + if (ANON_AGGR_TYPE_P (mem_type)) + { + walk_field_subobs (TYPE_FIELDS (mem_type), fnname, sfk, quals, + copy_arg_p, move_p, assign_p, spec_p, trivial_p, + deleted_p, msg, flags, complain); + continue; + } + + if (copy_arg_p) + { + int mem_quals = cp_type_quals (mem_type) | quals; + if (DECL_MUTABLE_P (field)) + mem_quals &= ~TYPE_QUAL_CONST; + argtype = build_stub_type (mem_type, mem_quals, move_p); + } + else + argtype = NULL_TREE; + + rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); + + process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, + msg, field); + } +} + /* The caller wants to generate an implicit declaration of SFK for CTYPE which is const if relevant and CONST_P is set. If spec_p, trivial_p and deleted_p are non-null, set their referent appropriately. If diag is @@ -940,7 +1046,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, tree *spec_p, bool *trivial_p, bool *deleted_p, bool diag) { - tree binfo, base_binfo, field, scope, fnname, rval, argtype; + tree binfo, base_binfo, scope, fnname, rval, argtype; bool move_p, copy_arg_p, assign_p, expected_trivial, check_vdtor; VEC(tree,gc) *vbases; int i, quals, flags; @@ -1052,6 +1158,15 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, quals = TYPE_UNQUALIFIED; argtype = NULL_TREE; + if (!diag) + msg = NULL; + else if (assign_p) + msg = ("base %qT does not have a move assignment operator or trivial " + "copy assignment operator"); + else + msg = ("base %qT does not have a move constructor or trivial " + "copy constructor"); + for (binfo = TYPE_BINFO (ctype), i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) { @@ -1060,15 +1175,6 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, argtype = build_stub_type (basetype, quals, move_p); rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); - if (!diag) - msg = NULL; - else if (assign_p) - msg = ("base %qT does not have a move assignment operator or trivial " - "copy assignment operator"); - else - msg = ("base %qT does not have a move constructor or trivial " - "copy constructor"); - process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, msg, BINFO_TYPE (base_binfo)); @@ -1094,105 +1200,31 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, *deleted_p = true; } else if (!assign_p) - for (i = 0; VEC_iterate (tree, vbases, i, base_binfo); ++i) - { - if (copy_arg_p) - argtype = build_stub_type (BINFO_TYPE (base_binfo), quals, move_p); - rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); - - if (!diag) - msg = NULL; - else if (assign_p) - msg = ("virtual base %qT does not have a move assignment " - "operator or trivial copy assignment operator"); - else - msg = ("virtual base %qT does not have a move constructor " - "or trivial copy constructor"); - - process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, - msg, BINFO_TYPE (base_binfo)); - } - - for (field = TYPE_FIELDS (ctype); field; field = TREE_CHAIN (field)) { - tree mem_type; - - if (TREE_CODE (field) != FIELD_DECL - || DECL_ARTIFICIAL (field)) - continue; - - mem_type = strip_array_types (TREE_TYPE (field)); - if (assign_p) + if (diag) + msg = ("virtual base %qT does not have a move constructor " + "or trivial copy constructor"); + for (i = 0; VEC_iterate (tree, vbases, i, base_binfo); ++i) { - bool bad = true; - if (CP_TYPE_CONST_P (mem_type) && !CLASS_TYPE_P (mem_type)) - { - if (diag) - error ("non-static const member %q#D, can't use default " - "assignment operator", field); - } - else if (TREE_CODE (mem_type) == REFERENCE_TYPE) - { - if (diag) - error ("non-static reference member %q#D, can't use " - "default assignment operator", field); - } - else - bad = false; + if (copy_arg_p) + argtype = build_stub_type (BINFO_TYPE (base_binfo), quals, move_p); + rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain); - if (bad && deleted_p) - *deleted_p = true; + process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, + msg, BINFO_TYPE (base_binfo)); } - else if (sfk == sfk_constructor) - { - bool bad = true; - if (CP_TYPE_CONST_P (mem_type) - && (!CLASS_TYPE_P (mem_type) - || !type_has_user_provided_default_constructor (mem_type))) - { - if (diag) - error ("uninitialized non-static const member %q#D", - field); - } - else if (TREE_CODE (mem_type) == REFERENCE_TYPE) - { - if (diag) - error ("uninitialized non-static reference member %q#D", - field); - } - else - bad = false; - - if (bad && deleted_p) - *deleted_p = true; - } - - if (!CLASS_TYPE_P (mem_type) - || ANON_AGGR_TYPE_P (mem_type)) - continue; - - if (copy_arg_p) - { - int mem_quals = cp_type_quals (mem_type) | quals; - if (DECL_MUTABLE_P (field)) - mem_quals &= ~TYPE_QUAL_CONST; - argtype = build_stub_type (mem_type, mem_quals, move_p); - } - - rval = locate_fn_flags (mem_type, fnname, argtype, flags, complain); - - if (!diag) - msg = NULL; - else if (assign_p) - msg = ("non-static data member %qD does not have a move " - "assignment operator or trivial copy assignment operator"); - else - msg = ("non-static data member %qD does not have a move " - "constructor or trivial copy constructor"); - - process_subob_fn (rval, move_p, spec_p, trivial_p, deleted_p, - msg, field); } + if (!diag) + /* Leave msg null. */; + else if (assign_p) + msg = ("non-static data member %qD does not have a move " + "assignment operator or trivial copy assignment operator"); + else + msg = ("non-static data member %qD does not have a move " + "constructor or trivial copy constructor"); + walk_field_subobs (TYPE_FIELDS (ctype), fnname, sfk, quals, + copy_arg_p, move_p, assign_p, spec_p, trivial_p, + deleted_p, msg, flags, complain); pop_scope (scope); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4478559..6b878c5 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2010-07-14 Jason Merrill + + Implement C++0x unrestricted unions (N2544) + * g++.dg/cpp0x/union1.C: New. + * g++.dg/cpp0x/union2.C: New. + * g++.dg/cpp0x/union3.C: New. + * g++.dg/cpp0x/defaulted2.C: Adjust. + * g++.old-deja/g++.bugs/900121_02.C: Adjust. + * g++.old-deja/g++.ext/anon2.C: Adjust. + * g++.old-deja/g++.mike/misc6.C: Adjust. + 2010-07-14 Janus Weil PR fortran/44925 diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted2.C b/gcc/testsuite/g++.dg/cpp0x/defaulted2.C index 909ebc5..1f400f4 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted2.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted2.C @@ -54,7 +54,7 @@ G::G() = default; union U { - G g; // { dg-error "constructor" } + G g; // { dg-error "union member.*non-trivial" } }; int main() @@ -62,5 +62,7 @@ int main() F f; F f2(f); // { dg-error "use" } B* b = new const B; // { dg-error "uninitialized const" } + U u; // { dg-error "deleted" } } +// { dg-prune-output "implicitly deleted because" } diff --git a/gcc/testsuite/g++.dg/cpp0x/union1.C b/gcc/testsuite/g++.dg/cpp0x/union1.C new file mode 100644 index 0000000..291853d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/union1.C @@ -0,0 +1,34 @@ +// Negative test for C++0x unrestricted unions +// { dg-options -std=c++0x } +// { dg-prune-output "implicitly deleted because" } + +struct A +{ + A(); + A(const A&); + ~A(); +}; + +union B +{ + A a; // { dg-error "union member" } +}; + +B b; // { dg-error "B::B\\(\\)" } +B b2(b); // { dg-error "B::B\\(const B&\\)" } + +struct C +{ + union + { + A a; // { dg-error "union member" } + }; +}; + +C c; // { dg-error "C::C\\(\\)" } +C c2(c); // { dg-error "C::C\\(const C&\\)" } + +// { dg-error "B::~B" "" { target *-*-* } 17 } +// { dg-error "B::~B" "" { target *-*-* } 18 } +// { dg-error "C::~C" "" { target *-*-* } 28 } +// { dg-error "C::~C" "" { target *-*-* } 29 } diff --git a/gcc/testsuite/g++.dg/cpp0x/union2.C b/gcc/testsuite/g++.dg/cpp0x/union2.C new file mode 100644 index 0000000..4f193e2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/union2.C @@ -0,0 +1,34 @@ +// Positive test for C++0x unrestricted unions +// { dg-options -std=c++0x } + +struct A +{ + A(); + A(const A&); + ~A(); +}; + +union B +{ + A a; + B(); + B(const B&); + ~B(); +}; + +B b; +B b2(b); + +struct C +{ + union + { + A a; + }; + C(); + C(const C&); + ~C(); +}; + +C c; +C c2(c); diff --git a/gcc/testsuite/g++.dg/cpp0x/union3.C b/gcc/testsuite/g++.dg/cpp0x/union3.C new file mode 100644 index 0000000..f1e8ddb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/union3.C @@ -0,0 +1,69 @@ +// Runtime test for C++0x unrestricted unions +// { dg-options -std=c++0x } +// { dg-do run } + +int c, d; +struct A +{ + int i; + A(): i(1) { ++c; } + A(const A&): i(2) { ++c; } + ~A() { ++d; } +}; + +union B +{ + A a; + B() { } + B(const B& b) { } + ~B() { } +}; + +struct C +{ + union { A a; }; + C() { } + C(const C&) { } + ~C() { } +}; + +union D +{ + A a; + D(): a() { } + D(const D& d): a(d.a) { } + ~D() { a.~A(); } +}; + +struct E +{ + union { A a; }; + E(): a() { } + E(const E& e): a (e.a) { } + ~E() { a.~A(); } +}; + +int main() +{ + { + B b1; + B b2(b1); + + C c1; + C c2(c1); + } + + if (c != 0 || d != 0) + return c+d*10; + + { + D d1; + D d2(d1); + + E e1; + E e2(e1); + } + + if (c != 4 || d != 4) + return c*100+d*1000; +} diff --git a/gcc/testsuite/g++.old-deja/g++.bugs/900121_02.C b/gcc/testsuite/g++.old-deja/g++.bugs/900121_02.C index cb31366..1a96e8e 100644 --- a/gcc/testsuite/g++.old-deja/g++.bugs/900121_02.C +++ b/gcc/testsuite/g++.old-deja/g++.bugs/900121_02.C @@ -1,4 +1,6 @@ // { dg-do assemble } +// { dg-prune-output "note" } + // g++ 1.36.1 bug 900121_02 // Assignment of structs is defined as memberwise assignment, diff --git a/gcc/testsuite/g++.old-deja/g++.ext/anon2.C b/gcc/testsuite/g++.old-deja/g++.ext/anon2.C index 6cf16bf..5bde08d 100644 --- a/gcc/testsuite/g++.old-deja/g++.ext/anon2.C +++ b/gcc/testsuite/g++.old-deja/g++.ext/anon2.C @@ -1,5 +1,6 @@ // { dg-do assemble } // { dg-options "" } +// { dg-prune-output "note" } struct S { diff --git a/gcc/testsuite/g++.old-deja/g++.mike/misc6.C b/gcc/testsuite/g++.old-deja/g++.mike/misc6.C index 5fbca65..1a07c12 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/misc6.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/misc6.C @@ -1,4 +1,6 @@ // { dg-do assemble } +// { dg-prune-output "note" } + // GROUPS uncaught // Cfront bug A.4 (See Language System Release Notes for the // SPARCompiler C++ version 3.0)