When we're building up a CONSTRUCTOR to represent the initialization
done by a constexpr constructor, initialization of a member of an
anonymous union shows up as an assignment to a COMPONENT_REF of the main
class object. We need to turn this into a CONSTRUCTOR for the anonymous
union object itself. This is complicated by the possibility of
arbitrarily nested anonymous unions, and also by anonymous structures
which are not part of C++, but are supported by G++ for C compatibility.
This is a moderately large chunk of code, but it is only hit in cases
that were previously completely broken.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 0d4d1658d66493525f78d8cc67a295219ac6925d
Author: paolo <paolo@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Tue Oct 23 23:43:21 2012 +0000
PR c++/54922
* semantics.c (build_anon_member_initialization): New.
(build_data_member_initialization): Use it.
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 95158a5..28b4b79 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5815,6 +5815,59 @@ is_valid_constexpr_fn (tree fun, bool complain)
return ret;
}
+/* Subroutine of build_data_member_initialization. MEMBER is a COMPONENT_REF
+ for a member of an anonymous aggregate, INIT is the initializer for that
+ member, and VEC_OUTER is the vector of constructor elements for the class
+ whose constructor we are processing. Add the initializer to the vector
+ and return true to indicate success. */
+
+static bool
+build_anon_member_initialization (tree member, tree init,
+ vec<constructor_elt, va_gc> **vec_outer)
+{
+ /* MEMBER presents the relevant fields from the inside out, but we need
+ to build up the initializer from the outside in so that we can reuse
+ previously built CONSTRUCTORs if this is, say, the second field in an
+ anonymous struct. So we use a vec as a stack. */
+ vec<tree> fields;
+ fields.create (2);
+ do
+ {
+ fields.safe_push (TREE_OPERAND (member, 1));
+ member = TREE_OPERAND (member, 0);
+ }
+ while (ANON_AGGR_TYPE_P (TREE_TYPE (member)));
+
+ /* VEC has the constructor elements vector for the context of FIELD.
+ If FIELD is an anonymous aggregate, we will push inside it. */
+ vec<constructor_elt, va_gc> **vec = vec_outer;
+ tree field;
+ while (field = fields.pop(),
+ ANON_AGGR_TYPE_P (TREE_TYPE (field)))
+ {
+ tree ctor;
+ /* If there is already an outer constructor entry for the anonymous
+ aggregate FIELD, use it; otherwise, insert one. */
+ if (vec_safe_is_empty (*vec)
+ || (*vec)->last().index != field)
+ {
+ ctor = build_constructor (TREE_TYPE (field), NULL);
+ CONSTRUCTOR_APPEND_ELT (*vec, field, ctor);
+ }
+ else
+ ctor = (*vec)->last().value;
+ vec = &CONSTRUCTOR_ELTS (ctor);
+ }
+
+ /* Now we're at the innermost field, the one that isn't an anonymous
+ aggregate. Add its initializer to the CONSTRUCTOR and we're done. */
+ gcc_assert (fields.is_empty());
+ fields.release ();
+ CONSTRUCTOR_APPEND_ELT (*vec, field, init);
+
+ return true;
+}
+
/* Subroutine of build_constexpr_constructor_member_initializers.
The expression tree T represents a data member initialization
in a (constexpr) constructor definition. Build a pairing of
@@ -5901,12 +5954,21 @@ build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec)
}
if (TREE_CODE (member) == ADDR_EXPR)
member = TREE_OPERAND (member, 0);
- if (TREE_CODE (member) == COMPONENT_REF
- /* If we're initializing a member of a subaggregate, it's a vtable
- pointer. Leave it as COMPONENT_REF so we remember the path to get
- to the vfield. */
- && TREE_CODE (TREE_OPERAND (member, 0)) != COMPONENT_REF)
- member = TREE_OPERAND (member, 1);
+ if (TREE_CODE (member) == COMPONENT_REF)
+ {
+ tree aggr = TREE_OPERAND (member, 0);
+ if (TREE_CODE (aggr) != COMPONENT_REF)
+ /* Normal member initialization. */
+ member = TREE_OPERAND (member, 1);
+ else if (ANON_AGGR_TYPE_P (TREE_TYPE (aggr)))
+ /* Initializing a member of an anonymous union. */
+ return build_anon_member_initialization (member, init, vec);
+ else
+ /* We're initializing a vtable pointer in a base. Leave it as
+ COMPONENT_REF so we remember the path to get to the vfield. */
+ gcc_assert (TREE_TYPE (member) == vtbl_ptr_type_node);
+ }
+
CONSTRUCTOR_APPEND_ELT (*vec, member, init);
return true;
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-union4.C
new file mode 100644
index 0000000..a8d6b8d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union4.C
@@ -0,0 +1,18 @@
+// PR c++/54922
+// { dg-do compile { target c++11 } }
+
+struct nullable_int
+{
+ bool init_;
+ union {
+ unsigned char for_value_init;
+ int value_;
+ };
+
+ constexpr nullable_int() : init_(false), for_value_init() {}
+};
+
+#define SA(X) static_assert(X,#X)
+
+constexpr nullable_int n;
+SA((n.for_value_init == 0));
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-union5.C
new file mode 100644
index 0000000..e8e678d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union5.C
@@ -0,0 +1,41 @@
+// PR c++/54922
+// { dg-options "-std=c++11 -pedantic" }
+
+#define SA(X) static_assert(X,#X)
+
+struct A
+{
+ union {
+ union {
+ union {
+ unsigned char i;
+ int j;
+ };
+ };
+ };
+
+ constexpr A() : i(42) {}
+};
+
+constexpr A a;
+SA((a.i == 42));
+
+struct B
+{
+ struct {
+ int h;
+ struct {
+ union {
+ unsigned char i;
+ int j;
+ };
+ int k;
+ }; // { dg-warning "anonymous struct" }
+ }; // { dg-warning "anonymous struct" }
+ int l;
+
+ constexpr B(): h(1), i(2), k(3), l(4) {}
+};
+
+constexpr B b;
+SA((b.h == 1 && b.i == 2 && b.k == 3 && b.l == 4));