diff options
-rw-r--r-- | lib/Sema/SemaInit.cpp | 35 | ||||
-rw-r--r-- | test/CodeGen/designated-initializers.c | 33 | ||||
-rw-r--r-- | test/Sema/designated-initializers.c | 28 |
3 files changed, 85 insertions, 11 deletions
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 6fd1d68dcd..bac9f8fa75 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -1167,6 +1167,22 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, StructuredList, StructuredIndex); } +/// \brief Similar to Sema::BuildAnonymousStructUnionMemberPath() but builds a +/// relative path and has strict checks. +static void BuildRelativeAnonymousStructUnionMemberPath(FieldDecl *Field, + llvm::SmallVectorImpl<FieldDecl *> &Path, + DeclContext *BaseDC) { + Path.push_back(Field); + for (DeclContext *Ctx = Field->getDeclContext(); + !Ctx->Equals(BaseDC); + Ctx = Ctx->getParent()) { + ValueDecl *AnonObject = + cast<RecordDecl>(Ctx)->getAnonymousStructOrUnionObject(); + FieldDecl *AnonField = cast<FieldDecl>(AnonObject); + Path.push_back(AnonField); + } +} + /// \brief Expand a field designator that refers to a member of an /// anonymous struct or union into a series of field designators that /// refers to the field within the appropriate subobject. @@ -1178,13 +1194,14 @@ static void ExpandAnonymousFieldDesignator(Sema &SemaRef, unsigned DesigIdx, FieldDecl *Field, RecordDecl::field_iterator &FieldIter, - unsigned &FieldIndex) { + unsigned &FieldIndex, + DeclContext *BaseDC) { typedef DesignatedInitExpr::Designator Designator; // Build the path from the current object to the member of the // anonymous struct/union (backwards). llvm::SmallVector<FieldDecl *, 4> Path; - SemaRef.BuildAnonymousStructUnionMemberPath(Field, Path); + BuildRelativeAnonymousStructUnionMemberPath(Field, Path, BaseDC); // Build the replacement designators. llvm::SmallVector<Designator, 4> Replacements; @@ -1343,7 +1360,9 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, if (Field->isUnnamedBitfield()) continue; - if (KnownField == *Field || Field->getIdentifier() == FieldName) + if (KnownField && KnownField == *Field) + break; + if (FieldName && FieldName == Field->getIdentifier()) break; ++FieldIndex; @@ -1400,10 +1419,10 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, cast<RecordDecl>((ReplacementField)->getDeclContext()) ->isAnonymousStructOrUnion()) { // Handle an field designator that refers to a member of an - // anonymous struct or union. + // anonymous struct or union. This is a C1X feature. ExpandAnonymousFieldDesignator(SemaRef, DIE, DesigIdx, ReplacementField, - Field, FieldIndex); + Field, FieldIndex, RT->getDecl()); D = DIE->getDesignator(DesigIdx); } else if (!KnownField) { // The replacement field comes from typo correction; find it @@ -1421,12 +1440,6 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, ++FieldIndex; } } - } else if (!KnownField && - cast<RecordDecl>((*Field)->getDeclContext()) - ->isAnonymousStructOrUnion()) { - ExpandAnonymousFieldDesignator(SemaRef, DIE, DesigIdx, *Field, - Field, FieldIndex); - D = DIE->getDesignator(DesigIdx); } // All of the fields of a union are located at the same place in diff --git a/test/CodeGen/designated-initializers.c b/test/CodeGen/designated-initializers.c index 312d785652..d928296ef2 100644 --- a/test/CodeGen/designated-initializers.c +++ b/test/CodeGen/designated-initializers.c @@ -19,6 +19,39 @@ int b[2] = { [1] = 22 }; +// PR6955 + +struct ds { + struct { + struct { + short a; + }; + short b; + struct { + short c; + }; + }; +}; + +// Traditional C anonymous member init +struct ds ds0 = { { { .a = 0 } } }; +// C1X lookup-based anonymous member init cases +struct ds ds1 = { { .a = 1 } }; +struct ds ds2 = { { .b = 1 } }; +struct ds ds3 = { .a = 0 }; +// CHECK: @ds4 = global %3 { %4 { %struct.anon zeroinitializer, i16 0, %struct.anon { i16 1 } } } +struct ds ds4 = { .c = 1 }; +struct ds ds5 = { { { .a = 0 } }, .b = 1 }; +struct ds ds6 = { { .a = 0, .b = 1 } }; +// CHECK: @ds7 = global %3 { %4 { %struct.anon { i16 2 }, i16 3, %struct.anon zeroinitializer } } +struct ds ds7 = { + { { + .a = 1 + } }, + .a = 2, + .b = 3 +}; + void test1(int argc, char **argv) { // CHECK: internal global %struct.foo { i8* null, i32 1024 } diff --git a/test/Sema/designated-initializers.c b/test/Sema/designated-initializers.c index 7e4ed6892a..c9a8482e85 100644 --- a/test/Sema/designated-initializers.c +++ b/test/Sema/designated-initializers.c @@ -249,3 +249,31 @@ struct expr expr0 = { } } }; + +// PR6955 + +struct ds { + struct { + struct { + unsigned int a; + }; + unsigned int b; + struct { + unsigned int c; + }; + }; +}; + +// C1X lookup-based anonymous member init cases +struct ds ds0 = { + { { + .a = 1 // expected-note{{previous initialization is here}} + } }, + .a = 2, // expected-warning{{initializer overrides prior initialization of this subobject}} + .b = 3 +}; +struct ds ds1 = { .c = 0 }; +struct ds ds2 = { { { + .a = 0, + .b = 1 // expected-error{{field designator 'b' does not refer to any field}} +} } }; |