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 @@ -4267,6 +4267,80 @@ } }; +/// \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]; + +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; + } + + 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 @@ -1211,8 +1211,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,11 @@ const Expr *Exp = cast(this)->getInitializer(); return Exp->isConstantInitializer(Ctx, false, Culprit); } + case DesignatedInitUpdateExprClass: { + const DesignatedInitUpdateExpr *DIUE = cast(this); + return DIUE->getBase()->isConstantInitializer(Ctx, false, Culprit) && + DIUE->getUpdater()->isConstantInitializer(Ctx, false, Culprit); + } case InitListExprClass: { const InitListExpr *ILE = cast(this); if (ILE->getType()->isArrayType()) { @@ -2818,6 +2823,7 @@ break; } case ImplicitValueInitExprClass: + case NoInitExprClass: return true; case ParenExprClass: return cast(this)->getSubExpr() @@ -2925,6 +2931,7 @@ case UnaryExprOrTypeTraitExprClass: case AddrLabelExprClass: case GNUNullExprClass: + case NoInitExprClass: case CXXBoolLiteralExprClass: case CXXNullPtrLiteralExprClass: case CXXThisExprClass: @@ -2983,6 +2990,7 @@ case CompoundLiteralExprClass: case ExtVectorElementExprClass: case DesignatedInitExprClass: + case DesignatedInitUpdateExprClass: case ParenListExprClass: case CXXPseudoDestructorExprClass: case CXXStdInitializerListExprClass: @@ -3989,6 +3997,25 @@ 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) { + BaseAndUpdaterExprs[0] = baseExpr; + + InitListExpr *ILE = new (C) InitListExpr(C, lBraceLoc, None, rBraceLoc); + ILE->setType(baseExpr->getType()); + BaseAndUpdaterExprs[1] = ILE; +} + +SourceLocation DesignatedInitUpdateExpr::getLocStart() const { + return getBase()->getLocStart(); +} + +SourceLocation DesignatedInitUpdateExpr::getLocEnd() const { + return getBase()->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 @@ -8675,6 +8675,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 @@ -2676,7 +2676,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 @@ -1428,6 +1428,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 @@ -744,6 +744,17 @@ } } +// Seems that if VisitInitListExpr() only works on the syntactic form of an +// InitListExpr, then a DesignatedInitUpdateExpr is not encountered. +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()); } @@ -1055,6 +1057,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); @@ -1275,6 +1280,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 @@ -958,6 +958,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/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -33,6 +33,7 @@ //===----------------------------------------------------------------------===// namespace { +class ConstExprEmitter; class ConstStructBuilder { CodeGenModule &CGM; CodeGenFunction *CGF; @@ -42,6 +43,10 @@ CharUnits LLVMStructAlignment; SmallVector Elements; public: + static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CFG, + ConstExprEmitter *Emitter, + llvm::ConstantStruct *Base, + InitListExpr *Updater); static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CGF, InitListExpr *ILE); static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CGF, @@ -68,6 +73,8 @@ void ConvertStructToPacked(); bool Build(InitListExpr *ILE); + bool Build(ConstExprEmitter *Emitter, llvm::ConstantStruct *Base, + InitListExpr *Updater); void Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase, const CXXRecordDecl *VTableClass, CharUnits BaseOffset); llvm::Constant *Finalize(QualType Ty); @@ -547,6 +554,17 @@ llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM, CodeGenFunction *CGF, + ConstExprEmitter *Emitter, + llvm::ConstantStruct *Base, + InitListExpr *Updater) { + ConstStructBuilder Builder(CGM, CGF); + if (!Builder.Build(Emitter, Base, Updater)) + return nullptr; + return Builder.Finalize(Updater->getType()); +} + +llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM, + CodeGenFunction *CGF, InitListExpr *ILE) { ConstStructBuilder Builder(CGM, CGF); @@ -818,6 +836,82 @@ return nullptr; } + llvm::Constant *EmitDesignatedInitUpdater(llvm::Constant *Base, + InitListExpr *Updater) { + QualType ExprType = Updater->getType(); + + if (ExprType->isArrayType()) { + llvm::ArrayType *AType = cast(ConvertType(ExprType)); + llvm::Type *ElemType = AType->getElementType(); + + unsigned NumInitElements = Updater->getNumInits(); + unsigned NumElements = AType->getNumElements(); + + std::vector Elts; + Elts.reserve(NumElements); + + if (llvm::ConstantDataArray *DataArray = + dyn_cast(Base)) + for (unsigned i = 0; i != NumElements; ++i) + Elts.push_back(DataArray->getElementAsConstant(i)); + else if (llvm::ConstantArray *Array = + dyn_cast(Base)) + for (unsigned i = 0; i != NumElements; ++i) + Elts.push_back(Array->getOperand(i)); + else + return nullptr; // FIXME: other array types not implemented + + llvm::Constant *fillC = nullptr; + if (Expr *filler = Updater->getArrayFiller()) + if (!isa(filler)) + fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF); + bool RewriteType = (fillC && fillC->getType() != ElemType); + + for (unsigned i = 0; i != NumElements; ++i) { + Expr *Init = nullptr; + if (i < NumInitElements) + Init = Updater->getInit(i); + + if (!Init && fillC) + Elts[i] = fillC; + else if (!Init || isa(Init)) + ; // Do nothing. + else if (InitListExpr *ChildILE = dyn_cast(Init)) + Elts[i] = EmitDesignatedInitUpdater(Elts[i], ChildILE); + else + Elts[i] = CGM.EmitConstantExpr(Init, Init->getType(), CGF); + + if (!Elts[i]) + return nullptr; + RewriteType |= (Elts[i]->getType() != ElemType); + } + + if (RewriteType) { + std::vector Types; + Types.reserve(NumElements); + for (unsigned i = 0; i != NumElements; ++i) + Types.push_back(Elts[i]->getType()); + llvm::StructType *SType = llvm::StructType::get(AType->getContext(), + Types, true); + return llvm::ConstantStruct::get(SType, Elts); + } + + return llvm::ConstantArray::get(AType, Elts); + } + + if (ExprType->isRecordType()) + return ConstStructBuilder::BuildStruct(CGM, CGF, this, + dyn_cast(Base), Updater); + + return nullptr; + } + + llvm::Constant *VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + return EmitDesignatedInitUpdater( + CGM.EmitConstantExpr(E->getBase(), E->getType(), CGF), + E->getUpdater()); + } + llvm::Constant *VisitCXXConstructExpr(CXXConstructExpr *E) { if (!E->getConstructor()->isTrivial()) return nullptr; @@ -1003,6 +1097,68 @@ } // end anonymous namespace. +bool ConstStructBuilder::Build(ConstExprEmitter *Emitter, + llvm::ConstantStruct *Base, + InitListExpr *Updater) { + assert(Base && "base expression should not be empty"); + + QualType ExprType = Updater->getType(); + RecordDecl *RD = ExprType->getAs()->getDecl(); + const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(RD); + const llvm::StructLayout *BaseLayout = CGM.getDataLayout().getStructLayout( + Base->getType()); + unsigned FieldNo = -1; + unsigned ElementNo = 0; + + for (FieldDecl *Field : RD->fields()) { + ++FieldNo; + + if (RD->isUnion() && Updater->getInitializedFieldInUnion() != Field) + continue; + + // Skip anonymous bitfields. + if (Field->isUnnamedBitfield()) + continue; + + llvm::Constant *EltInit = Base->getOperand(ElementNo); + + // Bail out if the type of the ConstantStruct does not have the same layout + // as the type of the InitListExpr. + if (CGM.getTypes().ConvertType(Field->getType()) != EltInit->getType() || + Layout.getFieldOffset(ElementNo) != + BaseLayout->getElementOffsetInBits(ElementNo)) + return false; + + // Get the initializer. If we encounter an empty field or a NoInitExpr, + // we use values from the base expression. + Expr *Init = nullptr; + if (ElementNo < Updater->getNumInits()) + Init = Updater->getInit(ElementNo); + + if (!Init || isa(Init)) + ; // Do nothing. + else if (InitListExpr *ChildILE = dyn_cast(Init)) + EltInit = Emitter->EmitDesignatedInitUpdater(EltInit, ChildILE); + else + EltInit = CGM.EmitConstantExpr(Init, Field->getType(), CGF); + + ++ElementNo; + + if (!EltInit) + return false; + + if (!Field->isBitField()) + AppendField(Field, Layout.getFieldOffset(FieldNo), EltInit); + else if (llvm::ConstantInt *CI = dyn_cast(EltInit)) + AppendBitField(Field, Layout.getFieldOffset(FieldNo), CI); + else + // Initializing a bitfield with a non-trivial constant? + return false; + } + + return true; +} + llvm::Constant *CodeGenModule::EmitConstantInit(const VarDecl &D, CodeGenFunction *CGF) { // Make a quick check if variable can be default NULL initialized 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 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,13 +648,23 @@ ElementEntity.setElementIndex(Init); Expr *InitExpr = (Init < NumInits ? ILE->getInit(Init) : nullptr); - if (!InitExpr && !ILE->hasArrayFiller()) { - ExprResult ElementInit = PerformEmptyInit(SemaRef, ILE->getLocEnd(), - ElementEntity, - /*VerifyOnly*/false); - if (ElementInit.isInvalid()) { - hadError = true; - return; + if (!InitExpr && Init < NumInits && ILE->hasArrayFiller()) + ILE->setInit(Init, ILE->getArrayFiller()); + else if (!InitExpr && !ILE->hasArrayFiller()) { + Expr *Filler = nullptr; + + if (FillWithNoInit) + Filler = new (SemaRef.Context) NoInitExpr(ElementType); + else { + ExprResult ElementInit = PerformEmptyInit(SemaRef, ILE->getLocEnd(), + ElementEntity, + /*VerifyOnly*/false); + if (ElementInit.isInvalid()) { + hadError = true; + return; + } + + Filler = ElementInit.getAs(); } if (hadError) { @@ -609,29 +673,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 +1035,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; @@ -1913,11 +1995,66 @@ // 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 (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 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()) { + // 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()); + + SemaRef.Diag(ExistingInit->getLocStart(), + diag::note_previous_initializer) + << /*FIXME:has side effects=*/0 + << ExistingInit->getSourceRange(); + } + } + } assert(StructuredList && "Expected a structured initializer list"); } @@ -2367,7 +2504,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 +2515,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 @@ -2469,13 +2616,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()) { + 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: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -7950,8 +7950,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,16 @@ Designators.data(), Designators.size()); } +void ASTStmtReader::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + VisitExpr(E); + E->setBase(Reader.ReadSubExpr()); + E->setUpdater(Reader.ReadSubExpr()); +} + +void ASTStmtReader::VisitNoInitExpr(NoInitExpr *E) { + VisitExpr(E); +} + void ASTStmtReader::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { VisitExpr(E); } @@ -2549,10 +2559,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 @@ -738,6 +738,18 @@ Code = serialization::EXPR_DESIGNATED_INIT; } +void ASTStmtWriter::VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) { + VisitExpr(E); + 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,41 @@ +// 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: /*implicit*/(int)0 +// CHECK: 15: {[B1.12], [B1.13]} +// CHECK: 18: /*no init*/ +// CHECK: 19: /*no init*/ +// CHECK: 20: 3 +// CHECK: 21: {[B1.18], [B1.19], [B1.20]} +// CHECK: 22: {/*base*/[B1.17], /*updater*/[B1.21]} +// CHECK: 24: struct Q s[] = {[0] = (struct Q){1, 2}, [0].c = 3}; 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 %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,108 @@ +// RUN: %clang_cc1 %s -triple x86_64-unknown-unknown -emit-llvm -o - | FileCheck %s + +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; }; + +// CHECK-LABEL: test1 +void test1(void) +{ + // CHECK: call void @llvm.memcpy{{.*}}%struct.P1, %struct.P1* @g1{{.*}}i64 6, i32 {{[0-9]}}, i1 false) + // CHECK: store i8 120, i8* %arrayinit + + struct LP1 l = { .p1 = g1, .p1.x[2] = 'x' }; +} + +// CHECK-LABEL: test2 +void test2(void) +{ + // CHECK: call void @llvm.memcpy{{.*}}%struct.P1, %struct.P1* @g1{{.*}}i64 6, i32 {{[0-9]}}, i1 false) + // CHECK: store i8 114, i8* %arrayinit + + struct LP1 l = { .p1 = g1, .p1.x[1] = 'r' }; +} + +// CHECK-LABEL: test3 +void test3(void) +{ + // CHECK: call void @llvm.memcpy{{.*}}%struct.P2* @g2{{.*}}i64 12, i32 {{[0-9]}}, i1 false) + // CHECK: store i32 10, i32* %b + + struct LP2 l = { .p2 = g2, .p2.b = 10 }; +} + +// CHECK-LABEL: get235 +struct P2 get235() +{ + struct P2 p = { 2, 3, 5 }; + return p; +} + +// CHECK-LABEL: get456789 +struct LP2P2 get456789() +{ + struct LP2P2 l = { { 4, 5, 6 }, { 7, 8, 9 } }; + return l; +} + +// CHECK-LABEL: get123 +union UP2 get123() +{ + union UP2 u = { { 1, 2, 3 } }; + return u; +} + +// CHECK-LABEL: test4 +void test4(void) +{ + // CHECK: [[CALL:%[a-z0-9]+]] = call {{.*}}@get123() + // CHECK: store{{.*}}[[CALL]], {{.*}}[[TMP0:%[a-z0-9]+]] + // CHECK: [[TMP1:%[a-z0-9]+]] = bitcast {{.*}}[[TMP0]] + // CHECK: call void @llvm.memcpy{{.*}}[[TMP1]], i64 12, i32 {{[0-9]}}, i1 false) + // CHECK: store i32 100, i32* %a + + struct LUP2 { union UP2 up; } var = { get123(), .up.p2.a = 100 }; +} + +// CHECK-LABEL: test5 +void test5(void) +{ + // .l3 = g3 + // CHECK: call void @llvm.memcpy{{.*}}%struct.LP3, %struct.LP3* @g3{{.*}}i64 12, i32 {{[0-9]}}, i1 false) + + // .l3.p1 = { [0] = g1 } implicitly sets [1] to zero + // CHECK: call void @llvm.memcpy{{.*}}%struct.P1, %struct.P1* @g1{{.*}}i64 6, i32 {{[0-9]}}, i1 false) + // CHECK: getelementptr{{.*}}%struct.P1, %struct.P1*{{.*}}i64 1 + // CHECK: call void @llvm.memset{{.*}}i8 0, i64 6, i32 {{[0-9]}}, i1 false) + + // .l3.p1[1].x[1] = 'x' + // CHECK: store i8 120, i8* %arrayinit + + struct LLP3 var = { .l3 = g3, .l3.p1 = { [0] = g1 }, .l3.p1[1].x[1] = 'x' }; +} + +// CHECK-LABEL: test6 +void test6(void) +{ + // CHECK: [[LP:%[a-z0-9]+]] = getelementptr{{.*}}%struct.LLP2P2, %struct.LLP2P2*{{.*}}, i32 0, i32 0 + // CHECK: call {{.*}}get456789(%struct.LP2P2* {{.*}}[[LP]]) + + // CHECK: [[CALL:%[a-z0-9]+]] = call {{.*}}@get235() + // CHECK: store{{.*}}[[CALL]], {{.*}}[[TMP0:%[a-z0-9]+]] + // CHECK: [[TMP1:%[a-z0-9]+]] = bitcast {{.*}}[[TMP0]] + // CHECK: call void @llvm.memcpy{{.*}}[[TMP1]], i64 12, i32 {{[0-9]}}, i1 false) + + // CHECK: store i32 10, i32* %b + + struct LLP2P2 { struct LP2P2 lp; } var = { get456789(), + .lp.p1 = get235(), + .lp.p1.b = 10 }; +} 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] = { @@ -129,11 +129,11 @@ void test() { struct X xs[] = { - [0] = (struct X){1, 2}, // expected-note{{previous initialization is here}} + [0] = (struct X){1, 2}, // expected-note 2 {{previous initialization is here}} [0].c = 3, // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing 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 + [0].b = 8 // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}} }; } @@ -332,12 +332,22 @@ 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}} }; 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}} }; +struct overwrite_string_struct +overwrite_string3[] = { + "foo", 1, // expected-note{{previous initialization is here}} + [0].L[4] = 'x' // expected-warning{{subobject initialization overrides initialization of other fields}} +}; +struct overwrite_string_struct +overwrite_string4[] = { + { { 'f', 'o', 'o' }, 1 }, + [0].L[4] = 'x' // no-warning +}; Index: test/SemaCXX/decltype.cpp =================================================================== --- test/SemaCXX/decltype.cpp +++ test/SemaCXX/decltype.cpp @@ -76,6 +76,31 @@ decltype(f(), 0) *e; // expected-error {{attempt to use a deleted function}} } +namespace D5789 { + struct P1 { char x[6]; } g1 = { "foo" }; + struct LP1 { struct P1 p1; }; + + // expected-warning@+3 {{subobject initialization overrides}} + // expected-note@+2 {{previous initialization}} + // expected-note@+1 {{previous definition}} + template void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {} + + // expected-warning@+3 {{subobject initialization overrides}} + // expected-note@+2 {{previous initialization}} + template + void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'r' }))) {} // okay + + // expected-warning@+3 {{subobject initialization overrides}} + // expected-note@+2 {{previous initialization}} + template + void foo(decltype(T(LP1{ .p1 = { "foo" }, .p1.x[1] = 'x'}))) {} // okay + + // expected-warning@+3 {{subobject initialization overrides}} + // expected-note@+2 {{previous initialization}} + // expected-error@+1 {{redefinition of 'foo'}} + template void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {} +} + template class conditional { }; 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: