When value-initializing a class with no user-defined constructors but

with a non-trivial default constructor, zero-initialize the storage
and then call the default constructor. Fixes PR5800.

llvm-svn: 91548
This commit is contained in:
Douglas Gregor 2009-12-16 18:50:27 +00:00
parent 22a8a4bfb9
commit 4f4b186215
11 changed files with 112 additions and 37 deletions

View File

@ -490,8 +490,8 @@ class CXXConstructExpr : public Expr {
CXXConstructorDecl *Constructor;
SourceLocation Loc;
bool Elidable;
bool Elidable : 1;
bool ZeroInitialization : 1;
Stmt **Args;
unsigned NumArgs;
@ -499,7 +499,8 @@ protected:
CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T,
SourceLocation Loc,
CXXConstructorDecl *d, bool elidable,
Expr **args, unsigned numargs);
Expr **args, unsigned numargs,
bool ZeroInitialization = false);
~CXXConstructExpr() { }
virtual void DoDestroy(ASTContext &C);
@ -512,7 +513,8 @@ public:
static CXXConstructExpr *Create(ASTContext &C, QualType T,
SourceLocation Loc,
CXXConstructorDecl *D, bool Elidable,
Expr **Args, unsigned NumArgs);
Expr **Args, unsigned NumArgs,
bool ZeroInitialization = false);
CXXConstructorDecl* getConstructor() const { return Constructor; }
@ -525,6 +527,13 @@ public:
bool isElidable() const { return Elidable; }
void setElidable(bool E) { Elidable = E; }
/// \brief Whether this construction first requires
/// zero-initialization before the initializer is called.
bool requiresZeroInitialization() const { return ZeroInitialization; }
void setRequiresZeroInitialization(bool ZeroInit) {
ZeroInitialization = ZeroInit;
}
typedef ExprIterator arg_iterator;
typedef ConstExprIterator const_arg_iterator;

View File

@ -380,28 +380,32 @@ CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(ASTContext &C,
CXXConstructExpr *CXXConstructExpr::Create(ASTContext &C, QualType T,
SourceLocation Loc,
CXXConstructorDecl *D, bool Elidable,
Expr **Args, unsigned NumArgs) {
Expr **Args, unsigned NumArgs,
bool ZeroInitialization) {
return new (C) CXXConstructExpr(C, CXXConstructExprClass, T, Loc, D,
Elidable, Args, NumArgs);
Elidable, Args, NumArgs, ZeroInitialization);
}
CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T,
SourceLocation Loc,
CXXConstructorDecl *D, bool elidable,
Expr **args, unsigned numargs)
Expr **args, unsigned numargs,
bool ZeroInitialization)
: Expr(SC, T,
T->isDependentType(),
(T->isDependentType() ||
CallExpr::hasAnyValueDependentArguments(args, numargs))),
Constructor(D), Loc(Loc), Elidable(elidable), Args(0), NumArgs(numargs) {
if (NumArgs) {
Args = new (C) Stmt*[NumArgs];
for (unsigned i = 0; i != NumArgs; ++i) {
assert(args[i] && "NULL argument in CXXConstructExpr");
Args[i] = args[i];
}
Constructor(D), Loc(Loc), Elidable(elidable),
ZeroInitialization(ZeroInitialization), Args(0), NumArgs(numargs)
{
if (NumArgs) {
Args = new (C) Stmt*[NumArgs];
for (unsigned i = 0; i != NumArgs; ++i) {
assert(args[i] && "NULL argument in CXXConstructExpr");
Args[i] = args[i];
}
}
}
CXXConstructExpr::CXXConstructExpr(EmptyShell Empty, ASTContext &C,

View File

@ -571,7 +571,7 @@ CodeGenFunction::EmitCXXConstructExpr(llvm::Value *Dest,
const CXXRecordDecl *RD =
cast<CXXRecordDecl>(InitType->getAs<RecordType>()->getDecl());
if (RD->hasTrivialConstructor())
return;
return;
}
// Code gen optimization to eliminate copy constructor and return
// its first argument instead.
@ -591,6 +591,7 @@ CodeGenFunction::EmitCXXConstructExpr(llvm::Value *Dest,
BasePtr = llvm::PointerType::getUnqual(BasePtr);
llvm::Value *BaseAddrPtr =
Builder.CreateBitCast(Dest, BasePtr);
EmitCXXAggrConstructorCall(CD, Array, BaseAddrPtr,
E->arg_begin(), E->arg_end());
}

View File

@ -458,6 +458,11 @@ AggExprEmitter::VisitCXXConstructExpr(const CXXConstructExpr *E) {
Val = CGF.CreateTempAlloca(CGF.ConvertTypeForMem(E->getType()), "tmp");
}
if (E->requiresZeroInitialization())
EmitNullInitializationToLValue(LValue::MakeAddr(Val,
E->getType().getQualifiers()),
E->getType());
CGF.EmitCXXConstructExpr(Val, E);
}

View File

@ -860,6 +860,7 @@ unsigned PCHStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) {
E->setConstructor(cast<CXXConstructorDecl>(Reader.GetDecl(Record[Idx++])));
E->setLocation(SourceLocation::getFromRawEncoding(Record[Idx++]));
E->setElidable(Record[Idx++]);
E->setRequiresZeroInitialization(Record[Idx++]);
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
E->setArg(I, cast<Expr>(StmtStack[StmtStack.size() - N + I]));
return E->getNumArgs();

View File

@ -787,6 +787,7 @@ void PCHStmtWriter::VisitCXXConstructExpr(CXXConstructExpr *E) {
Writer.AddDeclRef(E->getConstructor(), Record);
Writer.AddSourceLocation(E->getLocation(), Record);
Record.push_back(E->isElidable());
Record.push_back(E->requiresZeroInitialization());
Record.push_back(E->getNumArgs());
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
Writer.WriteSubStmt(E->getArg(I));

View File

@ -1816,7 +1816,8 @@ public:
OwningExprResult BuildCXXConstructExpr(SourceLocation ConstructLoc,
QualType DeclInitType,
CXXConstructorDecl *Constructor,
MultiExprArg Exprs);
MultiExprArg Exprs,
bool RequiresZeroInit = false);
// FIXME: Can re remove this and have the above BuildCXXConstructExpr check if
// the constructor can be elidable?
@ -1824,7 +1825,8 @@ public:
QualType DeclInitType,
CXXConstructorDecl *Constructor,
bool Elidable,
MultiExprArg Exprs);
MultiExprArg Exprs,
bool RequiresZeroInit = false);
OwningExprResult BuildCXXTemporaryObjectExpr(CXXConstructorDecl *Cons,
QualType writtenTy,

View File

@ -3759,7 +3759,8 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
Sema::OwningExprResult
Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
CXXConstructorDecl *Constructor,
MultiExprArg ExprArgs) {
MultiExprArg ExprArgs,
bool RequiresZeroInit) {
bool Elidable = false;
// C++ [class.copy]p15:
@ -3785,7 +3786,7 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
}
return BuildCXXConstructExpr(ConstructLoc, DeclInitType, Constructor,
Elidable, move(ExprArgs));
Elidable, move(ExprArgs), RequiresZeroInit);
}
/// BuildCXXConstructExpr - Creates a complete call to a constructor,
@ -3793,14 +3794,15 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
Sema::OwningExprResult
Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
CXXConstructorDecl *Constructor, bool Elidable,
MultiExprArg ExprArgs) {
MultiExprArg ExprArgs,
bool RequiresZeroInit) {
unsigned NumExprs = ExprArgs.size();
Expr **Exprs = (Expr **)ExprArgs.release();
MarkDeclarationReferenced(ConstructLoc, Constructor);
return Owned(CXXConstructExpr::Create(Context, DeclInitType, ConstructLoc,
Constructor,
Elidable, Exprs, NumExprs));
Constructor, Elidable, Exprs, NumExprs,
RequiresZeroInit));
}
Sema::OwningExprResult

View File

@ -444,6 +444,9 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity,
if (Field->isUnnamedBitfield())
continue;
if (hadError)
return;
InitializedEntity MemberEntity
= InitializedEntity::InitializeMember(*Field, &Entity);
if (Init >= NumInits || !ILE->getInit(Init)) {
@ -477,7 +480,7 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity,
= InitSeq.Perform(SemaRef, MemberEntity, Kind,
Sema::MultiExprArg(SemaRef, 0, 0));
if (MemberInit.isInvalid()) {
hadError = 0;
hadError = true;
return;
}
@ -529,6 +532,9 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity,
for (unsigned Init = 0; Init != NumElements; ++Init) {
if (hadError)
return;
if (ElementEntity.getKind() == InitializedEntity::EK_ArrayOrVectorElement)
ElementEntity.setElementIndex(Init);
@ -546,7 +552,7 @@ InitListChecker::FillInValueInitializations(const InitializedEntity &Entity,
= InitSeq.Perform(SemaRef, ElementEntity, Kind,
Sema::MultiExprArg(SemaRef, 0, 0));
if (ElementInit.isInvalid()) {
hadError = 0;
hadError = true;
return;
}
@ -585,7 +591,7 @@ InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity,
if (!hadError) {
bool RequiresSecondPass = false;
FillInValueInitializations(Entity, FullyStructuredList, RequiresSecondPass);
if (RequiresSecondPass)
if (RequiresSecondPass && !hadError)
FillInValueInitializations(Entity, FullyStructuredList,
RequiresSecondPass);
}
@ -2619,8 +2625,16 @@ static void TryValueInitialization(Sema &S,
if (ClassDecl->hasUserDeclaredConstructor())
return TryConstructorInitialization(S, Entity, Kind, 0, 0, T, Sequence);
// FIXME: non-union class type w/ non-trivial default constructor gets
// zero-initialized, then constructor gets called.
// -- if T is a (possibly cv-qualified) non-union class type
// without a user-provided constructor, then the object is
// zero-initialized and, if Ts implicitly-declared default
// constructor is non-trivial, that constructor is called.
if ((ClassDecl->getTagKind() == TagDecl::TK_class ||
ClassDecl->getTagKind() == TagDecl::TK_struct) &&
!ClassDecl->hasTrivialConstructor()) {
Sequence.AddZeroInitializationStep(Entity.getType().getType());
return TryConstructorInitialization(S, Entity, Kind, 0, 0, T, Sequence);
}
}
}
@ -3050,6 +3064,7 @@ InitializationSequence::Perform(Sema &S,
// Walk through the computed steps for the initialization sequence,
// performing the specified conversions along the way.
bool ConstructorInitRequiresZeroInit = false;
for (step_iterator Step = step_begin(), StepEnd = step_end();
Step != StepEnd; ++Step) {
if (CurInit.isInvalid())
@ -3216,7 +3231,8 @@ InitializationSequence::Perform(Sema &S,
// Build the an expression that constructs a temporary.
CurInit = S.BuildCXXConstructExpr(Loc, Step->Type, Constructor,
move_arg(ConstructorArgs));
move_arg(ConstructorArgs),
ConstructorInitRequiresZeroInit);
if (CurInit.isInvalid())
return S.ExprError();
@ -3224,14 +3240,22 @@ InitializationSequence::Perform(Sema &S,
}
case SK_ZeroInitialization: {
if (Kind.getKind() == InitializationKind::IK_Value &&
S.getLangOptions().CPlusPlus &&
!Kind.isImplicitValueInit())
step_iterator NextStep = Step;
++NextStep;
if (NextStep != StepEnd &&
NextStep->Kind == SK_ConstructorInitialization) {
// The need for zero-initialization is recorded directly into
// the call to the object's constructor within the next step.
ConstructorInitRequiresZeroInit = true;
} else if (Kind.getKind() == InitializationKind::IK_Value &&
S.getLangOptions().CPlusPlus &&
!Kind.isImplicitValueInit()) {
CurInit = S.Owned(new (S.Context) CXXZeroInitValueExpr(Step->Type,
Kind.getRange().getBegin(),
Kind.getRange().getEnd()));
else
} else {
CurInit = S.Owned(new (S.Context) ImplicitValueInitExpr(Step->Type));
}
break;
}
}

View File

@ -0,0 +1,25 @@
// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
struct A {
virtual ~A();
};
struct B : A { };
struct C {
int i;
B b;
};
// CHECK: _Z15test_value_initv
void test_value_init() {
// This value initialization requires zero initialization of the 'B'
// subobject followed by a call to its constructor.
// PR5800
// CHECK: store i32 17
// CHECK: call void @llvm.memset.i64
// CHECK: call void @_ZN1BC1Ev(%struct.A* %tmp1)
C c = { 17 } ;
// CHECK: call void @_ZN1CD1Ev(%struct.C* %c)
}

View File

@ -40,17 +40,18 @@ char cv[4] = { 'a', 's', 'd', 'f', 0 }; // expected-error{{excess elements in ar
struct TooFew { int a; char* b; int c; };
TooFew too_few = { 1, "asdf" }; // okay
struct NoDefaultConstructor { // expected-note 3 {{candidate function}}
struct NoDefaultConstructor { // expected-note 3 {{candidate function}} \
// expected-note{{declared here}}
NoDefaultConstructor(int); // expected-note 3 {{candidate function}}
};
struct TooFewError {
struct TooFewError { // expected-error{{implicit default constructor for}}
int a;
NoDefaultConstructor nodef;
NoDefaultConstructor nodef; // expected-note{{member is declared here}}
};
TooFewError too_few_okay = { 1, 1 };
TooFewError too_few_error = { 1 }; // expected-error{{no matching constructor}}
TooFewError too_few_okay2[2] = { 1, 1 };
TooFewError too_few_okay2[2] = { 1, 1 }; // expected-note{{implicit default constructor for 'struct TooFewError' first required here}}
TooFewError too_few_error2[2] = { 1 }; // expected-error{{no matching constructor}}
NoDefaultConstructor too_few_error3[3] = { }; // expected-error {{no matching constructor}}