Implement C++0x unrestricted unions (N2544)
authorjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 14 Jul 2010 17:00:51 +0000 (17:00 +0000)
committerjason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 14 Jul 2010 17:00:51 +0000 (17:00 +0000)
* 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

12 files changed:
gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/init.c
gcc/cp/method.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/defaulted2.C
gcc/testsuite/g++.dg/cpp0x/union1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/union2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/union3.C [new file with mode: 0644]
gcc/testsuite/g++.old-deja/g++.bugs/900121_02.C
gcc/testsuite/g++.old-deja/g++.ext/anon2.C
gcc/testsuite/g++.old-deja/g++.mike/misc6.C

index 98c6220..05a4dfa 100644 (file)
@@ -1,3 +1,15 @@
+2010-07-14  Jason Merrill  <jason@redhat.com>
+
+       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  <jason@redhat.com>
 
        PR c++/44909
index ed7367c..a572af8 100644 (file)
@@ -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
        {
index 20f921d..4e7cab3 100644 (file)
@@ -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,
index b09064b..9876af2 100644 (file)
@@ -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);
 
index 4478559..6b878c5 100644 (file)
@@ -1,3 +1,14 @@
+2010-07-14  Jason Merrill  <jason@redhat.com>
+
+       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  <janus@gcc.gnu.org>
 
        PR fortran/44925
index 909ebc5..1f400f4 100644 (file)
@@ -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 (file)
index 0000000..291853d
--- /dev/null
@@ -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 (file)
index 0000000..4f193e2
--- /dev/null
@@ -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 (file)
index 0000000..f1e8ddb
--- /dev/null
@@ -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;
+}
index cb31366..1a96e8e 100644 (file)
@@ -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,
index 6cf16bf..5bde08d 100644 (file)
@@ -1,5 +1,6 @@
 // { dg-do assemble  }
 // { dg-options "" }
+// { dg-prune-output "note" }
 
 struct S 
 {
index 5fbca65..1a07c12 100644 (file)
@@ -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)