From ffb4b6e299069139908540ce97be4462e16b53a4 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 15 Apr 2009 06:41:24 +0000 Subject: [PATCH] Implement support for designated initializers that refer to members of anonymous structs or unions. Fixes PR3778. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69153 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 31 +++++-- include/clang/Basic/DiagnosticSemaKinds.td | 3 - lib/AST/Expr.cpp | 66 +++++++++++---- lib/Sema/Sema.h | 2 + lib/Sema/SemaExpr.cpp | 44 ++++++---- lib/Sema/SemaInit.cpp | 97 +++++++++++++++++++--- test/Sema/designated-initializers.c | 34 ++++++++ 7 files changed, 224 insertions(+), 53 deletions(-) diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 057afeea8a..0e86a4c142 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -1963,6 +1963,11 @@ private: /// designators, one array designator for @c [2] followed by one field /// designator for @c .y. The initalization expression will be 1.0. class DesignatedInitExpr : public Expr { +public: + /// \brief Forward declaration of the Designator class. + class Designator; + +private: /// The location of the '=' or ':' prior to the actual initializer /// expression. SourceLocation EqualOrColonLoc; @@ -1974,17 +1979,20 @@ class DesignatedInitExpr : public Expr { /// The number of designators in this initializer expression. unsigned NumDesignators : 15; + /// \brief The designators in this designated initialization + /// expression. + Designator *Designators; + /// The number of subexpressions of this initializer expression, /// which contains both the initializer and any additional /// expressions used by array and array-range designators. unsigned NumSubExprs : 16; + DesignatedInitExpr(QualType Ty, unsigned NumDesignators, + const Designator *Designators, SourceLocation EqualOrColonLoc, bool GNUSyntax, - unsigned NumSubExprs) - : Expr(DesignatedInitExprClass, Ty), - EqualOrColonLoc(EqualOrColonLoc), GNUSyntax(GNUSyntax), - NumDesignators(NumDesignators), NumSubExprs(NumSubExprs) { } + unsigned NumSubExprs); public: /// A field designator, e.g., ".x". @@ -2041,6 +2049,8 @@ public: friend class DesignatedInitExpr; public: + Designator() {} + /// @brief Initializes a field designator. Designator(const IdentifierInfo *FieldName, SourceLocation DotLoc, SourceLocation FieldLoc) @@ -2136,8 +2146,10 @@ public: // Iterator access to the designators. typedef Designator* designators_iterator; - designators_iterator designators_begin(); - designators_iterator designators_end(); + designators_iterator designators_begin() { return Designators; } + designators_iterator designators_end() { + return Designators + NumDesignators; + } Designator *getDesignator(unsigned Idx) { return &designators_begin()[Idx]; } @@ -2162,8 +2174,15 @@ public: *child_begin() = init; } + /// \brief Replaces the designator at index @p Idx with the series + /// of designators in [First, Last). + void ExpandDesignator(unsigned Idx, const Designator *First, + const Designator *Last); + virtual SourceRange getSourceRange() const; + virtual void Destroy(ASTContext &C); + static bool classof(const Stmt *T) { return T->getStmtClass() == DesignatedInitExprClass; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a3458bcaf2..7bef91c57a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -49,9 +49,6 @@ def err_field_designator_unknown : Error< def err_field_designator_nonfield : Error< "field designator %0 does not refer to a non-static data member">; def note_field_designator_found : Note<"field designator refers here">; -def err_field_designator_anon_class : Error< - "field designator %0 refers to a member of an anonymous " - "%select{struct|class|union}1">; def err_designator_for_scalar_init : Error< "designator in initializer for scalar type %0">; def warn_subobject_initializer_overrides : Warning< diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index dd346176bf..e2dd64a703 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -20,6 +20,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/TargetInfo.h" +#include using namespace clang; //===----------------------------------------------------------------------===// @@ -1499,6 +1500,19 @@ IdentifierInfo *DesignatedInitExpr::Designator::getFieldName() { return getField()->getIdentifier(); } +DesignatedInitExpr::DesignatedInitExpr(QualType Ty, unsigned NumDesignators, + const Designator *Designators, + SourceLocation EqualOrColonLoc, + bool GNUSyntax, + unsigned NumSubExprs) + : Expr(DesignatedInitExprClass, Ty), + EqualOrColonLoc(EqualOrColonLoc), GNUSyntax(GNUSyntax), + NumDesignators(NumDesignators), NumSubExprs(NumSubExprs) { + this->Designators = new Designator[NumDesignators]; + for (unsigned I = 0; I != NumDesignators; ++I) + this->Designators[I] = Designators[I]; +} + DesignatedInitExpr * DesignatedInitExpr::Create(ASTContext &C, Designator *Designators, unsigned NumDesignators, @@ -1506,10 +1520,9 @@ DesignatedInitExpr::Create(ASTContext &C, Designator *Designators, SourceLocation ColonOrEqualLoc, bool UsesColonSyntax, Expr *Init) { void *Mem = C.Allocate(sizeof(DesignatedInitExpr) + - sizeof(Designator) * NumDesignators + sizeof(Stmt *) * (NumIndexExprs + 1), 8); DesignatedInitExpr *DIE - = new (Mem) DesignatedInitExpr(C.VoidTy, NumDesignators, + = new (Mem) DesignatedInitExpr(C.VoidTy, NumDesignators, Designators, ColonOrEqualLoc, UsesColonSyntax, NumIndexExprs + 1); @@ -1517,7 +1530,6 @@ DesignatedInitExpr::Create(ASTContext &C, Designator *Designators, unsigned ExpectedNumSubExprs = 0; designators_iterator Desig = DIE->designators_begin(); for (unsigned Idx = 0; Idx < NumDesignators; ++Idx, ++Desig) { - new (static_cast(Desig)) Designator(Designators[Idx]); if (Designators[Idx].isArrayDesignator()) ++ExpectedNumSubExprs; else if (Designators[Idx].isArrayRangeDesignator()) @@ -1549,22 +1561,10 @@ SourceRange DesignatedInitExpr::getSourceRange() const { return SourceRange(StartLoc, getInit()->getSourceRange().getEnd()); } -DesignatedInitExpr::designators_iterator -DesignatedInitExpr::designators_begin() { - char* Ptr = static_cast(static_cast(this)); - Ptr += sizeof(DesignatedInitExpr); - return static_cast(static_cast(Ptr)); -} - -DesignatedInitExpr::designators_iterator DesignatedInitExpr::designators_end() { - return designators_begin() + NumDesignators; -} - Expr *DesignatedInitExpr::getArrayIndex(const Designator& D) { assert(D.Kind == Designator::ArrayDesignator && "Requires array designator"); char* Ptr = static_cast(static_cast(this)); Ptr += sizeof(DesignatedInitExpr); - Ptr += sizeof(Designator) * NumDesignators; Stmt **SubExprs = reinterpret_cast(reinterpret_cast(Ptr)); return cast(*(SubExprs + D.ArrayOrRange.Index + 1)); } @@ -1574,7 +1574,6 @@ Expr *DesignatedInitExpr::getArrayRangeStart(const Designator& D) { "Requires array range designator"); char* Ptr = static_cast(static_cast(this)); Ptr += sizeof(DesignatedInitExpr); - Ptr += sizeof(Designator) * NumDesignators; Stmt **SubExprs = reinterpret_cast(reinterpret_cast(Ptr)); return cast(*(SubExprs + D.ArrayOrRange.Index + 1)); } @@ -1584,11 +1583,43 @@ Expr *DesignatedInitExpr::getArrayRangeEnd(const Designator& D) { "Requires array range designator"); char* Ptr = static_cast(static_cast(this)); Ptr += sizeof(DesignatedInitExpr); - Ptr += sizeof(Designator) * NumDesignators; Stmt **SubExprs = reinterpret_cast(reinterpret_cast(Ptr)); return cast(*(SubExprs + D.ArrayOrRange.Index + 2)); } +/// \brief Replaces the designator at index @p Idx with the series +/// of designators in [First, Last). +void DesignatedInitExpr::ExpandDesignator(unsigned Idx, + const Designator *First, + const Designator *Last) { + unsigned NumNewDesignators = Last - First; + if (NumNewDesignators == 0) { + std::copy_backward(Designators + Idx + 1, + Designators + NumDesignators, + Designators + Idx); + --NumNewDesignators; + return; + } else if (NumNewDesignators == 1) { + Designators[Idx] = *First; + return; + } + + Designator *NewDesignators + = new Designator[NumDesignators - 1 + NumNewDesignators]; + std::copy(Designators, Designators + Idx, NewDesignators); + std::copy(First, Last, NewDesignators + Idx); + std::copy(Designators + Idx + 1, Designators + NumDesignators, + NewDesignators + Idx + NumNewDesignators); + delete [] Designators; + Designators = NewDesignators; + NumDesignators = NumDesignators - 1 + NumNewDesignators; +} + +void DesignatedInitExpr::Destroy(ASTContext &C) { + delete [] Designators; + Expr::Destroy(C); +} + //===----------------------------------------------------------------------===// // ExprIterator. //===----------------------------------------------------------------------===// @@ -1774,7 +1805,6 @@ Stmt::child_iterator InitListExpr::child_end() { Stmt::child_iterator DesignatedInitExpr::child_begin() { char* Ptr = static_cast(static_cast(this)); Ptr += sizeof(DesignatedInitExpr); - Ptr += sizeof(Designator) * NumDesignators; return reinterpret_cast(reinterpret_cast(Ptr)); } Stmt::child_iterator DesignatedInitExpr::child_end() { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d64228794b..dc80bd98ec 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1226,6 +1226,8 @@ public: DeclRefExpr *BuildDeclRefExpr(NamedDecl *D, QualType Ty, SourceLocation Loc, bool TypeDependent, bool ValueDependent, const CXXScopeSpec *SS = 0); + VarDecl *BuildAnonymousStructUnionMemberPath(FieldDecl *Field, + llvm::SmallVectorImpl &Path); OwningExprResult BuildAnonymousStructUnionMemberReference(SourceLocation Loc, FieldDecl *Field, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a5999cc890..ef2bf91cb7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -486,29 +486,33 @@ static Decl *getObjectForAnonymousRecordDecl(ASTContext &Context, return 0; } -Sema::OwningExprResult -Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc, - FieldDecl *Field, - Expr *BaseObjectExpr, - SourceLocation OpLoc) { +/// \brief Given a field that represents a member of an anonymous +/// struct/union, build the path from that field's context to the +/// actual member. +/// +/// Construct the sequence of field member references we'll have to +/// perform to get to the field in the anonymous union/struct. The +/// list of members is built from the field outward, so traverse it +/// backwards to go from an object in the current context to the field +/// we found. +/// +/// \returns The variable from which the field access should begin, +/// for an anonymous struct/union that is not a member of another +/// class. Otherwise, returns NULL. +VarDecl *Sema::BuildAnonymousStructUnionMemberPath(FieldDecl *Field, + llvm::SmallVectorImpl &Path) { assert(Field->getDeclContext()->isRecord() && cast(Field->getDeclContext())->isAnonymousStructOrUnion() && "Field must be stored inside an anonymous struct or union"); - // Construct the sequence of field member references - // we'll have to perform to get to the field in the anonymous - // union/struct. The list of members is built from the field - // outward, so traverse it backwards to go from an object in - // the current context to the field we found. - llvm::SmallVector AnonFields; - AnonFields.push_back(Field); + Path.push_back(Field); VarDecl *BaseObject = 0; DeclContext *Ctx = Field->getDeclContext(); do { RecordDecl *Record = cast(Ctx); Decl *AnonObject = getObjectForAnonymousRecordDecl(Context, Record); if (FieldDecl *AnonField = dyn_cast(AnonObject)) - AnonFields.push_back(AnonField); + Path.push_back(AnonField); else { BaseObject = cast(AnonObject); break; @@ -516,7 +520,19 @@ Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc, Ctx = Ctx->getParent(); } while (Ctx->isRecord() && cast(Ctx)->isAnonymousStructOrUnion()); - + + return BaseObject; +} + +Sema::OwningExprResult +Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc, + FieldDecl *Field, + Expr *BaseObjectExpr, + SourceLocation OpLoc) { + llvm::SmallVector AnonFields; + VarDecl *BaseObject = BuildAnonymousStructUnionMemberPath(Field, + AnonFields); + // Build the expression that refers to the base object, from // which we will build a sequence of member references to each // of the anonymous union objects and, eventually, the field we diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 3a053216ae..a0f4e4ac38 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -1031,6 +1031,64 @@ void InitListChecker::CheckStructUnionTypes(InitListExpr *IList, StructuredIndex); } +/// \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. +/// +/// Field/FieldIndex will be updated to point to the (new) +/// currently-designated field. +static void ExpandAnonymousFieldDesignator(Sema &SemaRef, + DesignatedInitExpr *DIE, + unsigned DesigIdx, + FieldDecl *Field, + RecordDecl::field_iterator &FieldIter, + unsigned &FieldIndex) { + typedef DesignatedInitExpr::Designator Designator; + + // Build the path from the current object to the member of the + // anonymous struct/union (backwards). + llvm::SmallVector Path; + SemaRef.BuildAnonymousStructUnionMemberPath(Field, Path); + + // Build the replacement designators. + llvm::SmallVector Replacements; + for (llvm::SmallVector::reverse_iterator + FI = Path.rbegin(), FIEnd = Path.rend(); + FI != FIEnd; ++FI) { + if (FI + 1 == FIEnd) + Replacements.push_back(Designator((IdentifierInfo *)0, + DIE->getDesignator(DesigIdx)->getDotLoc(), + DIE->getDesignator(DesigIdx)->getFieldLoc())); + else + Replacements.push_back(Designator((IdentifierInfo *)0, SourceLocation(), + SourceLocation())); + Replacements.back().setField(*FI); + } + + // Expand the current designator into the set of replacement + // designators, so we have a full subobject path down to where the + // member of the anonymous struct/union is actually stored. + DIE->ExpandDesignator(DesigIdx, &Replacements[0], + &Replacements[0] + Replacements.size()); + + // Update FieldIter/FieldIndex; + RecordDecl *Record = cast(Path.back()->getDeclContext()); + FieldIter = Record->field_begin(SemaRef.Context); + FieldIndex = 0; + for (RecordDecl::field_iterator FEnd = Record->field_end(SemaRef.Context); + FieldIter != FEnd; ++FieldIter) { + if (FieldIter->isUnnamedBitfield()) + continue; + + if (*FieldIter == Path.back()) + return; + + ++FieldIndex; + } + + assert(false && "Unable to find anonymous struct/union field"); +} + /// @brief Check the well-formedness of a C99 designated initializer. /// /// Determines whether the designated initializer @p DIE, which @@ -1138,6 +1196,7 @@ InitListChecker::CheckDesignatedInitializer(InitListExpr *IList, // Note: we perform a linear search of the fields here, despite // the fact that we have a faster lookup method, because we always // need to compute the field's index. + FieldDecl *KnownField = D->getField(); IdentifierInfo *FieldName = D->getFieldName(); unsigned FieldIndex = 0; RecordDecl::field_iterator @@ -1147,40 +1206,50 @@ InitListChecker::CheckDesignatedInitializer(InitListExpr *IList, if (Field->isUnnamedBitfield()) continue; - if (Field->getIdentifier() == FieldName) + if (KnownField == *Field || Field->getIdentifier() == FieldName) break; ++FieldIndex; } if (Field == FieldEnd) { - // We did not find the field we're looking for. Produce a - // suitable diagnostic and return a failure. + // There was no normal field in the struct with the designated + // name. Perform another lookup for this name, which may find + // something that we can't designate (e.g., a member function), + // may find nothing, or may find a member of an anonymous + // struct/union. DeclContext::lookup_result Lookup = RT->getDecl()->lookup(SemaRef.Context, FieldName); if (Lookup.first == Lookup.second) { // Name lookup didn't find anything. SemaRef.Diag(D->getFieldLoc(), diag::err_field_designator_unknown) << FieldName << CurrentObjectType; + ++Index; + return true; + } else if (!KnownField && isa(*Lookup.first) && + cast((*Lookup.first)->getDeclContext()) + ->isAnonymousStructOrUnion()) { + // Handle an field designator that refers to a member of an + // anonymous struct or union. + ExpandAnonymousFieldDesignator(SemaRef, DIE, DesigIdx, + cast(*Lookup.first), + Field, FieldIndex); } else { // Name lookup found something, but it wasn't a field. SemaRef.Diag(D->getFieldLoc(), diag::err_field_designator_nonfield) << FieldName; SemaRef.Diag((*Lookup.first)->getLocation(), diag::note_field_designator_found); - } ++Index; return true; - } else if (cast((*Field)->getDeclContext()) + } + } else if (!KnownField && + cast((*Field)->getDeclContext()) ->isAnonymousStructOrUnion()) { - SemaRef.Diag(D->getFieldLoc(), diag::err_field_designator_anon_class) - << FieldName - << (cast((*Field)->getDeclContext())->isUnion()? 2 : - (int)SemaRef.getLangOptions().CPlusPlus); - SemaRef.Diag((*Field)->getLocation(), diag::note_field_designator_found); - ++Index; - return true; + 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 @@ -1284,6 +1353,10 @@ InitListChecker::CheckDesignatedInitializer(InitListExpr *IList, if (!FinishSubobjectInit) return false; + // We've already initialized something in the union; we're done. + if (RT->getDecl()->isUnion()) + return hadError; + // Check the remaining fields within this class/struct/union subobject. bool prevHadError = hadError; CheckStructUnionTypes(IList, CurrentObjectType, Field, false, Index, diff --git a/test/Sema/designated-initializers.c b/test/Sema/designated-initializers.c index f4170c267d..309a530459 100644 --- a/test/Sema/designated-initializers.c +++ b/test/Sema/designated-initializers.c @@ -187,3 +187,37 @@ const union wibble wobble = { .arr2[0] = 0xffff, .arr2[2] = 0xffff }; const union wibble wobble2 = { .arr2 = {4, 5, 6}, 7 }; // expected-warning{{excess elements in union initializer}} + +// PR3778 +struct s { + union { int i; }; +}; +struct s si = { + { .i = 1 } +}; + +double d0; +char c0; +float f0; +int i0; + +struct Enigma { + union { + struct { + struct { + double *double_ptr; + char *string; + }; + float *float_ptr; + }; + int *int_ptr; + }; + char *string2; +}; + +struct Enigma enigma = { + .double_ptr = &d0, &c0, + &f0, // expected-note{{previous}} + &c0, + .float_ptr = &f0 // expected-warning{{overrides}} +};