Hi rsmith,
Hi all,
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
I am proposing the following patch hopefully 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" };
The idea is that upon seeing the second initializer, we will add a member to
the InitListExpr corresponding to ".p1" to hold the expression "{ "foo" }",
whereas one child field of the InitListExpr will hold "x". We keep this
structure till the CodeGen phase where we evaluate "{ "foo" }" and recursively
pass down its (evaluated) values as needed to the child fields of ".p1". The
recursion will stop at the child fields which are initialized with a
brace-enclosed list (if they exist).
>From the Sema phase till the codeGen phase, the initializers for "l.p1" are
stored in two separate places: the new PrevInitExpr member of the top-level
InitListExpr, and the various child elements of the InitListExpr. This makes
it more complicated to do synatic/semantic checking of the initializer list.
This proposed patch does not cover all the semantic checking cases, but a
subsequent patch might be needed to do all the semantic checking on the new
PrevInitExpr member.
In the case where a string literal appears in place of "{ "foo" }", clang
evaluates it to a llvm::ConstantDataArray, which is not very easy to pass down
its components down to the child fields. In this proposal, I implemented a
method to build a llvm::ConstantArray out of a given ConstantDataArray. This is
the only part of the patch implemented in the llvm backend and will need to be
committed before the other parts. Or, maybe there are better ways to solve this?
Could someone take a look at the patch?
- Gao
http://reviews.llvm.org/D5789
Files:
llvm/include/llvm/IR/Constants.h
llvm/lib/IR/Constants.cpp
llvm/tools/clang/include/clang/AST/Expr.h
llvm/tools/clang/lib/AST/Expr.cpp
llvm/tools/clang/lib/AST/ExprConstant.cpp
llvm/tools/clang/lib/AST/ItaniumMangle.cpp
llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp
llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp
llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp
llvm/tools/clang/lib/Sema/SemaDecl.cpp
llvm/tools/clang/lib/Sema/SemaInit.cpp
llvm/tools/clang/lib/Sema/SemaOverload.cpp
llvm/tools/clang/lib/Sema/TreeTransform.h
llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp
llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp
llvm/tools/clang/test/CodeGen/Inputs/stdio.h
llvm/tools/clang/test/CodeGen/partial-reinitialization1.c
llvm/tools/clang/test/CodeGen/partial-reinitialization2.c
llvm/tools/clang/test/PCH/designated-init.c.h
llvm/tools/clang/test/Sema/designated-initializers.c
Index: llvm/include/llvm/IR/Constants.h
===================================================================
--- llvm/include/llvm/IR/Constants.h
+++ llvm/include/llvm/IR/Constants.h
@@ -332,6 +332,8 @@
}
};
+// Forward declaration for ConstantArray::get() below
+class ConstantDataArray;
//===----------------------------------------------------------------------===//
/// ConstantArray - Constant Array Declarations
@@ -345,6 +347,9 @@
// ConstantArray accessors
static Constant *get(ArrayType *T, ArrayRef<Constant*> V);
+ // builds a ConstantArray from a ConstantDataArray
+ static Constant *get(ArrayType *T, ConstantDataArray *DataArray);
+
private:
static Constant *getImpl(ArrayType *T, ArrayRef<Constant *> V);
Index: llvm/lib/IR/Constants.cpp
===================================================================
--- llvm/lib/IR/Constants.cpp
+++ llvm/lib/IR/Constants.cpp
@@ -923,6 +923,24 @@
return nullptr;
}
+// builds a ConstantArray from a ConstantDataArray
+Constant *ConstantArray::get(ArrayType *Ty, ConstantDataArray *DataArray) {
+ LLVMContextImpl *pImpl = Ty->getContext().pImpl;
+ unsigned NumElements = DataArray->getNumElements();
+
+ std::vector<llvm::Constant*> Elts;
+ Elts.reserve(NumElements);
+
+ for (unsigned i = 0; i < NumElements; ++i) {
+ Constant *Elt = DataArray->getElementAsConstant(i);
+ assert(Elt->getType() == Ty->getElementType() &&
+ "Wrong type in array element initializer");
+ Elts.push_back(Elt);
+ }
+
+ return pImpl->ArrayConstants.getOrCreate(Ty, makeArrayRef(Elts));
+}
+
/// getTypeForElements - Return an anonymous struct type to use for a constant
/// with the specified set of elements. The list must not be empty.
StructType *ConstantStruct::getTypeForElements(LLVMContext &Context,
Index: llvm/tools/clang/include/clang/AST/Expr.h
===================================================================
--- llvm/tools/clang/include/clang/AST/Expr.h
+++ llvm/tools/clang/include/clang/AST/Expr.h
@@ -30,6 +30,10 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
+namespace llvm {
+ class Constant;
+}
+
namespace clang {
class APValue;
class ASTContext;
@@ -3769,6 +3773,17 @@
/// - the semantic form, if this is in syntactic form.
llvm::PointerIntPair<InitListExpr *, 1, bool> AltForm;
+ // In cases like:
+ // struct Q1 { char a[6]; } q1 = { .a = {"foo"}, .a[2] = 'x' };
+ //
+ // In the InitListExpr corresponding to "q1.a", PrevInitExpr will hold the
+ // Expr corresponding to the string literal "foo". During the CodeGen phase,
+ // this PrevInitExpr is evaluated and recursively passed down to the child
+ // elements. If the child elements are also InitListExpr, the intermediate
+ // evaluation results will be stored into their PrevInitConstant members.
+ llvm::Constant *PrevInitConstant;
+ Expr *PrevInitExpr;
+
/// \brief Either:
/// If this initializer list initializes an array with more elements than
/// there are initializers in the list, specifies an expression to be used
@@ -3784,7 +3799,8 @@
/// \brief Build an empty initializer list.
explicit InitListExpr(EmptyShell Empty)
- : Expr(InitListExprClass, Empty) { }
+ : Expr(InitListExprClass, Empty),
+ PrevInitConstant(nullptr), PrevInitExpr(nullptr) { }
unsigned getNumInits() const { return InitExprs.size(); }
@@ -3834,6 +3850,14 @@
/// accommodate the new entry.
Expr *updateInit(const ASTContext &C, unsigned Init, Expr *expr);
+ llvm::Constant *getPrevInitConstant() const { return PrevInitConstant; }
+ void setPrevInitConstant(llvm::Constant *NewPrevInitConstant) {
+ PrevInitConstant = NewPrevInitConstant;
+ }
+
+ Expr *getPrevInitExpr() const { return PrevInitExpr; }
+ void setPrevInitExpr(Expr *newExpr) { PrevInitExpr = newExpr; }
+
/// \brief If this initializer list initializes an array with more elements
/// than there are initializers in the list, specifies an expression to be
/// used for value initialization of the rest of the elements.
Index: llvm/tools/clang/lib/AST/Expr.cpp
===================================================================
--- llvm/tools/clang/lib/AST/Expr.cpp
+++ llvm/tools/clang/lib/AST/Expr.cpp
@@ -1881,7 +1881,8 @@
: Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false,
false, false),
InitExprs(C, initExprs.size()),
- LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), AltForm(nullptr, true)
+ LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), AltForm(nullptr, true),
+ PrevInitConstant(nullptr), PrevInitExpr(nullptr)
{
sawArrayRangeDesignator(false);
for (unsigned I = 0; I != initExprs.size(); ++I) {
@@ -2772,10 +2773,12 @@
if (ILE->getType()->isArrayType()) {
unsigned numInits = ILE->getNumInits();
for (unsigned i = 0; i < numInits; i++) {
- if (!ILE->getInit(i)->isConstantInitializer(Ctx, false, Culprit))
- return false;
+ if (const Expr *Init = ILE->getInit(i))
+ if (!Init->isConstantInitializer(Ctx, false, Culprit))
+ return false;
}
- return true;
+ return !ILE->getPrevInitExpr() ||
+ ILE->getPrevInitExpr()->isConstantInitializer(Ctx, false, Culprit);
}
if (ILE->getType()->isRecordType()) {
@@ -2792,6 +2795,8 @@
if (ElementNo < ILE->getNumInits()) {
const Expr *Elt = ILE->getInit(ElementNo++);
+ if (!Elt)
+ continue;
if (Field->isBitField()) {
// Bitfields have to evaluate to an integer.
llvm::APSInt ResultTmp;
@@ -2807,7 +2812,8 @@
}
}
}
- return true;
+ return !ILE->getPrevInitExpr() ||
+ ILE->getPrevInitExpr()->isConstantInitializer(Ctx, false, Culprit);
}
break;
@@ -2981,6 +2987,9 @@
if (const Expr *E = cast<InitListExpr>(this)->getArrayFiller())
if (E->HasSideEffects(Ctx))
return true;
+ if (const Expr *E = cast<InitListExpr>(this)->getPrevInitExpr())
+ if (E->HasSideEffects(Ctx))
+ return true;
break;
case GenericSelectionExprClass:
Index: llvm/tools/clang/lib/AST/ExprConstant.cpp
===================================================================
--- llvm/tools/clang/lib/AST/ExprConstant.cpp
+++ llvm/tools/clang/lib/AST/ExprConstant.cpp
@@ -5265,7 +5265,7 @@
LValue Subobject = This;
- bool HaveInit = ElementNo < E->getNumInits();
+ bool HaveInit = ElementNo < E->getNumInits() && E->getInit(ElementNo);
// FIXME: Diagnostics here should point to the end of the initializer
// list, not the start.
@@ -5725,7 +5725,8 @@
Subobject.addArray(Info, E, CAT);
for (unsigned Index = 0; Index != NumEltsToInit; ++Index) {
const Expr *Init =
- Index < E->getNumInits() ? E->getInit(Index) : FillerExpr;
+ (Index < E->getNumInits() && E->getInit(Index)) ?
+ E->getInit(Index) : FillerExpr;
if (!EvaluateInPlace(Result.getArrayInitializedElt(Index),
Info, Subobject, Init) ||
!HandleLValueArrayAdjustment(Info, Init, Subobject,
Index: llvm/tools/clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- llvm/tools/clang/lib/AST/ItaniumMangle.cpp
+++ llvm/tools/clang/lib/AST/ItaniumMangle.cpp
@@ -2708,7 +2708,8 @@
Out << "il";
const InitListExpr *InitList = cast<InitListExpr>(E);
for (unsigned i = 0, e = InitList->getNumInits(); i != e; ++i)
- mangleExpression(InitList->getInit(i));
+ if (InitList->getInit(i))
+ mangleExpression(InitList->getInit(i));
Out << "E";
break;
}
@@ -2788,7 +2789,8 @@
// Only take InitListExprs apart for list-initialization.
const InitListExpr *InitList = cast<InitListExpr>(Init);
for (unsigned i = 0, e = InitList->getNumInits(); i != e; ++i)
- mangleExpression(InitList->getInit(i));
+ if (InitList->getInit(i))
+ mangleExpression(InitList->getInit(i));
} else
mangleExpression(Init);
}
Index: llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp
===================================================================
--- llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp
+++ llvm/tools/clang/lib/CodeGen/CGExprAgg.cpp
@@ -433,6 +433,7 @@
}
llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1);
+ Expr *filler = E->getArrayFiller();
// The 'current element to initialize'. The invariants on this
// variable are complicated. Essentially, after each iteration of
@@ -454,17 +455,19 @@
}
LValue elementLV = CGF.MakeAddrLValue(element, elementType);
- EmitInitializationToLValue(E->getInit(i), elementLV);
+ if (E->getInit(i))
+ EmitInitializationToLValue(E->getInit(i), elementLV);
+ else if (filler)
+ EmitInitializationToLValue(filler, elementLV);
}
// Check whether there's a non-trivial array-fill expression.
- Expr *filler = E->getArrayFiller();
bool hasTrivialFiller = isTrivialFiller(filler);
// Any remaining elements need to be zero-initialized, possibly
// using the filler expression. We can skip this if the we're
// emitting to zeroed memory.
- if (NumInitElements != NumArrayElements &&
+ if (NumInitElements != NumArrayElements && filler &&
!(Dest.isZeroed() && hasTrivialFiller &&
CGF.getTypes().isZeroInitializable(elementType))) {
@@ -493,10 +496,7 @@
// Emit the actual filler expression.
LValue elementLV = CGF.MakeAddrLValue(currentElement, elementType);
- if (filler)
- EmitInitializationToLValue(filler, elementLV);
- else
- EmitNullInitializationToLValue(elementLV);
+ EmitInitializationToLValue(filler, elementLV);
// Move on to the next element.
llvm::Value *nextElement =
@@ -1119,6 +1119,8 @@
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddr(), E->getType(),
Dest.getAlignment());
+ if (E->getPrevInitExpr())
+ EmitInitializationToLValue(E->getPrevInitExpr(), DestLV);
// Handle initialization of an array.
if (E->getType()->isArrayType()) {
@@ -1217,14 +1219,12 @@
LValue LV = CGF.EmitLValueForFieldInitialization(DestLV, field);
// We never generate write-barries for initialized fields.
LV.setNonGC(true);
-
- if (curInitIndex < NumInitElements) {
+
+ if (curInitIndex < NumInitElements && E->getInit(curInitIndex)) {
// Store the initializer into the field.
EmitInitializationToLValue(E->getInit(curInitIndex++), LV);
- } else {
- // We're out of initalizers; default-initialize to null
- EmitNullInitializationToLValue(LV);
- }
+ } else
+ ++curInitIndex;
// Push a destructor if necessary.
// FIXME: if we have an array of structures, all explicitly
@@ -1306,7 +1306,7 @@
if (Field->getType()->isReferenceType())
NumNonZeroBytes += CGF.getContext().toCharUnitsFromBits(
CGF.getTarget().getPointerWidth(0));
- else
+ else if (E)
NumNonZeroBytes += GetNumNonZeroBytesInInit(E, CGF);
}
@@ -1317,7 +1317,8 @@
CharUnits NumNonZeroBytes = CharUnits::Zero();
for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i)
- NumNonZeroBytes += GetNumNonZeroBytesInInit(ILE->getInit(i), CGF);
+ if (ILE->getInit(i))
+ NumNonZeroBytes += GetNumNonZeroBytesInInit(ILE->getInit(i), CGF);
return NumNonZeroBytes;
}
Index: llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp
===================================================================
--- llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp
+++ llvm/tools/clang/lib/CodeGen/CGExprCXX.cpp
@@ -923,9 +923,22 @@
"got wrong type of element to initialize");
// If we have an empty initializer list, we can usually use memset.
- if (auto *ILE = dyn_cast<InitListExpr>(Init))
- if (ILE->getNumInits() == 0 && TryMemsetInitialization())
- return;
+ 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();
Index: llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp
===================================================================
--- llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp
+++ llvm/tools/clang/lib/CodeGen/CGExprConstant.cpp
@@ -352,6 +352,14 @@
unsigned FieldNo = 0;
unsigned ElementNo = 0;
+
+ llvm::ConstantStruct *PrevInitEvaluated = nullptr;
+ if (ILE->getPrevInitExpr())
+ PrevInitEvaluated = llvm::dyn_cast_or_null<llvm::ConstantStruct>
+ (CGM.EmitConstantExpr(ILE->getPrevInitExpr(), ILE->getType(), CGF));
+ else if (ILE->getPrevInitConstant())
+ PrevInitEvaluated = llvm::dyn_cast_or_null<llvm::ConstantStruct>
+ (ILE->getPrevInitConstant());
for (RecordDecl::field_iterator Field = RD->field_begin(),
FieldEnd = RD->field_end(); Field != FieldEnd; ++Field, ++FieldNo) {
@@ -363,15 +371,22 @@
if (Field->isUnnamedBitfield())
continue;
- // Get the initializer. A struct can include fields without initializers,
- // we just use explicit null values for them.
- llvm::Constant *EltInit;
- if (ElementNo < ILE->getNumInits())
- EltInit = CGM.EmitConstantExpr(ILE->getInit(ElementNo++),
- Field->getType(), CGF);
+ // Get the initializer.
+ Expr *Init = ElementNo < ILE->getNumInits() ? ILE->getInit(ElementNo) : nullptr;
+ if (InitListExpr *InnerILE = dyn_cast_or_null<InitListExpr>(Init))
+ if (PrevInitEvaluated && !InnerILE->getPrevInitExpr())
+ InnerILE->setPrevInitConstant(PrevInitEvaluated->getOperand(ElementNo));
+
+ llvm::Constant *EltInit = nullptr;
+ if (Init)
+ EltInit = CGM.EmitConstantExpr(Init, Field->getType(), CGF);
+ else if (PrevInitEvaluated)
+ EltInit = PrevInitEvaluated->getOperand(ElementNo);
else
EltInit = CGM.EmitNullConstant(Field->getType());
+ ++ ElementNo;
+
if (!EltInit)
return false;
@@ -730,6 +745,37 @@
unsigned NumInitElements = ILE->getNumInits();
unsigned NumElements = AType->getNumElements();
+ // Filler value for implicitly initialized array elements.
+ // FIXME: This doesn't handle member pointers correctly!
+ llvm::Constant *fillC;
+ if (Expr *filler = ILE->getArrayFiller())
+ fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF);
+ else
+ fillC = llvm::Constant::getNullValue(ElemTy);
+
+ // In cases like:
+ // struct Q1 { char a[6]; } q1 = { .a = {"foo"}, .a[2] = 'x' };
+ // ILE->PrevInitExpr holds a string literal corresponding to "foo".
+ // This string literal is evaluated here to an array of Constant and
+ // some parts of the array get replaced with 'x'.
+ llvm::ConstantArray *PrevInitEvaluated = nullptr;
+ if (ILE->getPrevInitExpr()) {
+ llvm::Constant *TempInitEvaluated = Visit(ILE->getPrevInitExpr());
+ PrevInitEvaluated =
+ llvm::dyn_cast_or_null<llvm::ConstantArray>(TempInitEvaluated);
+
+ // While it is possible to always check for both ConstantArray and
+ // ConstantDataArray everywhere, I think it is prone to error. So just
+ // convert a ConstantDataArray to ConstantArray.
+ if (TempInitEvaluated && !PrevInitEvaluated)
+ if (llvm::ConstantDataArray *TempDataArray =
+ llvm::dyn_cast<llvm::ConstantDataArray>(TempInitEvaluated))
+ PrevInitEvaluated = llvm::dyn_cast<llvm::ConstantArray>
+ (llvm::ConstantArray::get(AType, TempDataArray));
+ } else if (ILE->getPrevInitConstant())
+ PrevInitEvaluated = llvm::dyn_cast_or_null<llvm::ConstantArray>
+ (ILE->getPrevInitConstant());
+
// Initialising an array requires us to automatically
// initialise any elements that have not been initialised explicitly
unsigned NumInitableElts = std::min(NumInitElements, NumElements);
@@ -736,12 +782,21 @@
// Copy initializer elements.
std::vector<llvm::Constant*> Elts;
- Elts.reserve(NumInitableElts + NumElements);
+ Elts.reserve(NumElements);
bool RewriteType = false;
- for (unsigned i = 0; i < NumInitableElts; ++i) {
- Expr *Init = ILE->getInit(i);
- llvm::Constant *C = CGM.EmitConstantExpr(Init, Init->getType(), CGF);
+ for (unsigned i = 0; i < NumElements; ++i) {
+ Expr *Init = i < NumInitElements ? ILE->getInit(i) : nullptr;
+ if (InitListExpr *InnerILE = dyn_cast_or_null<InitListExpr>(Init))
+ if (PrevInitEvaluated && !InnerILE->getPrevInitExpr())
+ InnerILE->setPrevInitConstant(PrevInitEvaluated->getOperand(i));
+ llvm::Constant *C = fillC;
+
+ if (Init)
+ C = CGM.EmitConstantExpr(Init, Init->getType(), CGF);
+ else if (!ILE->hasArrayFiller() && PrevInitEvaluated)
+ C = PrevInitEvaluated->getOperand(i);
+
if (!C)
return nullptr;
RewriteType |= (C->getType() != ElemTy);
@@ -748,18 +803,6 @@
Elts.push_back(C);
}
- // Initialize remaining array elements.
- // FIXME: This doesn't handle member pointers correctly!
- llvm::Constant *fillC;
- if (Expr *filler = ILE->getArrayFiller())
- fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF);
- else
- fillC = llvm::Constant::getNullValue(ElemTy);
- if (!fillC)
- return nullptr;
- RewriteType |= (fillC->getType() != ElemTy);
- Elts.resize(NumElements, fillC);
-
if (RewriteType) {
// FIXME: Try to avoid packing the array
std::vector<llvm::Type*> Types;
Index: llvm/tools/clang/lib/Sema/SemaDecl.cpp
===================================================================
--- llvm/tools/clang/lib/Sema/SemaDecl.cpp
+++ llvm/tools/clang/lib/Sema/SemaDecl.cpp
@@ -8268,7 +8268,8 @@
isInitList = true;
InitFieldIndex.push_back(0);
for (auto Child : InitList->children()) {
- CheckExpr(cast<Expr>(Child));
+ if (Child)
+ CheckExpr(cast<Expr>(Child));
++InitFieldIndex.back();
}
InitFieldIndex.pop_back();
Index: llvm/tools/clang/lib/Sema/SemaInit.cpp
===================================================================
--- llvm/tools/clang/lib/Sema/SemaInit.cpp
+++ llvm/tools/clang/lib/Sema/SemaInit.cpp
@@ -306,7 +306,8 @@
QualType CurrentObjectType,
InitListExpr *StructuredList,
unsigned StructuredIndex,
- SourceRange InitRange);
+ SourceRange InitRange,
+ bool IsFullyOverwritten = false);
void UpdateStructuredListElement(InitListExpr *StructuredList,
unsigned &StructuredIndex,
Expr *expr);
@@ -460,6 +461,25 @@
unsigned NumInits = ILE->getNumInits();
InitializedEntity MemberEntity
= InitializedEntity::InitializeMember(Field, &ParentEntity);
+
+ // Assume we have the following definitions:
+ //
+ // struct P { char x[6][6]; };
+ // struct P xp = { .x[1] = "bar" };
+ // struct PP { struct P lp; };
+ // struct PP l = { .lp = xp, .lp.x[1][2] = 'f' };
+ //
+ // l.lp.x[1][0..1] should not be filled with implicit initializers because the
+ // previous initializers will provide values for them; l.lp.x[1] will be "baf".
+ //
+ // But if we have:
+ // struct PP l = { .lp = xp, .lp.x[1] = { [2] = 'f' } };
+ //
+ // l.lp.x[1][0..1] are implicitly initialized and do not use values from the
+ // previous initializer; l.lp.x[1] will be "\0\0f\0\0\0".
+ if (ILE->getPrevInitExpr())
+ return;
+
if (Init >= NumInits || !ILE->getInit(Init)) {
// C++1y [dcl.init.aggr]p7:
// If there are fewer initializer-clauses in the list than there are
@@ -525,6 +545,24 @@
assert((ILE->getType() != SemaRef.Context.VoidTy) &&
"Should not have void type");
+ // Assume we have the following definitions:
+ //
+ // struct P { char x[6][6]; };
+ // struct P xp = { .x[1] = "bar" };
+ // struct PP { struct P lp; };
+ // struct PP l = { .lp = xp, .lp.x[1][2] = 'f' };
+ //
+ // l.lp.x[1][0..1] should not be filled with implicit initializers because the
+ // previous initializers will provide values for them; l.lp.x[1] will be "baf".
+ //
+ // But if we have:
+ // struct PP l = { .lp = xp, .lp.x[1] = { [2] = 'f' } };
+ //
+ // l.lp.x[1][0..1] are implicitly initialized and do not use values from the
+ // previous initializer; l.lp.x[1] will be "\0\0f\0\0\0".
+ if (ILE->getPrevInitExpr())
+ return;
+
if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) {
const RecordDecl *RDecl = RType->getDecl();
if (RDecl->isUnion() && ILE->getInitializedFieldInUnion())
@@ -539,6 +577,13 @@
}
}
} else {
+ unsigned NumFields = 0;
+ for (auto Field : RDecl->fields())
+ if (!Field->isUnnamedBitfield())
+ ++NumFields;
+ if (ILE->getNumInits() < NumFields)
+ ILE->resizeInits(SemaRef.Context, NumFields);
+
unsigned Init = 0;
for (auto *Field : RDecl->fields()) {
if (Field->isUnnamedBitfield())
@@ -785,6 +830,10 @@
return;
}
+ SourceLocation LocStart = IList->getInit(Index) ?
+ IList->getInit(Index)->getLocStart() : IList->getLocStart();
+ SourceRange SrcRange = IList->getInit(Index) ?
+ IList->getInit(Index)->getSourceRange() : IList->getSourceRange();
if (StructuredIndex == 1 &&
IsStringInit(StructuredList->getInit(0), T, SemaRef.Context) ==
SIF_None) {
@@ -794,8 +843,7 @@
hadError = true;
}
// Special-case
- SemaRef.Diag(IList->getInit(Index)->getLocStart(), DK)
- << IList->getInit(Index)->getSourceRange();
+ SemaRef.Diag(LocStart, DK) << SrcRange;
} else if (!T->isIncompleteType()) {
// Don't complain for incomplete types, since we'll get an error
// elsewhere
@@ -817,8 +865,7 @@
hadError = true;
}
- SemaRef.Diag(IList->getInit(Index)->getLocStart(), DK)
- << initKind << IList->getInit(Index)->getSourceRange();
+ SemaRef.Diag(LocStart, DK) << initKind << SrcRange;
}
}
@@ -900,20 +947,36 @@
StructuredList, StructuredIndex);
if (InitListExpr *SubInitList = dyn_cast<InitListExpr>(expr)) {
- if (!ElemType->isRecordType() || ElemType->isAggregateType()) {
- InitListExpr *InnerStructuredList
- = getStructuredSubobjectInit(IList, Index, ElemType,
- StructuredList, StructuredIndex,
- SubInitList->getSourceRange());
- CheckExplicitInitList(Entity, SubInitList, ElemType,
- InnerStructuredList);
- ++StructuredIndex;
- ++Index;
- return;
+ if (SubInitList->getNumInits() == 1 &&
+ IsStringInit(SubInitList->getInit(0), ElemType, SemaRef.Context) ==
+ SIF_None) {
+ expr = SubInitList->getInit(0);
+ } else {
+ if (!ElemType->isRecordType() || ElemType->isAggregateType()) {
+ InitListExpr *InnerStructuredList
+ = getStructuredSubobjectInit(IList, Index, ElemType,
+ StructuredList, StructuredIndex,
+ SubInitList->getSourceRange(), true);
+ CheckExplicitInitList(Entity, SubInitList, ElemType,
+ InnerStructuredList);
+
+ if (!hadError && !VerifyOnly) {
+ bool RequiresSecondPass = false;
+ FillInEmptyInitializations(Entity, InnerStructuredList,
+ RequiresSecondPass);
+ if (RequiresSecondPass && !hadError)
+ FillInEmptyInitializations(Entity, InnerStructuredList,
+ RequiresSecondPass);
+ }
+
+ ++StructuredIndex;
+ ++Index;
+ return;
+ }
+ assert(SemaRef.getLangOpts().CPlusPlus &&
+ "non-aggregate records are only possible in C++");
+ // C++ initialization is handled later.
}
- assert(SemaRef.getLangOpts().CPlusPlus &&
- "non-aggregate records are only possible in C++");
- // C++ initialization is handled later.
} else if (isa<ImplicitValueInitExpr>(expr)) {
// This happens during template instantiation when we see an InitListExpr
// that we've already checked once.
@@ -1359,7 +1422,7 @@
const ArrayType *arrayType = SemaRef.Context.getAsArrayType(DeclType);
// Check for the special-case of initializing an array with a string.
- if (Index < IList->getNumInits()) {
+ if (Index < IList->getNumInits() && IList->getInit(Index)) {
if (IsStringInit(IList->getInit(Index), arrayType, SemaRef.Context) ==
SIF_None) {
// We place the string literal directly into the resulting
@@ -1405,6 +1468,10 @@
QualType elementType = arrayType->getElementType();
while (Index < IList->getNumInits()) {
Expr *Init = IList->getInit(Index);
+ if (!Init) {
+ ++Index;
+ continue;
+ }
if (DesignatedInitExpr *DIE = dyn_cast<DesignatedInitExpr>(Init)) {
// If we're not the subobject that matches up with the '{' for
// the designator, we shouldn't be handling the
@@ -2298,7 +2365,8 @@
QualType CurrentObjectType,
InitListExpr *StructuredList,
unsigned StructuredIndex,
- SourceRange InitRange) {
+ SourceRange InitRange,
+ bool IsFullyOverwritten) {
if (VerifyOnly)
return nullptr; // No structured list in verification-only mode.
Expr *ExistingInit = nullptr;
@@ -2308,9 +2376,18 @@
ExistingInit = StructuredList->getInit(StructuredIndex);
if (InitListExpr *Result = dyn_cast_or_null<InitListExpr>(ExistingInit))
- return Result;
+ if (!IsFullyOverwritten)
+ return Result;
- if (ExistingInit) {
+ // We need to check on source range validity because the previous
+ // initializer does not have to be an explicit initializer. e.g.,
+ //
+ // struct P { int a, b; };
+ // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 };
+ //
+ // There is an overwrite taking place because the first braced initializer
+ // list "{ .a = 2 }" already provides value for .p.b (which is zero).
+ if (ExistingInit && ExistingInit->getSourceRange().isValid()) {
// We are creating an initializer list that initializes the
// subobjects of the current object, but there was already an
// initialization that completely initialized the current
@@ -2378,9 +2455,22 @@
// Link this new initializer list into the structured initializer
// lists.
- if (StructuredList)
- StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result);
- else {
+ if (StructuredList) {
+ // There might have already been initializers for subobjects of the current
+ // object, but a subsequent initializer list will overwrite the entirety
+ // of the current object. (See DR 253 and C99 6.7.8p21). e.g.,
+ //
+ // struct P { char x[6]; };
+ // struct P l = { .x[2] = 'x', .x = { [0] = 'f' } };
+ //
+ // The first designated initializer is ignored, and l.x is just "f"
+ Result->setPrevInitExpr(StructuredList->updateInit(SemaRef.Context,
+ StructuredIndex, Result));
+ if (!Result->getPrevInitExpr() && StructuredList->hasArrayFiller()/*StructuredList->arrayIsFilledWithDummy()*/)
+ Result->setPrevInitExpr(StructuredList->getArrayFiller());
+ if (IsFullyOverwritten)
+ Result->setPrevInitExpr(nullptr);
+ } else {
Result->setSyntacticForm(IList);
SyntacticToSemantic[IList] = Result;
}
@@ -2399,14 +2489,22 @@
if (Expr *PrevInit = StructuredList->updateInit(SemaRef.Context,
StructuredIndex, expr)) {
- // This initializer overwrites a previous initializer. Warn.
- SemaRef.Diag(expr->getLocStart(),
- diag::warn_initializer_overrides)
- << expr->getSourceRange();
- SemaRef.Diag(PrevInit->getLocStart(),
- diag::note_previous_initializer)
- << /*FIXME:has side effects=*/0
- << PrevInit->getSourceRange();
+ // We need to check on source range validity because the previous
+ // initializer does not have to be an explicit initializer.
+ // struct P { int a, b; };
+ // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 };
+ // There is an overwrite taking place because the first braced initializer
+ // list "{ .a = 2 }' already provides value for .p.b (which is zero).
+ if (PrevInit->getSourceRange().isValid()) {
+ // This initializer overwrites a previous initializer. Warn.
+ SemaRef.Diag(expr->getLocStart(),
+ diag::warn_initializer_overrides)
+ << expr->getSourceRange();
+ SemaRef.Diag(PrevInit->getLocStart(),
+ diag::note_previous_initializer)
+ << /*FIXME:has side effects=*/0
+ << PrevInit->getSourceRange();
+ }
}
++StructuredIndex;
Index: llvm/tools/clang/lib/Sema/SemaOverload.cpp
===================================================================
--- llvm/tools/clang/lib/Sema/SemaOverload.cpp
+++ llvm/tools/clang/lib/Sema/SemaOverload.cpp
@@ -4503,6 +4503,8 @@
if (!X.isNull()) {
for (unsigned i = 0, e = From->getNumInits(); i < e; ++i) {
Expr *Init = From->getInit(i);
+ if (!Init)
+ continue;
ImplicitConversionSequence ICS =
TryCopyInitialization(S, Init, X, SuppressUserConversions,
InOverloadResolution,
Index: llvm/tools/clang/lib/Sema/TreeTransform.h
===================================================================
--- llvm/tools/clang/lib/Sema/TreeTransform.h
+++ llvm/tools/clang/lib/Sema/TreeTransform.h
@@ -2968,6 +2968,9 @@
SmallVectorImpl<Expr *> &Outputs,
bool *ArgChanged) {
for (unsigned I = 0; I != NumInputs; ++I) {
+ if (!Inputs[I])
+ continue;
+
// If requested, drop call arguments that need to be dropped.
if (IsCall && getDerived().DropCallArgument(Inputs[I])) {
if (ArgChanged)
Index: llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp
+++ llvm/tools/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -716,8 +716,11 @@
E->setSyntacticForm(SyntForm);
E->setLBraceLoc(ReadSourceLocation(Record, Idx));
E->setRBraceLoc(ReadSourceLocation(Record, Idx));
+ bool isPrevInitExpr = Record[Idx++];
bool isArrayFiller = Record[Idx++];
Expr *filler = nullptr;
+ if (isPrevInitExpr)
+ E->PrevInitExpr = Reader.ReadSubExpr();
if (isArrayFiller) {
filler = Reader.ReadSubExpr();
E->ArrayFillerOrUnionFieldInit = filler;
Index: llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp
+++ llvm/tools/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -679,8 +679,12 @@
Writer.AddStmt(E->getSyntacticForm());
Writer.AddSourceLocation(E->getLBraceLoc(), Record);
Writer.AddSourceLocation(E->getRBraceLoc(), Record);
- bool isArrayFiller = E->ArrayFillerOrUnionFieldInit.is<Expr*>();
+ bool isPrevInitExpr = !!E->getPrevInitExpr();
+ Record.push_back(isPrevInitExpr);
+ bool isArrayFiller = E->hasArrayFiller();
Record.push_back(isArrayFiller);
+ if (isPrevInitExpr)
+ Writer.AddStmt(E->getPrevInitExpr());
if (isArrayFiller)
Writer.AddStmt(E->getArrayFiller());
else
@@ -692,7 +696,7 @@
// Replace them by 0 to indicate that the filler goes in that place.
Expr *filler = E->getArrayFiller();
for (unsigned I = 0, N = E->getNumInits(); I != N; ++I)
- Writer.AddStmt(E->getInit(I) != filler ? E->getInit(I) : nullptr);
+ Writer.AddStmt(E->getInit(I) && E->getInit(I) != filler ? E->getInit(I) : nullptr);
} else {
for (unsigned I = 0, N = E->getNumInits(); I != N; ++I)
Writer.AddStmt(E->getInit(I));
Index: llvm/tools/clang/test/CodeGen/Inputs/stdio.h
===================================================================
--- llvm/tools/clang/test/CodeGen/Inputs/stdio.h
+++ llvm/tools/clang/test/CodeGen/Inputs/stdio.h
@@ -1,4 +1,5 @@
struct FILE;
+extern int printf(const char *format, ...);
extern int vfprintf(struct FILE *s, const char *format, __builtin_va_list arg);
extern int vprintf(const char *format, __builtin_va_list arg);
Index: llvm/tools/clang/test/CodeGen/partial-reinitialization1.c
===================================================================
--- llvm/tools/clang/test/CodeGen/partial-reinitialization1.c
+++ llvm/tools/clang/test/CodeGen/partial-reinitialization1.c
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -isystem %S/Inputs %s -emit-llvm -o - | FileCheck %s
+
+struct P1 {
+ struct Q1 {
+ char a[6];
+ char b[6];
+ } q;
+};
+
+// CHECK: { [6 x i8] c"foo\00\00\00", [6 x i8] c"\00x\00\00\00\00" }
+struct P1 l1 = {
+ (struct Q1){ "foo", "bar" },
+ .q.b = { "boo" },
+ .q.b = { [1] = 'x' }
+};
+
+// CHECK: { [6 x i8] c"foo\00\00\00", [6 x i8] c"bxo\00\00\00" }
+struct P1 l1a = {
+ (struct Q1){ "foo", "bar" },
+ .q.b = { "boo" },
+ .q.b[1] = 'x'
+};
+
+
+struct P2 { char x[6]; };
+
+// CHECK: { [6 x i8] c"n\00\00\00\00\00" }
+struct P2 l2 = {
+ .x = { [1] = 'o' },
+ .x = { [0] = 'n' }
+};
+
+struct P3 {
+ struct Q3 {
+ struct R1 {
+ int a, b, c;
+ } r1;
+
+ struct R2 {
+ int d, e, f;
+ } r2;
+ } q;
+};
+
+// CHECK: @l3 = global %struct.P3 { %struct.Q3 { %struct.R1 { i32 1, i32 2, i32 3 }, %struct.R2 { i32 0, i32 10, i32 0 } } }
+struct P3 l3 = {
+ (struct Q3){ { 1, 2, 3 }, { 4, 5, 6 } },
+ .q.r2 = { 7, 8, 9 },
+ .q.r2 = { .e = 10 }
+};
+
+// This bit is taken from Sema/wchar.c so we can avoid the wchar.h include.
+typedef __WCHAR_TYPE__ wchar_t;
+#if defined(_WIN32) || defined(_M_IX86) || defined(__CYGWIN__) \
+ || defined(_M_X64) || defined(SHORT_WCHAR)
+ #define WCHAR_T_TYPE unsigned short
+#elif defined(__sun) || defined(__AuroraUX__)
+ #define WCHAR_T_TYPE long
+#else /* Solaris or AuroraUX. */
+ #define WCHAR_T_TYPE int
+#endif
+
+struct P4 {
+ wchar_t x[6];
+};
+
+// CHECK: { [6 x i32] [i32 102, i32 111, i32 120, i32 0, i32 0, i32 0] }
+struct P4 l4 = { { L"foo" }, .x[2] = L'x' };
Index: llvm/tools/clang/test/CodeGen/partial-reinitialization2.c
===================================================================
--- llvm/tools/clang/test/CodeGen/partial-reinitialization2.c
+++ llvm/tools/clang/test/CodeGen/partial-reinitialization2.c
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -isystem %S/Inputs %s -emit-llvm -o - | lli | FileCheck %s
+
+#include <stdio.h>
+
+struct P1 { char x[6]; } g1 = { "foo" };
+struct LP1 { struct P1 p1; };
+
+struct P2 { int a, b, c; } g2 = { 1, 2, 3 };
+struct LP2 { struct P2 p2; };
+
+
+void test1(void)
+{ // CHECK: fox
+ struct LP1 l = { .p1 = g1, .p1.x[2] = 'x' };
+ int i;
+ for (i = 0; i < 6; i ++)
+ printf("%c", l.p1.x[i]);
+ printf("\n");
+}
+
+void test2(void)
+{ // CHECK: fro
+ struct LP1 l = { .p1 = g1, .p1.x[1] = 'r' };
+ int i;
+ for (i = 0; i < 6; i ++)
+ printf("%c", l.p1.x[i]);
+ printf("\n");
+}
+
+void test3(void)
+{ // CHECK: 1 10 3
+ struct LP2 l = { .p2 = g2, .p2.b = 10 };
+ printf("%d %d %d\n", l.p2.a, l.p2.b, l.p2.c);
+}
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+ return 0;
+}
Index: llvm/tools/clang/test/PCH/designated-init.c.h
===================================================================
--- llvm/tools/clang/test/PCH/designated-init.c.h
+++ llvm/tools/clang/test/PCH/designated-init.c.h
@@ -40,3 +40,16 @@
},
}
};
+
+struct P1 {
+ struct Q1 {
+ char a[6];
+ char b[6];
+ } q;
+};
+
+struct P1 l1 = {
+ (struct Q1){ "foo", "bar" },
+ .q.b = { "boo" },
+ .q.b = { [1] = 'x' }
+};
Index: llvm/tools/clang/test/Sema/designated-initializers.c
===================================================================
--- llvm/tools/clang/test/Sema/designated-initializers.c
+++ llvm/tools/clang/test/Sema/designated-initializers.c
@@ -45,8 +45,8 @@
struct point array2[10] = {
[10].x = 2.0, // expected-error{{array designator index (10) exceeds array bounds (10)}}
- [4 ... 5].y = 2.0,
- [4 ... 6] = { .x = 3, .y = 4.0 }
+ [4 ... 5].y = 2.0, // expected-note 2 {{previous initialization is here}}
+ [4 ... 6] = { .x = 3, .y = 4.0 } // expected-warning 2 {{subobject initialization overrides initialization of other fields within its enclosing subobject}}
};
struct point array3[10] = {
@@ -332,12 +332,12 @@
int M;
} overwrite_string[] = {
{ { "foo" }, 1 }, // expected-note {{previous initialization is here}}
- [0].L[2] = 'x' // expected-warning{{initializer overrides prior initialization of this subobject}}
+ [0].L[2] = 'x' // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}}
};
struct overwrite_string_struct2 {
char L[6];
int M;
} overwrite_string2[] = {
- { { "foo" }, 1 },
- [0].L[4] = 'x' // no-warning
+ { { "foo" }, 1 }, // expected-note {{previous initialization is here}}
+ [0].L[4] = 'x' // expected-warning{{subobject initialization overrides initialization of other fields within its enclosing subobject}}
};
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits