Implementing C99 partial re-initialization behavior (DR-253)

Based on previous discussion on the mailing list, clang currently lacks support
for C99 partial re-initialization behavior:
Reference: http://lists.cs.uiuc.edu/pipermail/cfe-dev/2013-April/029188.html
Reference: http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_253.htm

This patch attempts to fix this problem.

Given the following code snippet,

struct P1 { char x[6]; };
struct LP1 { struct P1 p1; };

struct LP1 l = { .p1 = { "foo" }, .p1.x[2] = 'x' };
// this example is adapted from the example for "struct fred x[]" in DR-253;
// currently clang produces in l: { "\0\0x" },
//   whereas gcc 4.8 produces { "fox" };
// with this fix, clang will also produce: { "fox" };


Differential Review: http://reviews.llvm.org/D5789

llvm-svn: 239446
This commit is contained in:
Yunzhong Gao 2015-06-10 00:27:52 +00:00
parent 7912d9b899
commit cb77930d6b
28 changed files with 872 additions and 45 deletions

View File

@ -2204,9 +2204,11 @@ DEF_TRAVERSE_STMT(CXXThisExpr, {})
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())

View File

@ -4267,6 +4267,80 @@ public:
}
};
/// \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<Expr>(BaseAndUpdaterExprs[0]); }
void setBase(Expr *Base) { BaseAndUpdaterExprs[0] = Base; }
InitListExpr *getUpdater() const {
return cast<InitListExpr>(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.
///

View File

@ -2234,9 +2234,11 @@ DEF_TRAVERSE_STMT(CXXThisExpr, {})
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())

View File

@ -77,7 +77,9 @@ def CompoundLiteralExpr : DStmt<Expr>;
def ExtVectorElementExpr : DStmt<Expr>;
def InitListExpr : DStmt<Expr>;
def DesignatedInitExpr : DStmt<Expr>;
def DesignatedInitUpdateExpr : DStmt<Expr>;
def ImplicitValueInitExpr : DStmt<Expr>;
def NoInitExpr : DStmt<Expr>;
def ParenListExpr : DStmt<Expr>;
def VAArgExpr : DStmt<Expr>;
def GenericSelectionExpr : DStmt<Expr>;

View File

@ -1211,8 +1211,12 @@ namespace clang {
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.

View File

@ -2772,6 +2772,11 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
const Expr *Exp = cast<CompoundLiteralExpr>(this)->getInitializer();
return Exp->isConstantInitializer(Ctx, false, Culprit);
}
case DesignatedInitUpdateExprClass: {
const DesignatedInitUpdateExpr *DIUE = cast<DesignatedInitUpdateExpr>(this);
return DIUE->getBase()->isConstantInitializer(Ctx, false, Culprit) &&
DIUE->getUpdater()->isConstantInitializer(Ctx, false, Culprit);
}
case InitListExprClass: {
const InitListExpr *ILE = cast<InitListExpr>(this);
if (ILE->getType()->isArrayType()) {
@ -2818,6 +2823,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
break;
}
case ImplicitValueInitExprClass:
case NoInitExprClass:
return true;
case ParenExprClass:
return cast<ParenExpr>(this)->getSubExpr()
@ -2925,6 +2931,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case UnaryExprOrTypeTraitExprClass:
case AddrLabelExprClass:
case GNUNullExprClass:
case NoInitExprClass:
case CXXBoolLiteralExprClass:
case CXXNullPtrLiteralExprClass:
case CXXThisExprClass:
@ -2983,6 +2990,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case CompoundLiteralExprClass:
case ExtVectorElementExprClass:
case DesignatedInitExprClass:
case DesignatedInitUpdateExprClass:
case ParenListExprClass:
case CXXPseudoDestructorExprClass:
case CXXStdInitializerListExprClass:
@ -3989,6 +3997,25 @@ void DesignatedInitExpr::ExpandDesignator(const ASTContext &C, unsigned Idx,
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<Expr*> exprs,
SourceLocation rparenloc)

View File

@ -183,6 +183,8 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::ObjCIndirectCopyRestoreExprClass:
case Expr::AtomicExprClass:
case Expr::CXXFoldExprClass:
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
return Cl::CL_PRValue;
// Next come the complicated cases.

View File

@ -8675,6 +8675,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::CompoundLiteralExprClass:
case Expr::ExtVectorElementExprClass:
case Expr::DesignatedInitExprClass:
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
case Expr::ImplicitValueInitExprClass:
case Expr::ParenListExprClass:
case Expr::VAArgExprClass:

View File

@ -2680,7 +2680,9 @@ recurse:
// 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:

View File

@ -1430,6 +1430,22 @@ void StmtPrinter::VisitDesignatedInitExpr(DesignatedInitExpr *Node) {
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*/";

View File

@ -744,6 +744,18 @@ void StmtProfiler::VisitDesignatedInitExpr(const DesignatedInitExpr *S) {
}
}
// 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("Unexpected DesignatedInitUpdateExpr in syntactic form of "
"initializer");
}
void StmtProfiler::VisitNoInitExpr(const NoInitExpr *S) {
llvm_unreachable("Unexpected NoInitExpr in syntactic form of initializer");
}
void StmtProfiler::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *S) {
VisitExpr(S);
}

View File

@ -160,10 +160,12 @@ public:
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());
}
@ -1056,6 +1058,9 @@ AggExprEmitter::EmitInitializationToLValue(Expr *E, LValue LV) {
return;
} else if (isa<ImplicitValueInitExpr>(E) || isa<CXXScalarValueInitExpr>(E)) {
return EmitNullInitializationToLValue(LV);
} else if (isa<NoInitExpr>(E)) {
// Do nothing.
return;
} else if (type->isReferenceType()) {
RValue RV = CGF.EmitReferenceBindingToExpr(E);
return CGF.EmitStoreThroughLValue(RV, LV);
@ -1276,6 +1281,15 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
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
//===----------------------------------------------------------------------===//

View File

@ -958,6 +958,25 @@ void CodeGenFunction::EmitNewArrayInitializer(
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<InitListExpr>(Init)) {
if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) {
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<ImplicitValueInitExpr>(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");

View File

@ -33,6 +33,7 @@ using namespace CodeGen;
//===----------------------------------------------------------------------===//
namespace {
class ConstExprEmitter;
class ConstStructBuilder {
CodeGenModule &CGM;
CodeGenFunction *CGF;
@ -42,6 +43,10 @@ class ConstStructBuilder {
CharUnits LLVMStructAlignment;
SmallVector<llvm::Constant *, 32> 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 @@ private:
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);
@ -545,6 +552,17 @@ llvm::Constant *ConstStructBuilder::Finalize(QualType Ty) {
return Result;
}
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) {
@ -818,6 +836,82 @@ public:
return nullptr;
}
llvm::Constant *EmitDesignatedInitUpdater(llvm::Constant *Base,
InitListExpr *Updater) {
QualType ExprType = Updater->getType();
if (ExprType->isArrayType()) {
llvm::ArrayType *AType = cast<llvm::ArrayType>(ConvertType(ExprType));
llvm::Type *ElemType = AType->getElementType();
unsigned NumInitElements = Updater->getNumInits();
unsigned NumElements = AType->getNumElements();
std::vector<llvm::Constant *> Elts;
Elts.reserve(NumElements);
if (llvm::ConstantDataArray *DataArray =
dyn_cast<llvm::ConstantDataArray>(Base))
for (unsigned i = 0; i != NumElements; ++i)
Elts.push_back(DataArray->getElementAsConstant(i));
else if (llvm::ConstantArray *Array =
dyn_cast<llvm::ConstantArray>(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<NoInitExpr>(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<NoInitExpr>(Init))
; // Do nothing.
else if (InitListExpr *ChildILE = dyn_cast<InitListExpr>(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<llvm::Type *> 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<llvm::ConstantStruct>(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 @@ public:
} // 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<RecordType>()->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<NoInitExpr>(Init))
; // Do nothing.
else if (InitListExpr *ChildILE = dyn_cast<InitListExpr>(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<llvm::ConstantInt>(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

View File

@ -1041,6 +1041,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
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 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::ImaginaryLiteralClass:
case Expr::ImplicitValueInitExprClass:
case Expr::IntegerLiteralClass:
case Expr::NoInitExprClass:
case Expr::ObjCEncodeExprClass:
case Expr::ObjCStringLiteralClass:
case Expr::ObjCBoolLiteralExprClass:

View File

@ -306,7 +306,8 @@ class InitListChecker {
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 @@ class InitListChecker {
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::CheckEmptyInitializable(const InitializedEntity &Entity,
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<RecordType>())
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 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
} else if (InitListExpr *InnerILE
= dyn_cast<InitListExpr>(ILE->getInit(Init)))
FillInEmptyInitializations(MemberEntity, InnerILE,
RequiresSecondPass);
RequiresSecondPass, FillWithNoInit);
else if (DesignatedInitUpdateExpr *InnerDIUE
= dyn_cast<DesignatedInitUpdateExpr>(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::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
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 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
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<CXXRecordDecl>(RDecl) &&
cast<CXXRecordDecl>(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 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
if (hadError)
return;
FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass);
FillInEmptyInitForField(Init, Field, Entity, ILE, RequiresSecondPass,
FillWithNoInit);
if (hadError)
return;
@ -594,13 +648,23 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
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<Expr>();
}
if (hadError) {
@ -609,29 +673,34 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
// 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<Expr>());
ILE->setArrayFiller(Filler);
else
ILE->setInit(Init, ElementInit.getAs<Expr>());
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<Expr>());
ILE->setArrayFiller(Filler);
return;
}
if (!isa<ImplicitValueInitExpr>(ElementInit.get())) {
if (!isa<ImplicitValueInitExpr>(Filler) && !isa<NoInitExpr>(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<Expr>());
ILE->updateInit(SemaRef.Context, Init, Filler);
RequiresSecondPass = true;
}
}
} else if (InitListExpr *InnerILE
= dyn_cast_or_null<InitListExpr>(InitExpr))
FillInEmptyInitializations(ElementEntity, InnerILE, RequiresSecondPass);
FillInEmptyInitializations(ElementEntity, InnerILE, RequiresSecondPass,
FillWithNoInit);
else if (DesignatedInitUpdateExpr *InnerDIUE
= dyn_cast_or_null<DesignatedInitUpdateExpr>(InitExpr))
FillInEmptyInitializations(ElementEntity, InnerDIUE->getUpdater(),
RequiresSecondPass, /*FillWithNoInit =*/ true);
}
}
@ -966,13 +1035,26 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
StructuredList, StructuredIndex);
if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(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 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
// 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<InitListExpr>(ExistingInit))
StructuredList = Result;
else {
if (DesignatedInitUpdateExpr *E =
dyn_cast<DesignatedInitUpdateExpr>(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 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index,
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 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index,
ExistingInit = StructuredList->getInit(StructuredIndex);
if (InitListExpr *Result = dyn_cast_or_null<InitListExpr>(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 @@ void InitListChecker::UpdateStructuredListElement(InitListExpr *StructuredList,
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;

View File

@ -7950,6 +7950,25 @@ TreeTransform<Derived>::TransformDesignatedInitExpr(DesignatedInitExpr *E) {
E->usesGNUSyntax(), Init.get());
}
// Seems that if TransformInitListExpr() only works on the syntactic form of an
// InitListExpr, then a DesignatedInitUpdateExpr is not encountered.
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformDesignatedInitUpdateExpr(
DesignatedInitUpdateExpr *E) {
llvm_unreachable("Unexpected DesignatedInitUpdateExpr in syntactic form of "
"initializer");
return ExprError();
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformNoInitExpr(
NoInitExpr *E) {
llvm_unreachable("Unexpected NoInitExpr in syntactic form of initializer");
return ExprError();
}
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformImplicitValueInitExpr(

View File

@ -801,6 +801,16 @@ void ASTStmtReader::VisitDesignatedInitExpr(DesignatedInitExpr *E) {
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 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
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;

View File

@ -774,7 +774,9 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
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);

View File

@ -738,6 +738,18 @@ void ASTStmtWriter::VisitDesignatedInitExpr(DesignatedInitExpr *E) {
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;

View File

@ -859,6 +859,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// 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 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:
case Stmt::FloatingLiteralClass:
case Stmt::NoInitExprClass:
case Stmt::SizeOfPackExprClass:
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:

View File

@ -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};

View File

@ -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 };

View File

@ -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 };
}

View File

@ -40,3 +40,25 @@ static void *FooTable[256] = {
},
}
};
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;
}

View File

@ -45,8 +45,8 @@ struct point array[10] = {
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 @@ int get8() { ++counter; return 8; }
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 @@ struct overwrite_string_struct {
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
};

View File

@ -76,6 +76,31 @@ namespace PR18876 {
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<class T> void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {}
// expected-warning@+3 {{subobject initialization overrides}}
// expected-note@+2 {{previous initialization}}
template<class T>
void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'r' }))) {} // okay
// expected-warning@+3 {{subobject initialization overrides}}
// expected-note@+2 {{previous initialization}}
template<class T>
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<class T> void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {}
}
template<typename>
class conditional {
};

View File

@ -235,11 +235,13 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
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: