Index: llvm/include/llvm/IR/Constants.h =================================================================== --- llvm/include/llvm/IR/Constants.h +++ llvm/include/llvm/IR/Constants.h @@ -332,6 +332,8 @@ } }; +// Forward declaration for ConstantArray::get() below +class ConstantDataArray; //===----------------------------------------------------------------------===// /// ConstantArray - Constant Array Declarations @@ -345,6 +347,9 @@ // ConstantArray accessors static Constant *get(ArrayType *T, ArrayRef V); + // builds a ConstantArray from a ConstantDataArray + static Constant *get(ArrayType *T, ConstantDataArray *DataArray); + private: static Constant *getImpl(ArrayType *T, ArrayRef V); Index: llvm/lib/IR/Constants.cpp =================================================================== --- llvm/lib/IR/Constants.cpp +++ llvm/lib/IR/Constants.cpp @@ -923,6 +923,24 @@ return nullptr; } +// builds a ConstantArray from a ConstantDataArray +Constant *ConstantArray::get(ArrayType *Ty, ConstantDataArray *DataArray) { + LLVMContextImpl *pImpl = Ty->getContext().pImpl; + unsigned NumElements = DataArray->getNumElements(); + + std::vector Elts; + Elts.reserve(NumElements); + + for (unsigned i = 0; i < NumElements; ++i) { + Constant *Elt = DataArray->getElementAsConstant(i); + assert(Elt->getType() == Ty->getElementType() && + "Wrong type in array element initializer"); + Elts.push_back(Elt); + } + + return pImpl->ArrayConstants.getOrCreate(Ty, makeArrayRef(Elts)); +} + /// getTypeForElements - Return an anonymous struct type to use for a constant /// with the specified set of elements. The list must not be empty. StructType *ConstantStruct::getTypeForElements(LLVMContext &Context, Index: llvm/tools/clang/include/clang/AST/Expr.h =================================================================== --- llvm/tools/clang/include/clang/AST/Expr.h +++ llvm/tools/clang/include/clang/AST/Expr.h @@ -30,6 +30,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" +namespace llvm { + class Constant; +} + namespace clang { class APValue; class ASTContext; @@ -3769,6 +3773,17 @@ /// - the semantic form, if this is in syntactic form. llvm::PointerIntPair AltForm; + // In cases like: + // struct Q1 { char a[6]; } q1 = { .a = {"foo"}, .a[2] = 'x' }; + // + // In the InitListExpr corresponding to "q1.a", PrevInitExpr will hold the + // Expr corresponding to the string literal "foo". During the CodeGen phase, + // this PrevInitExpr is evaluated and recursively passed down to the child + // elements. If the child elements are also InitListExpr, the intermediate + // evaluation results will be stored into their PrevInitConstant members. + llvm::Constant *PrevInitConstant; + 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 @@ -3784,7 +3799,8 @@ /// \brief Build an empty initializer list. explicit InitListExpr(EmptyShell Empty) - : Expr(InitListExprClass, Empty) { } + : Expr(InitListExprClass, Empty), + PrevInitConstant(nullptr), PrevInitExpr(nullptr) { } unsigned getNumInits() const { return InitExprs.size(); } @@ -3834,6 +3850,14 @@ /// accommodate the new entry. Expr *updateInit(const ASTContext &C, unsigned Init, Expr *expr); + llvm::Constant *getPrevInitConstant() const { return PrevInitConstant; } + void setPrevInitConstant(llvm::Constant *NewPrevInitConstant) { + PrevInitConstant = NewPrevInitConstant; + } + + Expr *getPrevInitExpr() const { return PrevInitExpr; } + void setPrevInitExpr(Expr *newExpr) { PrevInitExpr = newExpr; } + /// \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: llvm/tools/clang/lib/AST/Expr.cpp =================================================================== --- llvm/tools/clang/lib/AST/Expr.cpp +++ llvm/tools/clang/lib/AST/Expr.cpp @@ -1881,7 +1881,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), + PrevInitConstant(nullptr), PrevInitExpr(nullptr) { sawArrayRangeDesignator(false); for (unsigned I = 0; I != initExprs.size(); ++I) { @@ -2772,10 +2773,12 @@ 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; + return !ILE->getPrevInitExpr() || + ILE->getPrevInitExpr()->isConstantInitializer(Ctx, false, Culprit); } if (ILE->getType()->isRecordType()) { @@ -2792,6 +2795,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; @@ -2807,7 +2812,8 @@ } } } - return true; + return !ILE->getPrevInitExpr() || + ILE->getPrevInitExpr()->isConstantInitializer(Ctx, false, Culprit); } break; @@ -2981,6 +2987,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: llvm/tools/clang/lib/AST/ExprConstant.cpp =================================================================== --- llvm/tools/clang/lib/AST/ExprConstant.cpp +++ llvm/tools/clang/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: llvm/tools/clang/lib/AST/ItaniumMangle.cpp =================================================================== --- llvm/tools/clang/lib/AST/ItaniumMangle.cpp +++ llvm/tools/clang/lib/AST/ItaniumMangle.cpp @@ -2708,7 +2708,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; } @@ -2788,7 +2789,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: llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp =================================================================== --- llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp +++ llvm/tools/clang/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); // 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: llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp =================================================================== --- llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp +++ llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp @@ -923,9 +923,22 @@ "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 (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: llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp +++ llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp @@ -352,6 +352,14 @@ unsigned FieldNo = 0; unsigned ElementNo = 0; + + llvm::ConstantStruct *PrevInitEvaluated = nullptr; + if (ILE->getPrevInitExpr()) + PrevInitEvaluated = llvm::dyn_cast_or_null + (CGM.EmitConstantExpr(ILE->getPrevInitExpr(), ILE->getType(), CGF)); + else if (ILE->getPrevInitConstant()) + PrevInitEvaluated = llvm::dyn_cast_or_null + (ILE->getPrevInitConstant()); for (RecordDecl::field_iterator Field = RD->field_begin(), FieldEnd = RD->field_end(); Field != FieldEnd; ++Field, ++FieldNo) { @@ -363,15 +371,22 @@ if (Field->isUnnamedBitfield()) continue; - // Get the initializer. A struct can include fields without initializers, - // we just use explicit null values for them. - llvm::Constant *EltInit; - if (ElementNo < ILE->getNumInits()) - EltInit = CGM.EmitConstantExpr(ILE->getInit(ElementNo++), - Field->getType(), CGF); + // Get the initializer. + Expr *Init = ElementNo < ILE->getNumInits() ? ILE->getInit(ElementNo) : nullptr; + if (InitListExpr *InnerILE = dyn_cast_or_null(Init)) + if (PrevInitEvaluated && !InnerILE->getPrevInitExpr()) + InnerILE->setPrevInitConstant(PrevInitEvaluated->getOperand(ElementNo)); + + llvm::Constant *EltInit = nullptr; + if (Init) + EltInit = CGM.EmitConstantExpr(Init, Field->getType(), CGF); + else if (PrevInitEvaluated) + EltInit = PrevInitEvaluated->getOperand(ElementNo); else EltInit = CGM.EmitNullConstant(Field->getType()); + ++ ElementNo; + if (!EltInit) return false; @@ -730,6 +745,37 @@ unsigned NumInitElements = ILE->getNumInits(); unsigned NumElements = AType->getNumElements(); + // 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); + + // In cases like: + // struct Q1 { char a[6]; } q1 = { .a = {"foo"}, .a[2] = 'x' }; + // ILE->PrevInitExpr holds a string literal corresponding to "foo". + // This string literal is evaluated here to an array of Constant and + // some parts of the array get replaced with 'x'. + llvm::ConstantArray *PrevInitEvaluated = nullptr; + if (ILE->getPrevInitExpr()) { + llvm::Constant *TempInitEvaluated = Visit(ILE->getPrevInitExpr()); + PrevInitEvaluated = + llvm::dyn_cast_or_null(TempInitEvaluated); + + // While it is possible to always check for both ConstantArray and + // ConstantDataArray everywhere, I think it is prone to error. So just + // convert a ConstantDataArray to ConstantArray. + if (TempInitEvaluated && !PrevInitEvaluated) + if (llvm::ConstantDataArray *TempDataArray = + llvm::dyn_cast(TempInitEvaluated)) + PrevInitEvaluated = llvm::dyn_cast + (llvm::ConstantArray::get(AType, TempDataArray)); + } else if (ILE->getPrevInitConstant()) + PrevInitEvaluated = llvm::dyn_cast_or_null + (ILE->getPrevInitConstant()); + // Initialising an array requires us to automatically // initialise any elements that have not been initialised explicitly unsigned NumInitableElts = std::min(NumInitElements, NumElements); @@ -736,12 +782,21 @@ // 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; + if (InitListExpr *InnerILE = dyn_cast_or_null(Init)) + if (PrevInitEvaluated && !InnerILE->getPrevInitExpr()) + InnerILE->setPrevInitConstant(PrevInitEvaluated->getOperand(i)); + llvm::Constant *C = fillC; + + if (Init) + C = CGM.EmitConstantExpr(Init, Init->getType(), CGF); + else if (!ILE->hasArrayFiller() && PrevInitEvaluated) + C = PrevInitEvaluated->getOperand(i); + if (!C) return nullptr; RewriteType |= (C->getType() != ElemTy); @@ -748,18 +803,6 @@ 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 std::vector Types; Index: llvm/tools/clang/lib/Sema/SemaDecl.cpp =================================================================== --- llvm/tools/clang/lib/Sema/SemaDecl.cpp +++ llvm/tools/clang/lib/Sema/SemaDecl.cpp @@ -8268,7 +8268,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: llvm/tools/clang/lib/Sema/SemaInit.cpp =================================================================== --- llvm/tools/clang/lib/Sema/SemaInit.cpp +++ llvm/tools/clang/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,9 +2376,18 @@ ExistingInit = StructuredList->getInit(StructuredIndex); if (InitListExpr *Result = dyn_cast_or_null(ExistingInit)) - return Result; + if (!IsFullyOverwritten) + return Result; - if (ExistingInit) { + // 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 && ExistingInit->getSourceRange().isValid()) { // 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 @@ -2378,9 +2455,22 @@ // Link this new initializer list into the structured initializer // lists. - if (StructuredList) - StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result); - else { + 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()/*StructuredList->arrayIsFilledWithDummy()*/) + Result->setPrevInitExpr(StructuredList->getArrayFiller()); + if (IsFullyOverwritten) + Result->setPrevInitExpr(nullptr); + } else { Result->setSyntacticForm(IList); SyntacticToSemantic[IList] = Result; } @@ -2399,14 +2489,22 @@ if (Expr *PrevInit = StructuredList->updateInit(SemaRef.Context, StructuredIndex, expr)) { - // This initializer overwrites a previous initializer. Warn. - 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()) { + // This initializer overwrites a previous initializer. Warn. + 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(); + } } ++StructuredIndex; Index: llvm/tools/clang/lib/Sema/SemaOverload.cpp =================================================================== --- llvm/tools/clang/lib/Sema/SemaOverload.cpp +++ llvm/tools/clang/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: llvm/tools/clang/lib/Sema/TreeTransform.h =================================================================== --- llvm/tools/clang/lib/Sema/TreeTransform.h +++ llvm/tools/clang/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: llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp +++ llvm/tools/clang/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: llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp +++ llvm/tools/clang/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: llvm/tools/clang/test/CodeGen/Inputs/stdio.h =================================================================== --- llvm/tools/clang/test/CodeGen/Inputs/stdio.h +++ llvm/tools/clang/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: llvm/tools/clang/test/CodeGen/partial-reinitialization1.c =================================================================== --- llvm/tools/clang/test/CodeGen/partial-reinitialization1.c +++ llvm/tools/clang/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: llvm/tools/clang/test/CodeGen/partial-reinitialization2.c =================================================================== --- llvm/tools/clang/test/CodeGen/partial-reinitialization2.c +++ llvm/tools/clang/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: llvm/tools/clang/test/PCH/designated-init.c.h =================================================================== --- llvm/tools/clang/test/PCH/designated-init.c.h +++ llvm/tools/clang/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: llvm/tools/clang/test/Sema/designated-initializers.c =================================================================== --- llvm/tools/clang/test/Sema/designated-initializers.c +++ llvm/tools/clang/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] = { @@ -332,12 +332,12 @@ int M; } overwrite_string[] = { { { "foo" }, 1 }, // expected-note {{previous initialization is here}} - [0].L[2] = 'x' // expected-warning{{initializer overrides prior initialization of this subobject}} + [0].L[2] = 'x' // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}} }; struct overwrite_string_struct2 { char L[6]; int M; } overwrite_string2[] = { - { { "foo" }, 1 }, - [0].L[4] = 'x' // no-warning + { { "foo" }, 1 }, // expected-note {{previous initialization is here}} + [0].L[4] = 'x' // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}} };