Patchwork C++0x PATCH to implement unrestricted unions

login
register
mail settings
Submitter Jason Merrill
Date July 14, 2010, 4:59 p.m.
Message ID <4C3DECF7.1080506@redhat.com>
Download mbox | patch
Permalink /patch/58916/
State New
Headers show

Comments

Jason Merrill - July 14, 2010, 4:59 p.m.
After my previous work to implement implicitly deleted functions, it was 
pretty simple to implement unrestricted unions, a C++ feature whereby 
unions are now allowed to contain members with non-trivial constructors, 
assignment ops and destructors, but having such makes the corresponding 
ones for the enclosing union deleted.

The second patch is just some minor code tidying in init.c.

Tested x86_64-pc-linux-gnu, applied to trunk.

Patch

commit 2d88908320f5b5f9c05b7b80eafff10d0787159a
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jul 14 10:50:10 2010 -0400

    	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.

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/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)

commit ad3591fdab5126192cedb9f101f9c3fdf190dccd
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jul 14 11:55:25 2010 -0400

    	* init.c (sort_mem_initializers): Rename "field_type" to "ctx".
    	(build_field_list): Cache field type.

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 4e7cab3..98a45cd 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -564,25 +564,27 @@  build_field_list (tree t, tree list, int *uses_unions_p)
 
   for (fields = TYPE_FIELDS (t); fields; fields = TREE_CHAIN (fields))
     {
+      tree fieldtype;
+
       /* Skip CONST_DECLs for enumeration constants and so forth.  */
       if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields))
 	continue;
 
+      fieldtype = TREE_TYPE (fields);
       /* Keep track of whether or not any fields are unions.  */
-      if (TREE_CODE (TREE_TYPE (fields)) == UNION_TYPE)
+      if (TREE_CODE (fieldtype) == UNION_TYPE)
 	*uses_unions_p = 1;
 
       /* For an anonymous struct or union, we must recursively
 	 consider the fields of the anonymous type.  They can be
 	 directly initialized from the constructor.  */
-      if (ANON_AGGR_TYPE_P (TREE_TYPE (fields)))
+      if (ANON_AGGR_TYPE_P (fieldtype))
 	{
 	  /* Add this field itself.  Synthesized copy constructors
 	     initialize the entire aggregate.  */
 	  list = tree_cons (fields, NULL_TREE, list);
 	  /* And now add the fields in the anonymous aggregate.  */
-	  list = build_field_list (TREE_TYPE (fields), list,
-				   uses_unions_p);
+	  list = build_field_list (fieldtype, list, uses_unions_p);
 	}
       /* Add this field.  */
       else if (DECL_NAME (fields))
@@ -718,7 +720,7 @@  sort_mem_initializers (tree t, tree mem_inits)
       for (p = &sorted_inits; *p; )
 	{
 	  tree field;
-	  tree field_type;
+	  tree ctx;
 	  int done;
 
 	  init = *p;
@@ -736,13 +738,13 @@  sort_mem_initializers (tree t, tree mem_inits)
 
 	  /* See if this field is a member of a union, or a member of a
 	     structure contained in a union, etc.  */
-	  for (field_type = DECL_CONTEXT (field);
-	       !same_type_p (field_type, t);
-	       field_type = TYPE_CONTEXT (field_type))
-	    if (TREE_CODE (field_type) == UNION_TYPE)
+	  for (ctx = DECL_CONTEXT (field);
+	       !same_type_p (ctx, t);
+	       ctx = TYPE_CONTEXT (ctx))
+	    if (TREE_CODE (ctx) == UNION_TYPE)
 	      break;
 	  /* If this field is not a member of a union, skip it.  */
-	  if (TREE_CODE (field_type) != UNION_TYPE)
+	  if (TREE_CODE (ctx) != UNION_TYPE)
 	    goto next;
 
 	  /* If this union member has no explicit initializer, splice
@@ -766,37 +768,37 @@  sort_mem_initializers (tree t, tree mem_inits)
 	       union { struct { int i; int j; }; };
 
 	     initializing both `i' and `j' makes sense.  */
-	  field_type = DECL_CONTEXT (field);
+	  ctx = DECL_CONTEXT (field);
 	  done = 0;
 	  do
 	    {
-	      tree last_field_type;
+	      tree last_ctx;
 
-	      last_field_type = DECL_CONTEXT (last_field);
+	      last_ctx = DECL_CONTEXT (last_field);
 	      while (1)
 		{
-		  if (same_type_p (last_field_type, field_type))
+		  if (same_type_p (last_ctx, ctx))
 		    {
-		      if (TREE_CODE (field_type) == UNION_TYPE)
+		      if (TREE_CODE (ctx) == UNION_TYPE)
 			error_at (DECL_SOURCE_LOCATION (current_function_decl),
 				  "initializations for multiple members of %qT",
-				  last_field_type);
+				  last_ctx);
 		      done = 1;
 		      break;
 		    }
 
-		  if (same_type_p (last_field_type, t))
+		  if (same_type_p (last_ctx, t))
 		    break;
 
-		  last_field_type = TYPE_CONTEXT (last_field_type);
+		  last_ctx = TYPE_CONTEXT (last_ctx);
 		}
 
 	      /* If we've reached the outermost class, then we're
 		 done.  */
-	      if (same_type_p (field_type, t))
+	      if (same_type_p (ctx, t))
 		break;
 
-	      field_type = TYPE_CONTEXT (field_type);
+	      ctx = TYPE_CONTEXT (ctx);
 	    }
 	  while (!done);