forked from OSchip/llvm-project
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:
parent
7912d9b899
commit
cb77930d6b
|
@ -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())
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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*/";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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};
|
|
@ -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 };
|
|
@ -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 };
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue