From a81072c78284d233c2ca7582235d0e01eda83082 Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Tue, 20 Oct 2015 22:24:08 -0400
Subject: [PATCH] re PR c++/66583 (incorrect implicitly-defined move
 constructor for class with anonymous union and NSDMI)

	PR c++/66583

	* init.c (innermost_aggr_scope): New.
	(build_field_list): Change uses_unions_p to uses_unions_or_anon_p.
	(sort_mem_initializers): Handle initializers for entire anonymous
	aggregates.

From-SVN: r229108
---
 gcc/cp/ChangeLog                         |  8 ++++
 gcc/cp/init.c                            | 55 ++++++++++++++----------
 gcc/testsuite/g++.dg/cpp0x/anon-union1.C | 27 ++++++++++++
 gcc/testsuite/g++.dg/ext/anon-struct7.C  | 28 ++++++++++++
 4 files changed, 95 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/anon-union1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/anon-struct7.C

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 63bb46f7e744..9f282d8352ec 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,11 @@
+2015-10-20  Jason Merrill  <jason@redhat.com>
+
+	PR c++/66583
+	* init.c (innermost_aggr_scope): New.
+	(build_field_list): Change uses_unions_p to uses_unions_or_anon_p.
+	(sort_mem_initializers): Handle initializers for entire anonymous
+	aggregates.
+
 2015-10-20  Marek Polacek  <polacek@redhat.com>
 
 	* parser.c (is_cilkplus_vector_p): Don't define here.
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 57a640683519..d89e1ac0b5a5 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -823,13 +823,13 @@ perform_member_init (tree member, tree init)
    the FIELD_DECLs on the TYPE_FIELDS list for T, in reverse order.  */
 
 static tree
-build_field_list (tree t, tree list, int *uses_unions_p)
+build_field_list (tree t, tree list, int *uses_unions_or_anon_p)
 {
   tree fields;
 
   /* Note whether or not T is a union.  */
   if (TREE_CODE (t) == UNION_TYPE)
-    *uses_unions_p = 1;
+    *uses_unions_or_anon_p = 1;
 
   for (fields = TYPE_FIELDS (t); fields; fields = DECL_CHAIN (fields))
     {
@@ -840,9 +840,6 @@ build_field_list (tree t, tree list, int *uses_unions_p)
 	continue;
 
       fieldtype = TREE_TYPE (fields);
-      /* Keep track of whether or not any fields are unions.  */
-      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
@@ -853,7 +850,8 @@ build_field_list (tree t, tree list, int *uses_unions_p)
 	     initialize the entire aggregate.  */
 	  list = tree_cons (fields, NULL_TREE, list);
 	  /* And now add the fields in the anonymous aggregate.  */
-	  list = build_field_list (fieldtype, list, uses_unions_p);
+	  list = build_field_list (fieldtype, list, uses_unions_or_anon_p);
+	  *uses_unions_or_anon_p = 1;
 	}
       /* Add this field.  */
       else if (DECL_NAME (fields))
@@ -863,6 +861,18 @@ build_field_list (tree t, tree list, int *uses_unions_p)
   return list;
 }
 
+/* Return the innermost aggregate scope for FIELD, whether that is
+   the enclosing class or an anonymous aggregate within it.  */
+
+static tree
+innermost_aggr_scope (tree field)
+{
+  if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
+    return TREE_TYPE (field);
+  else
+    return DECL_CONTEXT (field);
+}
+
 /* The MEM_INITS are a TREE_LIST.  The TREE_PURPOSE of each list gives
    a FIELD_DECL or BINFO in T that needs initialization.  The
    TREE_VALUE gives the initializer, or list of initializer arguments.
@@ -880,7 +890,7 @@ sort_mem_initializers (tree t, tree mem_inits)
   tree next_subobject;
   vec<tree, va_gc> *vbases;
   int i;
-  int uses_unions_p = 0;
+  int uses_unions_or_anon_p = 0;
 
   /* Build up a list of initializations.  The TREE_PURPOSE of entry
      will be the subobject (a FIELD_DECL or BINFO) to initialize.  The
@@ -900,7 +910,7 @@ sort_mem_initializers (tree t, tree mem_inits)
       sorted_inits = tree_cons (base_binfo, NULL_TREE, sorted_inits);
 
   /* Process the non-static data members.  */
-  sorted_inits = build_field_list (t, sorted_inits, &uses_unions_p);
+  sorted_inits = build_field_list (t, sorted_inits, &uses_unions_or_anon_p);
   /* Reverse the entire list of initializations, so that they are in
      the order that they will actually be performed.  */
   sorted_inits = nreverse (sorted_inits);
@@ -984,7 +994,7 @@ sort_mem_initializers (tree t, tree mem_inits)
      anonymous unions), the ctor-initializer is ill-formed.
 
      Here we also splice out uninitialized union members.  */
-  if (uses_unions_p)
+  if (uses_unions_or_anon_p)
     {
       tree *last_p = NULL;
       tree *p;
@@ -1001,21 +1011,18 @@ sort_mem_initializers (tree t, tree mem_inits)
 	  if (TREE_CODE (field) != FIELD_DECL)
 	    goto next;
 
-	  /* If this is an anonymous union with no explicit initializer,
+	  /* If this is an anonymous aggregate with no explicit initializer,
 	     splice it out.  */
-	  if (!TREE_VALUE (init) && ANON_UNION_TYPE_P (TREE_TYPE (field)))
+	  if (!TREE_VALUE (init) && ANON_AGGR_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.  */
-	  for (ctx = DECL_CONTEXT (field);
-	       !same_type_p (ctx, t);
-	       ctx = TYPE_CONTEXT (ctx))
-	    if (TREE_CODE (ctx) == UNION_TYPE
-		|| !ANON_AGGR_TYPE_P (ctx))
-	      break;
+	  ctx = innermost_aggr_scope (field);
+
 	  /* If this field is not a member of a union, skip it.  */
-	  if (TREE_CODE (ctx) != UNION_TYPE)
+	  if (TREE_CODE (ctx) != UNION_TYPE
+	      && !ANON_AGGR_TYPE_P (ctx))
 	    goto next;
 
 	  /* If this union member has no explicit initializer and no NSDMI,
@@ -1034,17 +1041,19 @@ sort_mem_initializers (tree t, tree mem_inits)
 	    }
 
 	  /* See if LAST_FIELD and the field initialized by INIT are
-	     members of the same union.  If so, there's a problem,
-	     unless they're actually members of the same structure
+	     members of the same union (or the union itself). If so, there's
+	     a problem, unless they're actually members of the same structure
 	     which is itself a member of a union.  For example, given:
 
 	       union { struct { int i; int j; }; };
 
 	     initializing both `i' and `j' makes sense.  */
-	  ctx = common_enclosing_class (DECL_CONTEXT (field),
-					DECL_CONTEXT (TREE_PURPOSE (*last_p)));
+	  ctx = common_enclosing_class
+	    (innermost_aggr_scope (field),
+	     innermost_aggr_scope (TREE_PURPOSE (*last_p)));
 
-	  if (ctx && TREE_CODE (ctx) == UNION_TYPE)
+	  if (ctx && (TREE_CODE (ctx) == UNION_TYPE
+		      || ctx == TREE_TYPE (TREE_PURPOSE (*last_p))))
 	    {
 	      /* A mem-initializer hides an NSDMI.  */
 	      if (TREE_VALUE (init) && !TREE_VALUE (*last_p))
diff --git a/gcc/testsuite/g++.dg/cpp0x/anon-union1.C b/gcc/testsuite/g++.dg/cpp0x/anon-union1.C
new file mode 100644
index 000000000000..19f6291211c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/anon-union1.C
@@ -0,0 +1,27 @@
+// PR c++/66583
+// { dg-do run { target c++11 } }
+
+template <class T>
+T&& move(T& t) { return static_cast<T&&>(t); }
+
+struct A {
+  A() { };
+  A(const A&) { }
+};
+
+struct B {
+  union {
+    int m_1 = 0;
+    int m_2;
+  };
+  A dummy;
+};
+
+int main()
+{
+  B b;
+  b.m_1 = 1;
+  B c = move(b);
+  if (c.m_1 != 1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/ext/anon-struct7.C b/gcc/testsuite/g++.dg/ext/anon-struct7.C
new file mode 100644
index 000000000000..6e47e34c7d4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/anon-struct7.C
@@ -0,0 +1,28 @@
+// PR c++/66583
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+template <class T>
+T&& move(T& t) { return static_cast<T&&>(t); }
+
+struct A {
+  A() { };
+  A(const A&) { }
+};
+
+struct B {
+  struct {
+    int m_1 = 0;
+    int m_2;
+  };
+  A dummy;
+};
+
+int main()
+{
+  B b;
+  b.m_1 = 1;
+  B c = move(b);
+  if (c.m_1 != 1)
+    __builtin_abort();
+}
-- 
GitLab