sort_mem_initializers was skipping over all fields without a
mem-initializer, even ones with an NSDMI.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 25b01320a7aefae42d9e14f3807247ca8f88ae40
Author: Jason Merrill <ja...@redhat.com>
Date: Sat May 25 17:28:15 2013 -0400
PR c++/52377
* class.c (common_enclosing_class): New.
* cp-tree.h: Declare it.
* init.c (sort_mem_initializers): Don't splice out a union member
with an NSDMI.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 90b31bf..64918c6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -9261,4 +9261,30 @@ publicly_uniquely_derived_p (tree parent, tree type)
return base && base != error_mark_node;
}
+/* CTX1 and CTX2 are declaration contexts. Return the innermost common
+ class between them, if any. */
+
+tree
+common_enclosing_class (tree ctx1, tree ctx2)
+{
+ if (!TYPE_P (ctx1) || !TYPE_P (ctx2))
+ return NULL_TREE;
+ gcc_assert (ctx1 == TYPE_MAIN_VARIANT (ctx1)
+ && ctx2 == TYPE_MAIN_VARIANT (ctx2));
+ if (ctx1 == ctx2)
+ return ctx1;
+ for (tree t = ctx1; TYPE_P (t); t = TYPE_CONTEXT (t))
+ TYPE_MARKED_P (t) = true;
+ tree found = NULL_TREE;
+ for (tree t = ctx2; TYPE_P (t); t = TYPE_CONTEXT (t))
+ if (TYPE_MARKED_P (t))
+ {
+ found = t;
+ break;
+ }
+ for (tree t = ctx1; TYPE_P (t); t = TYPE_CONTEXT (t))
+ TYPE_MARKED_P (t) = false;
+ return found;
+}
+
#include "gt-cp-class.h"
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1f85ca7..aebc440 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5112,6 +5112,7 @@ extern void deduce_noexcept_on_destructor (tree);
extern void insert_late_enum_def_into_classtype_sorted_fields (tree, tree);
extern bool uniquely_derived_from_p (tree, tree);
extern bool publicly_uniquely_derived_p (tree, tree);
+extern tree common_enclosing_class (tree, tree);
/* in cvt.c */
extern tree convert_to_reference (tree, tree, int, int, tree,
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 671c774..4edd150 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -914,13 +914,12 @@ sort_mem_initializers (tree t, tree mem_inits)
Here we also splice out uninitialized union members. */
if (uses_unions_p)
{
- tree last_field = NULL_TREE;
+ tree *last_p = NULL;
tree *p;
for (p = &sorted_inits; *p; )
{
tree field;
tree ctx;
- int done;
init = *p;
@@ -940,22 +939,25 @@ sort_mem_initializers (tree t, tree mem_inits)
for (ctx = DECL_CONTEXT (field);
!same_type_p (ctx, t);
ctx = TYPE_CONTEXT (ctx))
- if (TREE_CODE (ctx) == UNION_TYPE)
+ if (TREE_CODE (ctx) == UNION_TYPE
+ || !ANON_AGGR_TYPE_P (ctx))
break;
/* If this field is not a member of a union, skip it. */
if (TREE_CODE (ctx) != UNION_TYPE)
goto next;
- /* If this union member has no explicit initializer, splice
- it out. */
- if (!TREE_VALUE (init))
+ /* If this union member has no explicit initializer and no NSDMI,
+ splice it out. */
+ if (TREE_VALUE (init) || DECL_INITIAL (field))
+ /* OK. */;
+ else
goto splice;
/* It's only an error if we have two initializers for the same
union type. */
- if (!last_field)
+ if (!last_p)
{
- last_field = field;
+ last_p = p;
goto next;
}
@@ -967,41 +969,23 @@ sort_mem_initializers (tree t, tree mem_inits)
union { struct { int i; int j; }; };
initializing both `i' and `j' makes sense. */
- ctx = DECL_CONTEXT (field);
- done = 0;
- do
+ ctx = common_enclosing_class (DECL_CONTEXT (field),
+ DECL_CONTEXT (TREE_PURPOSE (*last_p)));
+
+ if (ctx && TREE_CODE (ctx) == UNION_TYPE)
{
- tree last_ctx;
-
- last_ctx = DECL_CONTEXT (last_field);
- while (1)
- {
- if (same_type_p (last_ctx, ctx))
- {
- if (TREE_CODE (ctx) == UNION_TYPE)
- error_at (DECL_SOURCE_LOCATION (current_function_decl),
- "initializations for multiple members of %qT",
- last_ctx);
- done = 1;
- break;
- }
-
- if (same_type_p (last_ctx, t))
- break;
-
- last_ctx = TYPE_CONTEXT (last_ctx);
- }
-
- /* If we've reached the outermost class, then we're
- done. */
- if (same_type_p (ctx, t))
- break;
-
- ctx = TYPE_CONTEXT (ctx);
+ /* A mem-initializer hides an NSDMI. */
+ if (TREE_VALUE (init) && !TREE_VALUE (*last_p))
+ *last_p = TREE_CHAIN (*last_p);
+ else if (TREE_VALUE (*last_p) && !TREE_VALUE (init))
+ goto splice;
+ else
+ error_at (DECL_SOURCE_LOCATION (current_function_decl),
+ "initializations for multiple members of %qT",
+ ctx);
}
- while (!done);
- last_field = field;
+ last_p = p;
next:
p = &TREE_CHAIN (*p);
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C
new file mode 100644
index 0000000..11bdd88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C
@@ -0,0 +1,25 @@
+// PR c++/52377
+// { dg-do run { target c++11 } }
+
+union Test
+{
+ int a{4};
+};
+
+union B
+{
+ int i = 42;
+ double d;
+ B() = default;
+ B(double d): d(d) { }
+};
+
+int main()
+{
+ Test t;
+ B b;
+ B b2(4.2);
+
+ if (t.a != 4 || b.i != 42 || b2.d != 4.2)
+ __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-union2.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union2.C
new file mode 100644
index 0000000..b45fe83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union2.C
@@ -0,0 +1,18 @@
+// PR c++/52377
+// { dg-do run { target c++11 } }
+
+union A // { dg-error "multiple" }
+{
+ int i = 4;
+ int j = 2;
+};
+
+A a;
+
+union B
+{
+ int i,j;
+ B(): i(1), j(2) {} // { dg-error "multiple" }
+};
+
+B b;