Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3765,6 +3765,14 @@ /// - the semantic form, if this is in syntactic form. llvm::PointerIntPair AltForm; + // In cases like: + // struct P { char x[6]; } pvar = { "foo" }; + // struct PP { struct P p; } ppvar = { .p = pvar, .p.x[1] = 'b' }; + // + // In the InitListExpr corresponding to "ppvar.p", PrevInitExpr will hold the + // Expr corresponding to the variable pvar. + Expr *PrevInitExpr; + /// \brief Either: /// If this initializer list initializes an array with more elements than /// there are initializers in the list, specifies an expression to be used @@ -3780,7 +3788,7 @@ /// \brief Build an empty initializer list. explicit InitListExpr(EmptyShell Empty) - : Expr(InitListExprClass, Empty) { } + : Expr(InitListExprClass, Empty), PrevInitExpr(nullptr) { } unsigned getNumInits() const { return InitExprs.size(); } @@ -3830,6 +3838,14 @@ /// accommodate the new entry. Expr *updateInit(const ASTContext &C, unsigned Init, Expr *expr); + Expr *getPrevInitExpr() const { return PrevInitExpr; } + void setPrevInitExpr(Expr *newExpr) { PrevInitExpr = newExpr; } + + /// \brief If this initializer only partially initializes the declarator, + /// the background initializer will initialize the rest. The background + /// initializer is assumed to be evalutable at compile time. + void partialMergeFrom(const ASTContext &C, Expr *BackgroundExpr); + /// \brief If this initializer list initializes an array with more elements /// than there are initializers in the list, specifies an expression to be /// used for value initialization of the rest of the elements. Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1871,7 +1871,8 @@ : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, false, false), InitExprs(C, initExprs.size()), - LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), AltForm(nullptr, true) + LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), AltForm(nullptr, true), + PrevInitExpr(nullptr) { sawArrayRangeDesignator(false); for (unsigned I = 0; I != initExprs.size(); ++I) { @@ -1909,6 +1910,95 @@ return Result; } +void InitListExpr::partialMergeFrom(const ASTContext &C, Expr *BackgroundExpr) { + assert(!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(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 > InitExprs.size()) + InitExprs.resize(C, StrLen, nullptr); + + for (unsigned i = 0; i != StrLen; ++i) { + if (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); + setInit(i, ElementExpr); + } + + if (ArraySize.ugt(StrLen)) + setArrayFiller(new (C) ImplicitValueInitExpr(CharTy)); + + return; + } + + assert(isa(BackgroundExpr) && + "cannot break down background initializer!"); + InitListExpr *BackgroundILE = cast(BackgroundExpr); + if (BackgroundILE->getNumInits() > InitExprs.size()) + InitExprs.resize(C, BackgroundILE->getNumInits(), nullptr); + + for (unsigned i = 0; i != getNumInits(); ++i) { + if (i >= BackgroundILE->getNumInits() || !BackgroundILE->getInit(i)) + // Nothing to merge from + continue; + + if (!getInit(i)) { + 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 = getInit(i); + if (!ChildExpr->getType()->isAggregateType()) + continue; + + assert(isa(ChildExpr) && "expecting an init list expr"); + + InitListExpr *ChildILE = cast(ChildExpr); + ChildILE->partialMergeFrom(C, BackgroundILE->getInit(i)); + + // Only look at the first initialization of a union + if (getType()->isUnionType()) + break; + } + + if (BackgroundILE->hasArrayFiller()) + setArrayFiller(BackgroundILE->getArrayFiller()); +} + void InitListExpr::setArrayFiller(Expr *filler) { assert(!hasArrayFiller() && "Filler already set!"); ArrayFillerOrUnionFieldInit = filler; @@ -2759,11 +2849,15 @@ } case InitListExprClass: { const InitListExpr *ILE = cast(this); + if (ILE->getPrevInitExpr()) + return ILE->getPrevInitExpr()->isConstantInitializer(Ctx, false, Culprit); + if (ILE->getType()->isArrayType()) { unsigned numInits = ILE->getNumInits(); for (unsigned i = 0; i < numInits; i++) { - if (!ILE->getInit(i)->isConstantInitializer(Ctx, false, Culprit)) - return false; + if (const Expr *Init = ILE->getInit(i)) + if (!Init->isConstantInitializer(Ctx, false, Culprit)) + return false; } return true; } @@ -2782,6 +2876,8 @@ if (ElementNo < ILE->getNumInits()) { const Expr *Elt = ILE->getInit(ElementNo++); + if (!Elt) + continue; if (Field->isBitField()) { // Bitfields have to evaluate to an integer. llvm::APSInt ResultTmp; @@ -2972,6 +3068,9 @@ if (const Expr *E = cast(this)->getArrayFiller()) if (E->HasSideEffects(Ctx)) return true; + if (const Expr *E = cast(this)->getPrevInitExpr()) + if (E->HasSideEffects(Ctx)) + return true; break; case GenericSelectionExprClass: Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -5265,7 +5265,7 @@ LValue Subobject = This; - bool HaveInit = ElementNo < E->getNumInits(); + bool HaveInit = ElementNo < E->getNumInits() && E->getInit(ElementNo); // FIXME: Diagnostics here should point to the end of the initializer // list, not the start. @@ -5725,7 +5725,8 @@ Subobject.addArray(Info, E, CAT); for (unsigned Index = 0; Index != NumEltsToInit; ++Index) { const Expr *Init = - Index < E->getNumInits() ? E->getInit(Index) : FillerExpr; + (Index < E->getNumInits() && E->getInit(Index)) ? + E->getInit(Index) : FillerExpr; if (!EvaluateInPlace(Result.getArrayInitializedElt(Index), Info, Subobject, Init) || !HandleLValueArrayAdjustment(Info, Init, Subobject, Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -2706,7 +2706,8 @@ Out << "il"; const InitListExpr *InitList = cast(E); for (unsigned i = 0, e = InitList->getNumInits(); i != e; ++i) - mangleExpression(InitList->getInit(i)); + if (InitList->getInit(i)) + mangleExpression(InitList->getInit(i)); Out << "E"; break; } @@ -2786,7 +2787,8 @@ // Only take InitListExprs apart for list-initialization. const InitListExpr *InitList = cast(Init); for (unsigned i = 0, e = InitList->getNumInits(); i != e; ++i) - mangleExpression(InitList->getInit(i)); + if (InitList->getInit(i)) + mangleExpression(InitList->getInit(i)); } else mangleExpression(Init); } Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -433,6 +433,7 @@ } llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); + Expr *Filler = E->getArrayFiller(); // The 'current element to initialize'. The invariants on this // variable are complicated. Essentially, after each iteration of @@ -454,17 +455,19 @@ } LValue elementLV = CGF.MakeAddrLValue(element, elementType); - EmitInitializationToLValue(E->getInit(i), elementLV); + if (E->getInit(i)) + EmitInitializationToLValue(E->getInit(i), elementLV); + else if (Filler) + EmitInitializationToLValue(Filler, elementLV); } // Check whether there's a non-trivial array-fill expression. - Expr *filler = E->getArrayFiller(); - bool hasTrivialFiller = isTrivialFiller(filler); + bool hasTrivialFiller = isTrivialFiller(Filler); // Any remaining elements need to be zero-initialized, possibly // using the filler expression. We can skip this if the we're // emitting to zeroed memory. - if (NumInitElements != NumArrayElements && + if (NumInitElements != NumArrayElements && Filler && !(Dest.isZeroed() && hasTrivialFiller && CGF.getTypes().isZeroInitializable(elementType))) { @@ -493,10 +496,7 @@ // Emit the actual filler expression. LValue elementLV = CGF.MakeAddrLValue(currentElement, elementType); - if (filler) - EmitInitializationToLValue(filler, elementLV); - else - EmitNullInitializationToLValue(elementLV); + EmitInitializationToLValue(Filler, elementLV); // Move on to the next element. llvm::Value *nextElement = @@ -1119,6 +1119,8 @@ LValue DestLV = CGF.MakeAddrLValue(Dest.getAddr(), E->getType(), Dest.getAlignment()); + if (E->getPrevInitExpr()) + EmitInitializationToLValue(E->getPrevInitExpr(), DestLV); // Handle initialization of an array. if (E->getType()->isArrayType()) { @@ -1217,14 +1219,12 @@ LValue LV = CGF.EmitLValueForFieldInitialization(DestLV, field); // We never generate write-barries for initialized fields. LV.setNonGC(true); - - if (curInitIndex < NumInitElements) { + + if (curInitIndex < NumInitElements && E->getInit(curInitIndex)) { // Store the initializer into the field. EmitInitializationToLValue(E->getInit(curInitIndex++), LV); - } else { - // We're out of initalizers; default-initialize to null - EmitNullInitializationToLValue(LV); - } + } else + ++curInitIndex; // Push a destructor if necessary. // FIXME: if we have an array of structures, all explicitly @@ -1306,7 +1306,7 @@ if (Field->getType()->isReferenceType()) NumNonZeroBytes += CGF.getContext().toCharUnitsFromBits( CGF.getTarget().getPointerWidth(0)); - else + else if (E) NumNonZeroBytes += GetNumNonZeroBytesInInit(E, CGF); } @@ -1317,7 +1317,8 @@ CharUnits NumNonZeroBytes = CharUnits::Zero(); for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) - NumNonZeroBytes += GetNumNonZeroBytesInInit(ILE->getInit(i), CGF); + if (ILE->getInit(i)) + NumNonZeroBytes += GetNumNonZeroBytesInInit(ILE->getInit(i), CGF); return NumNonZeroBytes; } Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -943,10 +943,24 @@ assert(getContext().hasSameUnqualifiedType(ElementType, Init->getType()) && "got wrong type of element to initialize"); - // If we have an empty initializer list, we can usually use memset. - if (auto *ILE = dyn_cast(Init)) - 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(); Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -746,6 +746,14 @@ unsigned NumInitElements = ILE->getNumInits(); unsigned NumElements = AType->getNumElements(); + // Get filler value for implicitly initialized array elements + // FIXME: This doesn't handle member pointers correctly! + llvm::Constant *fillC; + if (Expr *filler = ILE->getArrayFiller()) + fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF); + else + fillC = llvm::Constant::getNullValue(ElemTy); + // Initialising an array requires us to automatically // initialise any elements that have not been initialised explicitly unsigned NumInitableElts = std::min(NumInitElements, NumElements); @@ -752,12 +760,14 @@ // Copy initializer elements. std::vector Elts; - Elts.reserve(NumInitableElts + NumElements); + Elts.reserve(NumElements); bool RewriteType = false; - for (unsigned i = 0; i < NumInitableElts; ++i) { - Expr *Init = ILE->getInit(i); - llvm::Constant *C = CGM.EmitConstantExpr(Init, Init->getType(), CGF); + for (unsigned i = 0; i < NumElements; ++i) { + Expr *Init = i < NumInitElements ? ILE->getInit(i) : nullptr; + llvm::Constant *C = fillC; + if (Init) + C = CGM.EmitConstantExpr(Init, Init->getType(), CGF); if (!C) return nullptr; RewriteType |= (C->getType() != ElemTy); @@ -764,17 +774,9 @@ Elts.push_back(C); } - // Initialize remaining array elements. - // FIXME: This doesn't handle member pointers correctly! - llvm::Constant *fillC; - if (Expr *filler = ILE->getArrayFiller()) - fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF); - else - fillC = llvm::Constant::getNullValue(ElemTy); if (!fillC) return nullptr; RewriteType |= (fillC->getType() != ElemTy); - Elts.resize(NumElements, fillC); if (RewriteType) { // FIXME: Try to avoid packing the array @@ -799,6 +801,8 @@ } llvm::Constant *VisitInitListExpr(InitListExpr *ILE) { + assert(!ILE->getPrevInitExpr() && "unexpected non-const initializer"); + if (ILE->getType()->isArrayType()) return EmitArrayInitialization(ILE); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8278,7 +8278,8 @@ isInitList = true; InitFieldIndex.push_back(0); for (auto Child : InitList->children()) { - CheckExpr(cast(Child)); + if (Child) + CheckExpr(cast(Child)); ++InitFieldIndex.back(); } InitFieldIndex.pop_back(); 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); @@ -460,6 +461,25 @@ unsigned NumInits = ILE->getNumInits(); InitializedEntity MemberEntity = InitializedEntity::InitializeMember(Field, &ParentEntity); + + // Assume we have the following definitions: + // + // struct P { char x[6][6]; }; + // struct P xp = { .x[1] = "bar" }; + // struct PP { struct P lp; }; + // struct PP l = { .lp = xp, .lp.x[1][2] = 'f' }; + // + // l.lp.x[1][0..1] should not be filled with implicit initializers because the + // previous initializers will provide values for them; l.lp.x[1] will be "baf". + // + // But if we have: + // 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 + // previous initializer; l.lp.x[1] will be "\0\0f\0\0\0". + if (ILE->getPrevInitExpr()) + return; + if (Init >= NumInits || !ILE->getInit(Init)) { // C++1y [dcl.init.aggr]p7: // If there are fewer initializer-clauses in the list than there are @@ -525,6 +545,24 @@ assert((ILE->getType() != SemaRef.Context.VoidTy) && "Should not have void type"); + // Assume we have the following definitions: + // + // struct P { char x[6][6]; }; + // struct P xp = { .x[1] = "bar" }; + // struct PP { struct P lp; }; + // struct PP l = { .lp = xp, .lp.x[1][2] = 'f' }; + // + // l.lp.x[1][0..1] should not be filled with implicit initializers because the + // previous initializers will provide values for them; l.lp.x[1] will be "baf". + // + // But if we have: + // 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 + // previous initializer; l.lp.x[1] will be "\0\0f\0\0\0". + if (ILE->getPrevInitExpr()) + return; + if (const RecordType *RType = ILE->getType()->getAs()) { const RecordDecl *RDecl = RType->getDecl(); if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) @@ -539,6 +577,13 @@ } } } else { + 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()) @@ -785,6 +830,10 @@ return; } + SourceLocation LocStart = IList->getInit(Index) ? + IList->getInit(Index)->getLocStart() : IList->getLocStart(); + SourceRange SrcRange = IList->getInit(Index) ? + IList->getInit(Index)->getSourceRange() : IList->getSourceRange(); if (StructuredIndex == 1 && IsStringInit(StructuredList->getInit(0), T, SemaRef.Context) == SIF_None) { @@ -794,8 +843,7 @@ hadError = true; } // Special-case - SemaRef.Diag(IList->getInit(Index)->getLocStart(), DK) - << IList->getInit(Index)->getSourceRange(); + SemaRef.Diag(LocStart, DK) << SrcRange; } else if (!T->isIncompleteType()) { // Don't complain for incomplete types, since we'll get an error // elsewhere @@ -817,8 +865,7 @@ hadError = true; } - SemaRef.Diag(IList->getInit(Index)->getLocStart(), DK) - << initKind << IList->getInit(Index)->getSourceRange(); + SemaRef.Diag(LocStart, DK) << initKind << SrcRange; } } @@ -900,20 +947,36 @@ StructuredList, StructuredIndex); if (InitListExpr *SubInitList = dyn_cast(expr)) { - if (!ElemType->isRecordType() || ElemType->isAggregateType()) { - InitListExpr *InnerStructuredList - = getStructuredSubobjectInit(IList, Index, ElemType, - StructuredList, StructuredIndex, - SubInitList->getSourceRange()); - CheckExplicitInitList(Entity, SubInitList, ElemType, - InnerStructuredList); - ++StructuredIndex; - ++Index; - return; + if (SubInitList->getNumInits() == 1 && + IsStringInit(SubInitList->getInit(0), ElemType, SemaRef.Context) == + SIF_None) { + expr = SubInitList->getInit(0); + } else { + if (!ElemType->isRecordType() || ElemType->isAggregateType()) { + InitListExpr *InnerStructuredList + = getStructuredSubobjectInit(IList, Index, ElemType, + StructuredList, StructuredIndex, + 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; + } + assert(SemaRef.getLangOpts().CPlusPlus && + "non-aggregate records are only possible in C++"); + // C++ initialization is handled later. } - assert(SemaRef.getLangOpts().CPlusPlus && - "non-aggregate records are only possible in C++"); - // C++ initialization is handled later. } else if (isa(expr)) { // This happens during template instantiation when we see an InitListExpr // that we've already checked once. @@ -1359,7 +1422,7 @@ const ArrayType *arrayType = SemaRef.Context.getAsArrayType(DeclType); // Check for the special-case of initializing an array with a string. - if (Index < IList->getNumInits()) { + if (Index < IList->getNumInits() && IList->getInit(Index)) { if (IsStringInit(IList->getInit(Index), arrayType, SemaRef.Context) == SIF_None) { // We place the string literal directly into the resulting @@ -1405,6 +1468,10 @@ QualType elementType = arrayType->getElementType(); while (Index < IList->getNumInits()) { Expr *Init = IList->getInit(Index); + if (!Init) { + ++Index; + continue; + } if (DesignatedInitExpr *DIE = dyn_cast(Init)) { // If we're not the subobject that matches up with the '{' for // the designator, we shouldn't be handling the @@ -2298,7 +2365,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; @@ -2308,29 +2376,9 @@ ExistingInit = StructuredList->getInit(StructuredIndex); if (InitListExpr *Result = dyn_cast_or_null(ExistingInit)) - return Result; + if (!IsFullyOverwritten) + return Result; - if (ExistingInit) { - // 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(InitRange.getBegin(), - diag::warn_subobject_initializer_overrides) - << InitRange; - SemaRef.Diag(ExistingInit->getLocStart(), - diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 - << ExistingInit->getSourceRange(); - } - InitListExpr *Result = new (SemaRef.Context) InitListExpr(SemaRef.Context, InitRange.getBegin(), None, @@ -2378,13 +2426,65 @@ // Link this new initializer list into the structured initializer // lists. - if (StructuredList) - StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result); + bool ExistingInitIsDecomposed = false; + if (StructuredList) { + // 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" + Result->setPrevInitExpr(StructuredList->updateInit(SemaRef.Context, + StructuredIndex, Result)); + if (!Result->getPrevInitExpr() && StructuredList->hasArrayFiller()) + Result->setPrevInitExpr(StructuredList->getArrayFiller()); + if (IsFullyOverwritten) + Result->setPrevInitExpr(nullptr); + if (Expr *PrevInitExpr = Result->getPrevInitExpr()) + if (PrevInitExpr->isConstantInitializer(SemaRef.Context, false)) { + Result->partialMergeFrom(SemaRef.Context, PrevInitExpr); + Result->setPrevInitExpr(nullptr); + ExistingInitIsDecomposed = true; + } + } else { Result->setSyntacticForm(IList); SyntacticToSemantic[IList] = Result; } + if (ExistingInit && !ExistingInitIsDecomposed) { + // 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(InitRange.getBegin(), + diag::warn_subobject_initializer_overrides) + << InitRange; + + // 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(); + } + return Result; } @@ -2403,11 +2503,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/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -4503,6 +4503,8 @@ if (!X.isNull()) { for (unsigned i = 0, e = From->getNumInits(); i < e; ++i) { Expr *Init = From->getInit(i); + if (!Init) + continue; ImplicitConversionSequence ICS = TryCopyInitialization(S, Init, X, SuppressUserConversions, InOverloadResolution, Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2968,6 +2968,9 @@ SmallVectorImpl &Outputs, bool *ArgChanged) { for (unsigned I = 0; I != NumInputs; ++I) { + if (!Inputs[I]) + continue; + // If requested, drop call arguments that need to be dropped. if (IsCall && getDerived().DropCallArgument(Inputs[I])) { if (ArgChanged) Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -716,8 +716,11 @@ E->setSyntacticForm(SyntForm); E->setLBraceLoc(ReadSourceLocation(Record, Idx)); E->setRBraceLoc(ReadSourceLocation(Record, Idx)); + bool isPrevInitExpr = Record[Idx++]; bool isArrayFiller = Record[Idx++]; Expr *filler = nullptr; + if (isPrevInitExpr) + E->PrevInitExpr = Reader.ReadSubExpr(); if (isArrayFiller) { filler = Reader.ReadSubExpr(); E->ArrayFillerOrUnionFieldInit = filler; Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -679,8 +679,12 @@ Writer.AddStmt(E->getSyntacticForm()); Writer.AddSourceLocation(E->getLBraceLoc(), Record); Writer.AddSourceLocation(E->getRBraceLoc(), Record); - bool isArrayFiller = E->ArrayFillerOrUnionFieldInit.is(); + bool isPrevInitExpr = !!E->getPrevInitExpr(); + Record.push_back(isPrevInitExpr); + bool isArrayFiller = E->hasArrayFiller(); Record.push_back(isArrayFiller); + if (isPrevInitExpr) + Writer.AddStmt(E->getPrevInitExpr()); if (isArrayFiller) Writer.AddStmt(E->getArrayFiller()); else @@ -692,7 +696,7 @@ // Replace them by 0 to indicate that the filler goes in that place. Expr *filler = E->getArrayFiller(); for (unsigned I = 0, N = E->getNumInits(); I != N; ++I) - Writer.AddStmt(E->getInit(I) != filler ? E->getInit(I) : nullptr); + Writer.AddStmt(E->getInit(I) && E->getInit(I) != filler ? E->getInit(I) : nullptr); } else { for (unsigned I = 0, N = E->getNumInits(); I != N; ++I) Writer.AddStmt(E->getInit(I)); 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,68 @@ +// 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; +#if defined(_WIN32) || defined(_M_IX86) || defined(__CYGWIN__) \ + || defined(_M_X64) || defined(SHORT_WCHAR) + #define WCHAR_T_TYPE unsigned short +#elif defined(__sun) || defined(__AuroraUX__) + #define WCHAR_T_TYPE long +#else /* Solaris or AuroraUX. */ + #define WCHAR_T_TYPE int +#endif + +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' }; Index: test/CodeGen/partial-reinitialization2.c =================================================================== --- test/CodeGen/partial-reinitialization2.c +++ test/CodeGen/partial-reinitialization2.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -isystem %S/Inputs %s -emit-llvm -o - | lli | 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; }; + + +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); +} + +int main() +{ + test1(); + test2(); + test3(); + return 0; +} Index: test/PCH/designated-init.c.h =================================================================== --- test/PCH/designated-init.c.h +++ test/PCH/designated-init.c.h @@ -40,3 +40,16 @@ }, } }; + +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' } +}; 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}} +};