Gracefully (and correctly) handle init of multiple union members
We now emit warnings when doing so and code generation is consistent
with GCC. Note that the C99 spec is unclear as to the precise
behavior.
See also ...
Bug:
http://llvm.org/bugs/show_bug.cgi?id=16644 and
cfe-dev discussion:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2013-September/031918.html
llvm-svn: 191890
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 8a884c0..a53b88d 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -3824,6 +3824,10 @@
return const_cast<InitListExpr *>(this)->getInitializedFieldInUnion();
}
void setInitializedFieldInUnion(FieldDecl *FD) {
+ assert((FD == 0
+ || getInitializedFieldInUnion() == 0
+ || getInitializedFieldInUnion() == FD)
+ && "Only one field of a union may be initialized at a time!");
ArrayFillerOrUnionFieldInit = FD;
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index afd9393..e407253 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -1868,8 +1868,29 @@
// the initializer list.
if (RT->getDecl()->isUnion()) {
FieldIndex = 0;
- if (!VerifyOnly)
+ if (!VerifyOnly) {
+ FieldDecl *CurrentField = StructuredList->getInitializedFieldInUnion();
+ if (CurrentField && CurrentField != *Field) {
+ assert(StructuredList->getNumInits() == 1
+ && "A union should never have more than one initializer!");
+
+ // we're about to throw away an initializer, emit warning
+ SemaRef.Diag(D->getFieldLoc(),
+ diag::warn_initializer_overrides)
+ << D->getSourceRange();
+ Expr *ExistingInit = StructuredList->getInit(0);
+ SemaRef.Diag(ExistingInit->getLocStart(),
+ diag::note_previous_initializer)
+ << /*FIXME:has side effects=*/0
+ << ExistingInit->getSourceRange();
+
+ // remove existing initializer
+ StructuredList->resizeInits(SemaRef.Context, 0);
+ StructuredList->setInitializedFieldInUnion(0);
+ }
+
StructuredList->setInitializedFieldInUnion(*Field);
+ }
}
// Make sure we can use this declaration.
diff --git a/clang/test/CodeGen/designated-initializers.c b/clang/test/CodeGen/designated-initializers.c
index b418986..b11c67a 100644
--- a/clang/test/CodeGen/designated-initializers.c
+++ b/clang/test/CodeGen/designated-initializers.c
@@ -81,6 +81,63 @@
// CHECK: [6 x i8] c"foo\00y\00", i32 1
+// CHECK: @u1 = {{.*}} { i32 65535 }
+union u_FFFF { char c; long l; } u1 = { .l = 0xFFFF };
+
+
+/// PR16644
+typedef union u_16644 {
+ struct s_16644 {
+ int zero;
+ int one;
+ int two;
+ int three;
+ } a;
+ int b[4];
+} union_16644_t;
+
+// CHECK: @union_16644_instance_0 = {{.*}} { i32 0, i32 0, i32 0, i32 3 } }
+union_16644_t union_16644_instance_0 =
+{
+ .b[0] = 0,
+ .a.one = 1,
+ .b[2] = 2,
+ .a.three = 3,
+};
+
+// CHECK: @union_16644_instance_1 = {{.*}} [i32 10, i32 0, i32 0, i32 0]
+union_16644_t union_16644_instance_1 =
+{
+ .a.three = 13,
+ .b[2] = 12,
+ .a.one = 11,
+ .b[0] = 10,
+};
+
+// CHECK: @union_16644_instance_2 = {{.*}} [i32 0, i32 20, i32 0, i32 0]
+union_16644_t union_16644_instance_2 =
+{
+ .a.one = 21,
+ .b[1] = 20,
+};
+
+// CHECK: @union_16644_instance_3 = {{.*}} { i32 0, i32 31, i32 0, i32 0 }
+union_16644_t union_16644_instance_3 =
+{
+ .b[1] = 30,
+ .a = {
+ .one = 31
+ }
+};
+
+// CHECK: @union_16644_instance_4 = {{.*}} { i32 5, i32 2, i32 0, i32 0 } {{.*}} [i32 0, i32 4, i32 0, i32 0]
+union_16644_t union_16644_instance_4[2] =
+{
+ [0].a.one = 2,
+ [1].a.zero = 3,
+ [0].a.zero = 5,
+ [1].b[1] = 4
+};
void test1(int argc, char **argv)
{
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index 36fa559..6630da6 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -137,7 +137,6 @@
};
}
-// FIXME: How do we test that this initializes the long properly?
union { char c; long l; } u1 = { .l = 0xFFFF };
extern float global_float;
@@ -223,6 +222,55 @@
};
+/// PR16644
+typedef union {
+ struct {
+ int zero;
+ int one;
+ int two;
+ int three;
+ } a;
+ int b[4];
+} union_16644_t;
+
+union_16644_t union_16644_instance_0 =
+{
+ .b[0] = 0, // expected-note{{previous}}
+ .a.one = 1, // expected-warning{{overrides}} expected-note{{previous}}
+ .b[2] = 2, // expected-warning{{overrides}} expected-note{{previous}}
+ .a.three = 3, // expected-warning{{overrides}}
+};
+
+union_16644_t union_16644_instance_1 =
+{
+ .a.three = 13, // expected-note{{previous}}
+ .b[2] = 12, // expected-warning{{overrides}} expected-note{{previous}}
+ .a.one = 11, // expected-warning{{overrides}} expected-note{{previous}}
+ .b[0] = 10, // expected-warning{{overrides}}
+};
+
+union_16644_t union_16644_instance_2 =
+{
+ .a.one = 21, // expected-note{{previous}}
+ .b[1] = 20, // expected-warning{{overrides}}
+};
+
+union_16644_t union_16644_instance_3 =
+{
+ .b[1] = 30, // expected-note{{previous}}
+ .a = { // expected-warning{{overrides}}
+ .one = 31
+ }
+};
+
+union_16644_t union_16644_instance_4[2] =
+{
+ [0].a.one = 2,
+ [1].a.zero = 3,// expected-note{{previous}}
+ [0].a.zero = 5,
+ [1].b[1] = 4 // expected-warning{{overrides}}
+};
+
/// PR4073
/// Should use evaluate to fold aggressively and emit a warning if not an ice.
extern int crazy_x;