Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -2204,9 +2204,11 @@ DEF_TRAVERSE_STMT(CXXThrowExpr, {}) DEF_TRAVERSE_STMT(UserDefinedLiteral, {}) DEF_TRAVERSE_STMT(DesignatedInitExpr, {}) +DEF_TRAVERSE_STMT(DesignatedInitUpdateExpr, {}) DEF_TRAVERSE_STMT(ExtVectorElementExpr, {}) DEF_TRAVERSE_STMT(GNUNullExpr, {}) DEF_TRAVERSE_STMT(ImplicitValueInitExpr, {}) +DEF_TRAVERSE_STMT(NoInitExpr, {}) DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, {}) DEF_TRAVERSE_STMT(ObjCEncodeExpr, { if (TypeSourceInfo *TInfo = S->getEncodedTypeSourceInfo()) Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -4262,6 +4262,86 @@ } }; +/// \brief Represents a place-holder for an object not to be initialized by +/// anything. +/// +/// This only makes sense when it appears as part of an updater of a +/// DesignatedInitUpdateExpr (see below). The base expression of a DIUE +/// initializes a big object, and the NoInitExpr's mark the spots within the +/// big object not to be overwritten by the updater. +/// +/// \see DesignatedInitUpdateExpr +class NoInitExpr : public Expr { +public: + explicit NoInitExpr(QualType ty) + : Expr(NoInitExprClass, ty, VK_RValue, OK_Ordinary, + false, false, ty->isInstantiationDependentType(), false) { } + + explicit NoInitExpr(EmptyShell Empty) + : Expr(NoInitExprClass, Empty) { } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == NoInitExprClass; + } + + SourceLocation getLocStart() const LLVM_READONLY { return SourceLocation(); } + SourceLocation getLocEnd() const LLVM_READONLY { return SourceLocation(); } + + // Iterators + child_range children() { return child_range(); } +}; + +// In cases like: +// struct Q { int a, b, c; }; +// Q *getQ(); +// void foo() { +// struct A { Q q; } a = { *getQ(), .q.b = 3 }; +// } +// +// We will have an InitListExpr for a, with type A, and then a +// DesignatedInitUpdateExpr for "a.q" with type Q. The "base" for this DIUE +// is the call expression *getQ(); the "updater" for the DIUE is ".q.b = 3" +// +class DesignatedInitUpdateExpr : public Expr { + // BaseAndUpdaterExprs[0] is the base expression; + // BaseAndUpdaterExprs[1] is an InitListExpr overwriting part of the base. + Stmt *BaseAndUpdaterExprs[2]; + SourceLocation LBraceLoc, RBraceLoc; + +public: + DesignatedInitUpdateExpr(const ASTContext &C, SourceLocation lbraceloc, + Expr* baseExprs, SourceLocation rbraceloc); + + explicit DesignatedInitUpdateExpr(EmptyShell Empty) + : Expr(DesignatedInitUpdateExprClass, Empty) { } + + SourceLocation getLocStart() const LLVM_READONLY; + SourceLocation getLocEnd() const LLVM_READONLY; + + static bool classof(const Stmt *T) { + return T->getStmtClass() == DesignatedInitUpdateExprClass; + } + + SourceLocation getLBraceLoc() const { return LBraceLoc; } + void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; } + SourceLocation getRBraceLoc() const { return RBraceLoc; } + void setRBraceLoc(SourceLocation Loc) { RBraceLoc = Loc; } + + Expr *getBase() const { return cast(BaseAndUpdaterExprs[0]); } + void setBase(Expr *Base) { BaseAndUpdaterExprs[0] = Base; } + + InitListExpr *getUpdater() const { + return cast(BaseAndUpdaterExprs[1]); + } + void setUpdater(Expr *Updater) { BaseAndUpdaterExprs[1] = Updater; } + + // Iterators + // children = the base and the updater + child_range children() { + return child_range(&BaseAndUpdaterExprs[0], &BaseAndUpdaterExprs[0] + 2); + } +}; + /// \brief Represents an implicitly-generated value initialization of /// an object of a given type. /// Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2234,9 +2234,11 @@ DEF_TRAVERSE_STMT(CXXThrowExpr, {}) DEF_TRAVERSE_STMT(UserDefinedLiteral, {}) DEF_TRAVERSE_STMT(DesignatedInitExpr, {}) +DEF_TRAVERSE_STMT(DesignatedInitUpdateExpr, {}) DEF_TRAVERSE_STMT(ExtVectorElementExpr, {}) DEF_TRAVERSE_STMT(GNUNullExpr, {}) DEF_TRAVERSE_STMT(ImplicitValueInitExpr, {}) +DEF_TRAVERSE_STMT(NoInitExpr, {}) DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, {}) DEF_TRAVERSE_STMT(ObjCEncodeExpr, { if (TypeSourceInfo *TInfo = S->getEncodedTypeSourceInfo()) Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -77,7 +77,9 @@ def ExtVectorElementExpr : DStmt; def InitListExpr : DStmt; def DesignatedInitExpr : DStmt; +def DesignatedInitUpdateExpr : DStmt; def ImplicitValueInitExpr : DStmt; +def NoInitExpr : DStmt; def ParenListExpr : DStmt; def VAArgExpr : DStmt; def GenericSelectionExpr : DStmt; Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1196,8 +1196,12 @@ EXPR_INIT_LIST, /// \brief A DesignatedInitExpr record. EXPR_DESIGNATED_INIT, + /// \brief A DesignatedInitUpdateExpr record. + EXPR_DESIGNATED_INIT_UPDATE, /// \brief An ImplicitValueInitExpr record. EXPR_IMPLICIT_VALUE_INIT, + /// \brief An NoInitExpr record. + EXPR_NO_INIT, /// \brief A VAArgExpr record. EXPR_VA_ARG, /// \brief An AddrLabelExpr record. Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2772,6 +2772,13 @@ const Expr *Exp = cast(this)->getInitializer(); return Exp->isConstantInitializer(Ctx, false, Culprit); } + case DesignatedInitUpdateExprClass: { + const DesignatedInitUpdateExpr *DIUE = cast(this); + if (!DIUE->getBase()->isConstantInitializer(Ctx, false, Culprit)) + return false; + assert(false && "DIUE->getBase() should not be a constant expression"); + break; + } case InitListExprClass: { const InitListExpr *ILE = cast(this); if (ILE->getType()->isArrayType()) { @@ -2818,6 +2825,7 @@ break; } case ImplicitValueInitExprClass: + case NoInitExprClass: return true; case ParenExprClass: return cast(this)->getSubExpr() @@ -2925,6 +2933,7 @@ case UnaryExprOrTypeTraitExprClass: case AddrLabelExprClass: case GNUNullExprClass: + case NoInitExprClass: case CXXBoolLiteralExprClass: case CXXNullPtrLiteralExprClass: case CXXThisExprClass: @@ -2975,6 +2984,7 @@ case CompoundLiteralExprClass: case ExtVectorElementExprClass: case DesignatedInitExprClass: + case DesignatedInitUpdateExprClass: case ParenListExprClass: case CXXPseudoDestructorExprClass: case CXXStdInitializerListExprClass: @@ -3981,6 +3991,30 @@ NumDesignators = NumDesignators - 1 + NumNewDesignators; } +DesignatedInitUpdateExpr::DesignatedInitUpdateExpr(const ASTContext &C, + SourceLocation lbraceloc, Expr *baseExpr, SourceLocation rbraceloc) + : Expr(DesignatedInitUpdateExprClass, baseExpr->getType(), VK_RValue, + OK_Ordinary, false, false, false, false), + LBraceLoc(lbraceloc), RBraceLoc(rbraceloc) { + BaseAndUpdaterExprs[0] = baseExpr; + + InitListExpr *ILE = new (C) InitListExpr(C, lbraceloc, None, rbraceloc); + ILE->setType(baseExpr->getType()); + BaseAndUpdaterExprs[1] = ILE; +} + +SourceLocation DesignatedInitUpdateExpr::getLocStart() const { + if (LBraceLoc.isValid()) + return LBraceLoc; + return getBase()->getLocStart(); +} + +SourceLocation DesignatedInitUpdateExpr::getLocEnd() const { + if (RBraceLoc.isValid()) + return RBraceLoc; + return getUpdater()->getLocEnd(); +} + ParenListExpr::ParenListExpr(const ASTContext& C, SourceLocation lparenloc, ArrayRef exprs, SourceLocation rparenloc) Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -183,6 +183,8 @@ case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::AtomicExprClass: case Expr::CXXFoldExprClass: + case Expr::NoInitExprClass: + case Expr::DesignatedInitUpdateExprClass: return Cl::CL_PRValue; // Next come the complicated cases. Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -8671,6 +8671,8 @@ case Expr::CompoundLiteralExprClass: case Expr::ExtVectorElementExprClass: case Expr::DesignatedInitExprClass: + case Expr::NoInitExprClass: + case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: case Expr::ParenListExprClass: case Expr::VAArgExprClass: Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -2673,7 +2673,9 @@ // These all can only appear in local or variable-initialization // contexts and so should never appear in a mangling. case Expr::AddrLabelExprClass: + case Expr::DesignatedInitUpdateExprClass: case Expr::ImplicitValueInitExprClass: + case Expr::NoInitExprClass: case Expr::ParenListExprClass: case Expr::LambdaExprClass: case Expr::MSPropertyRefExprClass: Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1422,6 +1422,22 @@ PrintExpr(Node->getInit()); } +void StmtPrinter::VisitDesignatedInitUpdateExpr( + DesignatedInitUpdateExpr *Node) { + OS << "{"; + OS << "/*base*/"; + PrintExpr(Node->getBase()); + OS << ", "; + + OS << "/*updater*/"; + PrintExpr(Node->getUpdater()); + OS << "}"; +} + +void StmtPrinter::VisitNoInitExpr(NoInitExpr *Node) { + OS << "/*no init*/"; +} + void StmtPrinter::VisitImplicitValueInitExpr(ImplicitValueInitExpr *Node) { if (Policy.LangOpts.CPlusPlus) { OS << "/*implicit*/"; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -713,6 +713,16 @@ } } +// FIXME: how to test these functions? +void StmtProfiler::VisitDesignatedInitUpdateExpr( + const DesignatedInitUpdateExpr *S) { + llvm_unreachable("Not implemented"); +} + +void StmtProfiler::VisitNoInitExpr(const NoInitExpr *S) { + llvm_unreachable("Not implemented"); +} + void StmtProfiler::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *S) { VisitExpr(S); } Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -159,10 +159,12 @@ EmitAggLoadOfLValue(E); } + void VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E); void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO); void VisitChooseExpr(const ChooseExpr *CE); void VisitInitListExpr(InitListExpr *E); void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); + void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing. void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { Visit(DAE->getExpr()); } @@ -1050,6 +1052,9 @@ return; } else if (isa(E) || isa(E)) { return EmitNullInitializationToLValue(LV); + } else if (isa(E)) { + // Do nothing. + return; } else if (type->isReferenceType()) { RValue RV = CGF.EmitReferenceBindingToExpr(E); return CGF.EmitStoreThroughLValue(RV, LV); @@ -1270,6 +1275,15 @@ cleanupDominator->eraseFromParent(); } +void AggExprEmitter::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + AggValueSlot Dest = EnsureSlot(E->getType()); + + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddr(), E->getType(), + Dest.getAlignment()); + EmitInitializationToLValue(E->getBase(), DestLV); + VisitInitListExpr(E->getUpdater()); +} + //===----------------------------------------------------------------------===// // Entry Points into this File //===----------------------------------------------------------------------===// Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -952,6 +952,25 @@ if (ILE->getNumInits() == 0 && TryMemsetInitialization()) return; + // If we have a struct whose every field is value-initialized, we can + // usually use memset. + if (auto *ILE = dyn_cast(Init)) { + if (const RecordType *RType = ILE->getType()->getAs()) { + if (RType->getDecl()->isStruct()) { + unsigned NumFields = 0; + for (auto *Field : RType->getDecl()->fields()) + if (!Field->isUnnamedBitfield()) + ++NumFields; + if (ILE->getNumInits() == NumFields) + for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) + if (!isa(ILE->getInit(i))) + --NumFields; + if (ILE->getNumInits() == NumFields && TryMemsetInitialization()) + return; + } + } + } + // Create the loop blocks. llvm::BasicBlock *EntryBB = Builder.GetInsertBlock(); llvm::BasicBlock *LoopBB = createBasicBlock("new.loop"); Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1041,6 +1041,7 @@ case Expr::CXXReinterpretCastExprClass: case Expr::CXXStdInitializerListExprClass: case Expr::DesignatedInitExprClass: + case Expr::DesignatedInitUpdateExprClass: case Expr::ExprWithCleanupsClass: case Expr::ExtVectorElementExprClass: case Expr::InitListExprClass: @@ -1135,6 +1136,7 @@ case Expr::ImaginaryLiteralClass: case Expr::ImplicitValueInitExprClass: case Expr::IntegerLiteralClass: + case Expr::NoInitExprClass: case Expr::ObjCEncodeExprClass: case Expr::ObjCStringLiteralClass: case Expr::ObjCBoolLiteralExprClass: Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -306,7 +306,8 @@ QualType CurrentObjectType, InitListExpr *StructuredList, unsigned StructuredIndex, - SourceRange InitRange); + SourceRange InitRange, + bool IsFullyOverwritten = false); void UpdateStructuredListElement(InitListExpr *StructuredList, unsigned &StructuredIndex, Expr *expr); @@ -317,11 +318,33 @@ SourceLocation Loc, const InitializedEntity &Entity, bool VerifyOnly); + + // Explanation on the "FillWithNoInit" mode: + // + // Assume we have the following definitions (Case#1): + // struct P { char x[6][6]; } xp = { .x[1] = "bar" }; + // struct PP { struct P lp; } l = { .lp = xp, .lp.x[1][2] = 'f' }; + // + // l.lp.x[1][0..1] should not be filled with implicit initializers because the + // "base" initializer "xp" will provide values for them; l.lp.x[1] will be "baf". + // + // But if we have (Case#2): + // struct PP l = { .lp = xp, .lp.x[1] = { [2] = 'f' } }; + // + // l.lp.x[1][0..1] are implicitly initialized and do not use values from the + // "base" initializer; l.lp.x[1] will be "\0\0f\0\0\0". + // + // To distinguish Case#1 from Case#2, and also to avoid leaving many "holes" + // in the InitListExpr, the "holes" in Case#1 are filled not with empty + // initializers but with a special "NoInitExpr" place holders, which tells the + // CodeGen not to generate any initializers for these parts. void FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, - InitListExpr *ILE, bool &RequiresSecondPass); + InitListExpr *ILE, bool &RequiresSecondPass, + bool FillWithNoInit = false); void FillInEmptyInitializations(const InitializedEntity &Entity, - InitListExpr *ILE, bool &RequiresSecondPass); + InitListExpr *ILE, bool &RequiresSecondPass, + bool FillWithNoInit = false); bool CheckFlexibleArrayInit(const InitializedEntity &Entity, Expr *InitExpr, FieldDecl *Field, bool TopLevelObject); @@ -455,12 +478,26 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, const InitializedEntity &ParentEntity, InitListExpr *ILE, - bool &RequiresSecondPass) { + bool &RequiresSecondPass, + bool FillWithNoInit) { SourceLocation Loc = ILE->getLocEnd(); unsigned NumInits = ILE->getNumInits(); InitializedEntity MemberEntity = InitializedEntity::InitializeMember(Field, &ParentEntity); + + if (const RecordType *RType = ILE->getType()->getAs()) + if (!RType->getDecl()->isUnion()) + assert(Init < NumInits && "This ILE should have been expanded"); + if (Init >= NumInits || !ILE->getInit(Init)) { + if (FillWithNoInit) { + Expr *Filler = new (SemaRef.Context) NoInitExpr(Field->getType()); + if (Init < NumInits) + ILE->setInit(Init, Filler); + else + ILE->updateInit(SemaRef.Context, Init, Filler); + return; + } // C++1y [dcl.init.aggr]p7: // If there are fewer initializer-clauses in the list than there are // members in the aggregate, then each member not explicitly initialized @@ -516,7 +553,11 @@ } else if (InitListExpr *InnerILE = dyn_cast(ILE->getInit(Init))) FillInEmptyInitializations(MemberEntity, InnerILE, - RequiresSecondPass); + RequiresSecondPass, FillWithNoInit); + else if (DesignatedInitUpdateExpr *InnerDIUE + = dyn_cast(ILE->getInit(Init))) + FillInEmptyInitializations(MemberEntity, InnerDIUE->getUpdater(), + RequiresSecondPass, /*FillWithNoInit =*/ true); } /// Recursively replaces NULL values within the given initializer list @@ -525,7 +566,8 @@ void InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, InitListExpr *ILE, - bool &RequiresSecondPass) { + bool &RequiresSecondPass, + bool FillWithNoInit) { assert((ILE->getType() != SemaRef.Context.VoidTy) && "Should not have void type"); @@ -533,16 +575,27 @@ const RecordDecl *RDecl = RType->getDecl(); if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(), - Entity, ILE, RequiresSecondPass); + Entity, ILE, RequiresSecondPass, FillWithNoInit); else if (RDecl->isUnion() && isa(RDecl) && cast(RDecl)->hasInClassInitializer()) { for (auto *Field : RDecl->fields()) { if (Field->hasInClassInitializer()) { - FillInEmptyInitForField(0, Field, Entity, ILE, RequiresSecondPass); + FillInEmptyInitForField(0, Field, Entity, ILE, RequiresSecondPass, + FillWithNoInit); break; } } } else { + // The fields beyond ILE->getNumInits() are default initialized, so in + // order to leave them uninitialized, the ILE is expanded and the extra + // fields are then filled with NoInitExpr. + unsigned NumFields = 0; + for (auto *Field : RDecl->fields()) + if (!Field->isUnnamedBitfield()) + ++NumFields; + if (ILE->getNumInits() < NumFields) + ILE->resizeInits(SemaRef.Context, NumFields); + unsigned Init = 0; for (auto *Field : RDecl->fields()) { if (Field->isUnnamedBitfield()) @@ -551,7 +604,8 @@ if (hadError) return; - FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass); + FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass, + FillWithNoInit); if (hadError) return; @@ -594,7 +648,9 @@ ElementEntity.setElementIndex(Init); Expr *InitExpr = (Init < NumInits ? ILE->getInit(Init) : nullptr); - if (!InitExpr && !ILE->hasArrayFiller()) { + if (!InitExpr && Init < NumInits && ILE->hasArrayFiller()) + ILE->setInit(Init, ILE->getArrayFiller()); + else if (!InitExpr && !ILE->hasArrayFiller()) { ExprResult ElementInit = PerformEmptyInit(SemaRef, ILE->getLocEnd(), ElementEntity, /*VerifyOnly*/false); @@ -603,6 +659,10 @@ return; } + Expr *Filler = FillWithNoInit ? + new (SemaRef.Context) NoInitExpr(ElementType) : + ElementInit.getAs(); + if (hadError) { // Do nothing } else if (Init < NumInits) { @@ -609,29 +669,34 @@ // For arrays, just set the expression used for value-initialization // of the "holes" in the array. if (ElementEntity.getKind() == InitializedEntity::EK_ArrayElement) - ILE->setArrayFiller(ElementInit.getAs()); + ILE->setArrayFiller(Filler); else - ILE->setInit(Init, ElementInit.getAs()); + ILE->setInit(Init, Filler); } else { // For arrays, just set the expression used for value-initialization // of the rest of elements and exit. if (ElementEntity.getKind() == InitializedEntity::EK_ArrayElement) { - ILE->setArrayFiller(ElementInit.getAs()); + ILE->setArrayFiller(Filler); return; } - if (!isa(ElementInit.get())) { + if (!isa(Filler) && !isa(Filler)) { // Empty initialization requires a constructor call, so // extend the initializer list to include the constructor // call and make a note that we'll need to take another pass // through the initializer list. - ILE->updateInit(SemaRef.Context, Init, ElementInit.getAs()); + ILE->updateInit(SemaRef.Context, Init, Filler); RequiresSecondPass = true; } } } else if (InitListExpr *InnerILE = dyn_cast_or_null(InitExpr)) - FillInEmptyInitializations(ElementEntity, InnerILE, RequiresSecondPass); + FillInEmptyInitializations(ElementEntity, InnerILE, RequiresSecondPass, + FillWithNoInit); + else if (DesignatedInitUpdateExpr *InnerDIUE + = dyn_cast_or_null(InitExpr)) + FillInEmptyInitializations(ElementEntity, InnerDIUE->getUpdater(), + RequiresSecondPass, /*FillWithNoInit =*/ true); } } @@ -966,13 +1031,26 @@ StructuredList, StructuredIndex); if (InitListExpr *SubInitList = dyn_cast(expr)) { - if (!SemaRef.getLangOpts().CPlusPlus) { + if (SubInitList->getNumInits() == 1 && + IsStringInit(SubInitList->getInit(0), ElemType, SemaRef.Context) == + SIF_None) { + expr = SubInitList->getInit(0); + } else if (!SemaRef.getLangOpts().CPlusPlus) { InitListExpr *InnerStructuredList = getStructuredSubobjectInit(IList, Index, ElemType, StructuredList, StructuredIndex, - SubInitList->getSourceRange()); + SubInitList->getSourceRange(), true); CheckExplicitInitList(Entity, SubInitList, ElemType, InnerStructuredList); + + if (!hadError && !VerifyOnly) { + bool RequiresSecondPass = false; + FillInEmptyInitializations(Entity, InnerStructuredList, + RequiresSecondPass); + if (RequiresSecondPass && !hadError) + FillInEmptyInitializations(Entity, InnerStructuredList, + RequiresSecondPass); + } ++StructuredIndex; ++Index; return; @@ -1835,6 +1913,183 @@ } +static void partialMergeFrom(const ASTContext &C, InitListExpr *TargetExpr, + Expr *BackgroundExpr) { + assert(!TargetExpr->hasArrayFiller() && "Filler already exists!"); + + BackgroundExpr = BackgroundExpr->IgnoreParenImpCasts(); + if (CompoundLiteralExpr *CLE = + dyn_cast(BackgroundExpr)) + BackgroundExpr = CLE->getInitializer()->IgnoreParenImpCasts(); + + // A string literal or an obj-c encode expression gets decomposed into + // individual characters. + if (isa(BackgroundExpr) || + isa(BackgroundExpr)) { + const ArrayType *AT = C.getAsArrayType(TargetExpr->getType()); + assert(AT && "expecting array type"); + + QualType CharTy = AT->getElementType(); + QualType PromotedCharTy = (CharTy->isPromotableIntegerType() ? + C.getPromotedIntegerType(CharTy) : CharTy); + unsigned CharWidth = C.getTypeSize(PromotedCharTy); + + const StringLiteral *SL = dyn_cast(BackgroundExpr); + const ObjCEncodeExpr *OEE = dyn_cast(BackgroundExpr); + std::string OEE_Str; + if (OEE) + C.getObjCEncodingForType(OEE->getEncodedType(), OEE_Str); + + uint64_t StrLen = SL ? SL->getLength() : OEE_Str.size(); + const llvm::APInt &ArraySize = cast(AT)->getSize(); + if (ArraySize.ult(StrLen)) + StrLen = ArraySize.getZExtValue(); + + if (StrLen > TargetExpr->getNumInits()) + TargetExpr->resizeInits(C, StrLen); + + for (unsigned i = 0; i != StrLen; ++i) { + if (TargetExpr->getInit(i)) + continue; + + llvm::APInt Element(CharWidth, SL ? SL->getCodeUnit(i) : OEE_Str[i]); + Expr *ElementExpr = IntegerLiteral::Create(C, Element, PromotedCharTy, + BackgroundExpr->getExprLoc()); + if (CharTy != PromotedCharTy) + ElementExpr = ImplicitCastExpr::Create(C, CharTy, CK_IntegralCast, + ElementExpr, nullptr, VK_RValue); + TargetExpr->setInit(i, ElementExpr); + } + + if (ArraySize.ugt(StrLen)) + TargetExpr->setArrayFiller(new (C) ImplicitValueInitExpr(CharTy)); + + return; + } + + if (isa(BackgroundExpr)) { + unsigned NumInits = TargetExpr->getNumInits(); + + if (const RecordType *RType = TargetExpr->getType()->getAs()) { + const RecordDecl *RD = RType->getDecl(); + unsigned i = 0; + + unsigned NumFields = 0; + for (FieldDecl *FD : RD->fields()) { + if (FD->isUnnamedBitfield()) + continue; + ++NumFields; + } + if (NumInits < NumFields) + TargetExpr->resizeInits(C, NumFields); + + for (RecordDecl::field_iterator I = RD->field_begin(),E = RD->field_end(); + I != E; ++I, ++i) { + FieldDecl *FD = *I; + if (FD->isUnnamedBitfield()) { + --i; + continue; + } + + ImplicitValueInitExpr *EmptyInit = + new (C) ImplicitValueInitExpr(FD->getType()); + + if (!TargetExpr->getInit(i)) { + TargetExpr->setInit(i, EmptyInit); + continue; + } + + // If this is not an aggregate type, we are done. The current + // initializer takes effect; the background initializer is overwritten. + Expr *ChildExpr = TargetExpr->getInit(i); + if (!ChildExpr->getType()->isAggregateType()) + continue; + + assert(isa(ChildExpr) && "expecting an init list expr"); + + InitListExpr *ChildILE = cast(ChildExpr); + partialMergeFrom(C, ChildILE, EmptyInit); + + // Only look at the first initialization of a union + if (TargetExpr->getType()->isUnionType()) + break; + } + + return; + } + + QualType ElementType; + bool IsArray = false; + + if (const VectorType *VT = TargetExpr->getType()->getAs()) + ElementType = VT->getElementType(); + else if (const ArrayType *AT = C.getAsArrayType(TargetExpr->getType())) { + ElementType = AT->getElementType(); + IsArray = true; + } else + ElementType = TargetExpr->getType(); + + ImplicitValueInitExpr *EmptyInit = + new (C) ImplicitValueInitExpr(ElementType); + + if (IsArray) + TargetExpr->setArrayFiller(EmptyInit); + + for (unsigned i = 0; i != NumInits; ++i) { + if (!TargetExpr->getInit(i) && !IsArray) + TargetExpr->setInit(i, EmptyInit); + + // If this is not an aggregate type, we are done. The current + // initializer takes effect; the background initializer is overwritten. + Expr *ChildExpr = TargetExpr->getInit(i); + if (!ChildExpr->getType()->isAggregateType()) + continue; + + assert(isa(ChildExpr) && "expecting an init list expr"); + + InitListExpr *ChildILE = cast(ChildExpr); + partialMergeFrom(C, ChildILE, EmptyInit); + } + + return; + } + + assert(isa(BackgroundExpr) && + "cannot break down background initializer!"); + InitListExpr *BackgroundILE = cast(BackgroundExpr); + if (BackgroundILE->getNumInits() > TargetExpr->getNumInits()) + TargetExpr->resizeInits(C, BackgroundILE->getNumInits()); + + for (unsigned i = 0; i != TargetExpr->getNumInits(); ++i) { + if (i >= BackgroundILE->getNumInits() || !BackgroundILE->getInit(i)) + // Nothing to merge from. + continue; + + if (!TargetExpr->getInit(i)) { + TargetExpr->setInit(i, BackgroundILE->getInit(i)); + continue; + } + + // If this is not an aggregate type, we are done. The current initializer + // takes effect; the background initializer is overwritten. + Expr *ChildExpr = TargetExpr->getInit(i); + if (!ChildExpr->getType()->isAggregateType()) + continue; + + assert(isa(ChildExpr) && "expecting an init list expr"); + + InitListExpr *ChildILE = cast(ChildExpr); + partialMergeFrom(C, ChildILE, BackgroundILE->getInit(i)); + + // Only look at the first initialization of a union + if (TargetExpr->getType()->isUnionType()) + break; + } + + if (BackgroundILE->hasArrayFiller()) + TargetExpr->setArrayFiller(BackgroundILE->getArrayFiller()); +} + /// @brief Check the well-formedness of a C99 designated initializer. /// /// Determines whether the designated initializer @p DIE, which @@ -1913,11 +2168,82 @@ // Determine the structural initializer list that corresponds to the // current subobject. - StructuredList = IsFirstDesignator? SyntacticToSemantic.lookup(IList) - : getStructuredSubobjectInit(IList, Index, CurrentObjectType, - StructuredList, StructuredIndex, - SourceRange(D->getLocStart(), - DIE->getLocEnd())); + if (IsFirstDesignator) + StructuredList = SyntacticToSemantic.lookup(IList); + else { + Expr *ExistingInit = StructuredIndex < StructuredList->getNumInits() ? + StructuredList->getInit(StructuredIndex) : nullptr; + if (!ExistingInit && StructuredList->hasArrayFiller()) + ExistingInit = StructuredList->getArrayFiller(); + + if (!ExistingInit) + StructuredList = + getStructuredSubobjectInit(IList, Index, CurrentObjectType, + StructuredList, StructuredIndex, + SourceRange(D->getLocStart(), + DIE->getLocEnd())); + else if (InitListExpr *Result = dyn_cast(ExistingInit)) + StructuredList = Result; + else if (ExistingInit->isConstantInitializer(SemaRef.Context, false)) { + InitListExpr *Result = new (SemaRef.Context) InitListExpr( + SemaRef.Context, D->getLocStart(), None, DIE->getLocEnd()); + + QualType ResultType = CurrentObjectType; + if (!ResultType->isArrayType()) + ResultType = ResultType.getNonLValueExprType(SemaRef.Context); + Result->setType(ResultType); + + // If we decomposed an aggregate initializer into individual components, + // we do not issue diagnostics here; instead we wait till later and + // issue diagnostics on each individual component. + partialMergeFrom(SemaRef.Context, Result, ExistingInit); + ExistingInit = nullptr; + StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result); + StructuredList = Result; + } + else { + if (DesignatedInitUpdateExpr *E = + dyn_cast(ExistingInit)) + StructuredList = E->getUpdater(); + else { + DesignatedInitUpdateExpr *DIUE = + new (SemaRef.Context) DesignatedInitUpdateExpr(SemaRef.Context, + D->getLocStart(), ExistingInit, + DIE->getLocEnd()); + StructuredList->updateInit(SemaRef.Context, StructuredIndex, DIUE); + StructuredList = DIUE->getUpdater(); + } + + // We are creating an initializer list that initializes the + // subobjects of the current object, but there was already an + // initialization that completely initialized the current + // subobject, e.g., by a compound literal: + // + // struct X { int a, b; }; + // struct X xs[] = { [0] = (struct X) { 1, 2 }, [0].b = 3 }; + // + // Here, xs[0].a == 0 and xs[0].b == 3, since the second, + // designated initializer re-initializes the whole + // subobject [0], overwriting previous initializers. + SemaRef.Diag(D->getLocStart(), + diag::warn_subobject_initializer_overrides) + << SourceRange(D->getLocStart(), DIE->getLocEnd()); + + // We need to check on source range validity because the previous + // initializer does not have to be an explicit initializer. e.g., + // + // struct P { int a, b; }; + // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 }; + // + // There is an overwrite taking place because the first braced initializer + // list "{ .a = 2 }" already provides value for .p.b (which is zero). + if (ExistingInit->getSourceRange().isValid()) + SemaRef.Diag(ExistingInit->getLocStart(), + diag::note_previous_initializer) + << /*FIXME:has side effects=*/0 + << ExistingInit->getSourceRange(); + } + } assert(StructuredList && "Expected a structured initializer list"); } @@ -2367,7 +2693,8 @@ QualType CurrentObjectType, InitListExpr *StructuredList, unsigned StructuredIndex, - SourceRange InitRange) { + SourceRange InitRange, + bool IsFullyOverwritten) { if (VerifyOnly) return nullptr; // No structured list in verification-only mode. Expr *ExistingInit = nullptr; @@ -2377,7 +2704,16 @@ ExistingInit = StructuredList->getInit(StructuredIndex); if (InitListExpr *Result = dyn_cast_or_null(ExistingInit)) - return Result; + // There might have already been initializers for subobjects of the current + // object, but a subsequent initializer list will overwrite the entirety + // of the current object. (See DR 253 and C99 6.7.8p21). e.g., + // + // struct P { char x[6]; }; + // struct P l = { .x[2] = 'x', .x = { [0] = 'f' } }; + // + // The first designated initializer is ignored, and l.x is just "f". + if (!IsFullyOverwritten) + return Result; if (ExistingInit) { // We are creating an initializer list that initializes the @@ -2472,11 +2808,28 @@ SemaRef.Diag(expr->getLocStart(), diag::warn_initializer_overrides) << expr->getSourceRange(); - SemaRef.Diag(PrevInit->getLocStart(), - diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 - << PrevInit->getSourceRange(); + + // We need to check on source range validity because the previous + // initializer does not have to be an explicit initializer. + // struct P { int a, b; }; + // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 }; + // There is an overwrite taking place because the first braced initializer + // list "{ .a = 2 }' already provides value for .p.b (which is zero). + if (PrevInit->getSourceRange().isValid()) + SemaRef.Diag(PrevInit->getLocStart(), + diag::note_previous_initializer) + << /*FIXME:has side effects=*/0 + << PrevInit->getSourceRange(); } + // It is also possible to overwrite an implicitly initialized array element: + // struct S { char L[6]; } var[] = { + // { { 'f', 'o', 'o' } }, // var[0].L[3..6] zero-initialized, and then + // [0].L[4] = 'x' }; // var[0].L[4] was overriden + else if (StructuredList->hasArrayFiller()) { + SemaRef.Diag(expr->getLocStart(), + diag::warn_initializer_overrides) + << expr->getSourceRange(); + } ++StructuredIndex; } Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -7923,8 +7923,26 @@ E->usesGNUSyntax(), Init.get()); } +// Seems that if TransformInitListExpr() only works on the syntactic form of an +// InitListExpr, then a DesignatedInitUpdateExpr is not encountered. template ExprResult +TreeTransform::TransformDesignatedInitUpdateExpr( + DesignatedInitUpdateExpr *E) { + llvm_unreachable("not implemented"); + return ExprError(); +} + +template +ExprResult +TreeTransform::TransformNoInitExpr( + NoInitExpr *E) { + llvm_unreachable("not implemented"); + return ExprError(); +} + +template +ExprResult TreeTransform::TransformImplicitValueInitExpr( ImplicitValueInitExpr *E) { TemporaryBase Rebase(*this, E->getLocStart(), DeclarationName()); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -801,6 +801,18 @@ Designators.data(), Designators.size()); } +void ASTStmtReader::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + VisitExpr(E); + E->setLBraceLoc(ReadSourceLocation(Record, Idx)); + E->setRBraceLoc(ReadSourceLocation(Record, Idx)); + E->setBase(Reader.ReadSubExpr()); + E->setUpdater(Reader.ReadSubExpr()); +} + +void ASTStmtReader::VisitNoInitExpr(NoInitExpr *E) { + VisitExpr(E); +} + void ASTStmtReader::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { VisitExpr(E); } @@ -2506,10 +2518,18 @@ break; + case EXPR_DESIGNATED_INIT_UPDATE: + S = new (Context) DesignatedInitUpdateExpr(Empty); + break; + case EXPR_IMPLICIT_VALUE_INIT: S = new (Context) ImplicitValueInitExpr(Empty); break; + case EXPR_NO_INIT: + S = new (Context) NoInitExpr(Empty); + break; + case EXPR_VA_ARG: S = new (Context) VAArgExpr(Empty); break; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -774,7 +774,9 @@ RECORD(EXPR_EXT_VECTOR_ELEMENT); RECORD(EXPR_INIT_LIST); RECORD(EXPR_DESIGNATED_INIT); + RECORD(EXPR_DESIGNATED_INIT_UPDATE); RECORD(EXPR_IMPLICIT_VALUE_INIT); + RECORD(EXPR_NO_INIT); RECORD(EXPR_VA_ARG); RECORD(EXPR_ADDR_LABEL); RECORD(EXPR_STMT); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -737,6 +737,20 @@ Code = serialization::EXPR_DESIGNATED_INIT; } +void ASTStmtWriter::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + VisitExpr(E); + Writer.AddSourceLocation(E->getLBraceLoc(), Record); + Writer.AddSourceLocation(E->getRBraceLoc(), Record); + Writer.AddStmt(E->getBase()); + Writer.AddStmt(E->getUpdater()); + Code = serialization::EXPR_DESIGNATED_INIT_UPDATE; +} + +void ASTStmtWriter::VisitNoInitExpr(NoInitExpr *E) { + VisitExpr(E); + Code = serialization::EXPR_NO_INIT; +} + void ASTStmtWriter::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { VisitExpr(E); Code = serialization::EXPR_IMPLICIT_VALUE_INIT; Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -859,6 +859,7 @@ // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: case Stmt::ObjCAtCatchStmtClass: @@ -891,6 +892,7 @@ case Stmt::CXXBoolLiteralExprClass: case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: + case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: Index: test/Analysis/designated-initializer.c =================================================================== --- test/Analysis/designated-initializer.c +++ test/Analysis/designated-initializer.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG %s 2>&1 \ +// RUN: | FileCheck %s + +struct Q { int a, b, c; }; +union UQ { struct Q q; }; +union UQ getUQ() { + union UQ u = { { 1, 2, 3 } }; + return u; +} + +void test() { + struct LUQ { union UQ uq; } var = { getUQ(), .uq.q.a = 100 }; + struct Q s[] = { + [0] = (struct Q){1, 2}, + [0].c = 3 + }; +} + +// CHECK: void test() +// CHECK: [B1] +// CHECK: 1: getUQ +// CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, union UQ (*)()) +// CHECK: 3: [B1.2]() +// CHECK: 4: 100 +// CHECK: 5: /*no init*/ +// CHECK: 6: /*no init*/ +// CHECK: 7: {[B1.4], [B1.5], [B1.6]} +// CHECK: 8: {[B1.7]} +// CHECK: 9: {/*base*/[B1.3], /*updater*/[B1.8]} +// CHECK: 10: {[B1.3], .uq.q.a = [B1.4]} +// CHECK: 11: struct LUQ var = {getUQ(), .uq.q.a = 100}; +// CHECK: 12: 1 +// CHECK: 13: 2 +// CHECK: 14: 3 +// CHECK: 15: {[B1.12], [B1.13], [B1.14]} +// CHECK: 16: {[0] = (struct Q){[B1.12], [B1.13]}, [0].c = [B1.14]} +// CHECK: 17: struct Q s[] = {[0] = (struct Q){1, 2}, [0].c = 3}; Index: test/CodeGen/Inputs/stdio.h =================================================================== --- test/CodeGen/Inputs/stdio.h +++ test/CodeGen/Inputs/stdio.h @@ -1,4 +1,5 @@ struct FILE; +extern int printf(const char *format, ...); extern int vfprintf(struct FILE *s, const char *format, __builtin_va_list arg); extern int vprintf(const char *format, __builtin_va_list arg); Index: test/CodeGen/partial-reinitialization1.c =================================================================== --- test/CodeGen/partial-reinitialization1.c +++ test/CodeGen/partial-reinitialization1.c @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -isystem %S/Inputs %s -emit-llvm -o - | FileCheck %s + +struct P1 { + struct Q1 { + char a[6]; + char b[6]; + } q; +}; + +// CHECK: { [6 x i8] c"foo\00\00\00", [6 x i8] c"\00x\00\00\00\00" } +struct P1 l1 = { + (struct Q1){ "foo", "bar" }, + .q.b = { "boo" }, + .q.b = { [1] = 'x' } +}; + +// CHECK: { [6 x i8] c"foo\00\00\00", [6 x i8] c"bxo\00\00\00" } +struct P1 l1a = { + (struct Q1){ "foo", "bar" }, + .q.b = { "boo" }, + .q.b[1] = 'x' +}; + + +struct P2 { char x[6]; }; + +// CHECK: { [6 x i8] c"n\00\00\00\00\00" } +struct P2 l2 = { + .x = { [1] = 'o' }, + .x = { [0] = 'n' } +}; + +struct P3 { + struct Q3 { + struct R1 { + int a, b, c; + } r1; + + struct R2 { + int d, e, f; + } r2; + } q; +}; + +// CHECK: @l3 = global %struct.P3 { %struct.Q3 { %struct.R1 { i32 1, i32 2, i32 3 }, %struct.R2 { i32 0, i32 10, i32 0 } } } +struct P3 l3 = { + (struct Q3){ { 1, 2, 3 }, { 4, 5, 6 } }, + .q.r2 = { 7, 8, 9 }, + .q.r2 = { .e = 10 } +}; + +// This bit is taken from Sema/wchar.c so we can avoid the wchar.h include. +typedef __WCHAR_TYPE__ wchar_t; + +struct P4 { + wchar_t x[6]; +}; + +// CHECK: { [6 x i32] [i32 102, i32 111, i32 120, i32 0, i32 0, i32 0] } +struct P4 l4 = { { L"foo" }, .x[2] = L'x' }; + +struct P5 { + int x; + struct Q5 { + int a, b, c; + } q; + int y; +}; + +// A three-pass test +// CHECK: @l5 = global %struct.P5 { i32 1, %struct.Q5 { i32 6, i32 9, i32 8 }, i32 5 } +struct P5 l5 = { 1, { 2, 3, 4 }, 5, + .q = { 6, 7, 8 }, + .q.b = 9 }; Index: test/CodeGen/partial-reinitialization2.c =================================================================== --- test/CodeGen/partial-reinitialization2.c +++ test/CodeGen/partial-reinitialization2.c @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -isystem %S/Inputs %s -emit-llvm -o - \ +// RUN: | lli -force-interpreter | FileCheck %s + +#include + +struct P1 { char x[6]; } g1 = { "foo" }; +struct LP1 { struct P1 p1; }; + +struct P2 { int a, b, c; } g2 = { 1, 2, 3 }; +struct LP2 { struct P2 p2; }; +struct LP2P2 { struct P2 p1, p2; }; +union UP2 { struct P2 p2; }; + +struct LP3 { struct P1 p1[2]; } g3 = { { "dog" }, { "cat" } }; +struct LLP3 { struct LP3 l3; }; +union ULP3 { struct LP3 l3; }; + +void test1(void) +{ // CHECK: fox + struct LP1 l = { .p1 = g1, .p1.x[2] = 'x' }; + int i; + for (i = 0; i < 6; i ++) + printf("%c", l.p1.x[i]); + printf("\n"); +} + +void test2(void) +{ // CHECK: fro + struct LP1 l = { .p1 = g1, .p1.x[1] = 'r' }; + int i; + for (i = 0; i < 6; i ++) + printf("%c", l.p1.x[i]); + printf("\n"); +} + +void test3(void) +{ // CHECK: 1 10 3 + struct LP2 l = { .p2 = g2, .p2.b = 10 }; + printf("%d %d %d\n", l.p2.a, l.p2.b, l.p2.c); +} + +#if 0 +// FIXME: with -force-interpreter, the use of struct type here ran into bv21177 +// without -force-interpreter, MCJIT on Windows does not quite work yet. +struct P2 get235() +{ + struct P2 p = { 2, 3, 5 }; + return p; +} + +struct LP2P2 get456789() +{ + struct LP2P2 l = { { 4, 5, 6 }, { 7, 8, 9 } }; + return l; +} + +union UP2 get123() +{ + union UP2 u = { { 1, 2, 3 } }; + return u; +} +#else +struct P2 my235 = { 2, 3, 5 }; +struct LP2P2 my456789 = { { 4, 5, 6 }, { 7, 8, 9 } }; +union UP2 my123 = { { 1, 2, 3 } }; +#define get235() my235 +#define get456789() my456789 +#define get123() my123 +#endif + +void test4(void) +{ // CHECK: 100 2 3 + struct LUP2 { union UP2 up; } var = { get123(), .up.p2.a = 100 }; + printf("%d %d %d\n", var.up.p2.a, var.up.p2.b, var.up.p2.c); +} + +void test5(void) +{ // CHECK: 0 78 0 0 0 0 + struct LLP3 var = { .l3 = g3, .l3.p1 = { [0] = g1 }, .l3.p1[1].x[1] = 'x' }; + int i; + for (i = 0; i < 6; i ++) + printf("%x ", var.l3.p1[1].x[i]); + printf("\n"); +} + +void test6(void) +{ // CHECK: 2 10 5 : 7 8 9 + struct LLP2P2 { struct LP2P2 lp; } var = { get456789(), + .lp.p1 = get235(), + .lp.p1.b = 10 }; + printf("%d %d %d : %d %d %d\n", + var.lp.p1.a, var.lp.p1.b, var.lp.p1.c, + var.lp.p2.a, var.lp.p2.b, var.lp.p2.c); +} + +int main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + return 0; +} Index: test/PCH/designated-init.c.h =================================================================== --- test/PCH/designated-init.c.h +++ test/PCH/designated-init.c.h @@ -40,3 +40,25 @@ }, } }; + +struct P1 { + struct Q1 { + char a[6]; + char b[6]; + } q; +}; + +struct P1 l1 = { + (struct Q1){ "foo", "bar" }, + .q.b = { "boo" }, + .q.b = { [1] = 'x' } +}; + +extern struct Q1 *foo(); +static struct P1 test_foo() { + struct P1 l = { *foo(), + .q.b = { "boo" }, + .q.b = { [1] = 'x' } + }; + return l; +} Index: test/Sema/designated-initializers.c =================================================================== --- test/Sema/designated-initializers.c +++ test/Sema/designated-initializers.c @@ -45,8 +45,8 @@ struct point array2[10] = { [10].x = 2.0, // expected-error{{array designator index (10) exceeds array bounds (10)}} - [4 ... 5].y = 2.0, - [4 ... 6] = { .x = 3, .y = 4.0 } + [4 ... 5].y = 2.0, // expected-note 2 {{previous initialization is here}} + [4 ... 6] = { .x = 3, .y = 4.0 } // expected-warning 2 {{subobject initialization overrides initialization of other fields within its enclosing subobject}} }; struct point array3[10] = { @@ -130,10 +130,10 @@ void test() { struct X xs[] = { [0] = (struct X){1, 2}, // expected-note{{previous initialization is here}} - [0].c = 3, // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}} + [0].c = 3, // expected-warning{{initializer overrides prior initialization of this subobject}} (struct X) {4, 5, 6}, // expected-note{{previous initialization is here}} - [1].b = get8(), // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}} - [0].b = 8 + [1].b = get8(), // expected-warning{{initializer overrides prior initialization of this subobject}} + [0].b = 8 // expected-warning{{initializer overrides prior initialization of this subobject}} }; } @@ -318,7 +318,7 @@ .a = 1 // expected-note{{previous initialization is here}} } }, .a = 2, // expected-warning{{initializer overrides prior initialization of this subobject}} - .b = 3 + .b = 3 // expected-warning{{initializer overrides prior initialization of this subobject}} }; struct ds ds1 = { .c = 0 }; struct ds ds2 = { { { @@ -339,5 +339,15 @@ int M; } overwrite_string2[] = { { { "foo" }, 1 }, - [0].L[4] = 'x' // no-warning + [0].L[4] = 'x' // expected-warning{{initializer overrides prior initialization of this subobject}} }; +struct overwrite_string_struct +overwrite_string3[] = { + "foo", 1, + [0].L[4] = 'x' // expected-warning{{initializer overrides prior initialization of this subobject}} +}; +struct overwrite_string_struct +overwrite_string4[] = { + { { 'f', 'o', 'o' }, 1 }, + [0].L[4] = 'x' // expected-warning{{initializer overrides prior initialization of this subobject}} +}; Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -235,11 +235,13 @@ case Stmt::CXXUuidofExprClass: case Stmt::ChooseExprClass: case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: case Stmt::ExprWithCleanupsClass: case Stmt::ExpressionTraitExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImplicitCastExprClass: case Stmt::ImplicitValueInitExprClass: + case Stmt::NoInitExprClass: case Stmt::MaterializeTemporaryExprClass: case Stmt::ObjCIndirectCopyRestoreExprClass: case Stmt::OffsetOfExprClass: