Improve handling of initialization by constructor, by ensuring that
such initializations properly convert constructor arguments and fill
in default arguments where necessary. This also makes the ownership
model more clear.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81394 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 68dd12e..745e0ab 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1891,11 +1891,17 @@
CXXConstructorDecl *
PerformInitializationByConstructor(QualType ClassType,
- Expr **Args, unsigned NumArgs,
+ MultiExprArg ArgsPtr,
SourceLocation Loc, SourceRange Range,
DeclarationName InitEntity,
- InitializationKind Kind);
+ InitializationKind Kind,
+ ASTOwningVector<&ActionBase::DeleteExpr> &ConvertedArgs);
+ bool CompleteConstructorCall(CXXConstructorDecl *Constructor,
+ MultiExprArg ArgsPtr,
+ SourceLocation Loc,
+ ASTOwningVector<&ActionBase::DeleteExpr> &ConvertedArgs);
+
/// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's.
virtual OwningExprResult ActOnCXXNamedCast(SourceLocation OpLoc,
tok::TokenKind Kind,
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d49ba89..9bd89ef 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -3278,7 +3278,6 @@
}
// C++ [dcl.init]p9:
- //
// If no initializer is specified for an object, and the object
// is of (possibly cv-qualified) non-POD class type (or array
// thereof), the object shall be default-initialized; if the
@@ -3290,28 +3289,26 @@
InitType = Array->getElementType();
if ((!Var->hasExternalStorage() && !Var->isExternC(Context)) &&
InitType->isRecordType() && !InitType->isDependentType()) {
- CXXRecordDecl *RD =
- cast<CXXRecordDecl>(InitType->getAs<RecordType>()->getDecl());
- CXXConstructorDecl *Constructor = 0;
if (!RequireCompleteType(Var->getLocation(), InitType,
- diag::err_invalid_incomplete_type_use))
- Constructor
- = PerformInitializationByConstructor(InitType, 0, 0,
+ diag::err_invalid_incomplete_type_use)) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
+ CXXConstructorDecl *Constructor
+ = PerformInitializationByConstructor(InitType,
+ MultiExprArg(*this, 0, 0),
Var->getLocation(),
SourceRange(Var->getLocation(),
Var->getLocation()),
Var->getDeclName(),
- IK_Default);
- if (!Constructor)
- Var->setInvalidDecl();
- else {
- if (!RD->hasTrivialConstructor() || !RD->hasTrivialDestructor()) {
- if (InitializeVarWithConstructor(Var, Constructor, InitType,
- MultiExprArg(*this)))
- Var->setInvalidDecl();
- }
-
- FinalizeVarWithDestructor(Var, InitType);
+ IK_Default,
+ ConstructorArgs);
+
+ if (!Constructor ||
+ InitializeVarWithConstructor(Var, Constructor, InitType,
+ move_arg(ConstructorArgs)))
+ Var->setInvalidDecl();
+ else
+ FinalizeVarWithDestructor(Var, InitType);
}
}
}
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 64dc41e..8fd3a70 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -781,10 +781,24 @@
if (FieldType->isDependentType()) {
// Can't check init for dependent type.
} else if (FieldType->getAs<RecordType>()) {
- if (!HasDependentArg)
- C = PerformInitializationByConstructor(
- FieldType, (Expr **)Args, NumArgs, IdLoc,
- SourceRange(IdLoc, RParenLoc), Member->getDeclName(), IK_Direct);
+ if (!HasDependentArg) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
+ C = PerformInitializationByConstructor(FieldType,
+ MultiExprArg(*this,
+ (void**)Args,
+ NumArgs),
+ IdLoc,
+ SourceRange(IdLoc, RParenLoc),
+ Member->getDeclName(), IK_Direct,
+ ConstructorArgs);
+
+ if (C) {
+ // Take over the constructor arguments as our own.
+ NumArgs = ConstructorArgs.size();
+ Args = (Expr **)ConstructorArgs.take();
+ }
+ }
} else if (NumArgs != 1 && NumArgs != 0) {
return Diag(IdLoc, diag::err_mem_initializer_mismatch)
<< Member->getDeclName() << SourceRange(IdLoc, RParenLoc);
@@ -884,9 +898,19 @@
if (!BaseType->isDependentType() && !HasDependentArg) {
DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(
Context.getCanonicalType(BaseType));
- C = PerformInitializationByConstructor(BaseType, (Expr **)Args, NumArgs,
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
+ C = PerformInitializationByConstructor(BaseType,
+ MultiExprArg(*this,
+ (void**)Args, NumArgs),
IdLoc, SourceRange(IdLoc, RParenLoc),
- Name, IK_Direct);
+ Name, IK_Direct,
+ ConstructorArgs);
+ if (C) {
+ // Take over the constructor arguments as our own.
+ NumArgs = ConstructorArgs.size();
+ Args = (Expr **)ConstructorArgs.take();
+ }
}
return new (Context) CXXBaseOrMemberInitializer(BaseType, (Expr **)Args,
@@ -2812,19 +2836,22 @@
MultiExprArg ExprArgs) {
bool Elidable = false;
- // [class.copy]p15:
- // Whenever a temporary class object is copied using a copy constructor, and
- // this object and the copy have the same cv-unqualified type, an
- // implementation is permitted to treat the original and the copy as two
- // different ways of referring to the same object and not perform a copy at
- //all, even if the class copy constructor or destructor have side effects.
+ // C++ [class.copy]p15:
+ // Whenever a temporary class object is copied using a copy constructor, and
+ // this object and the copy have the same cv-unqualified type, an
+ // implementation is permitted to treat the original and the copy as two
+ // different ways of referring to the same object and not perform a copy at
+ // all, even if the class copy constructor or destructor have side effects.
// FIXME: Is this enough?
- if (Constructor->isCopyConstructor(Context) && ExprArgs.size() == 1) {
+ if (Constructor->isCopyConstructor(Context)) {
Expr *E = ((Expr **)ExprArgs.get())[0];
while (CXXBindTemporaryExpr *BE = dyn_cast<CXXBindTemporaryExpr>(E))
E = BE->getSubExpr();
-
+ if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E))
+ if (ICE->getCastKind() == CastExpr::CK_NoOp)
+ E = ICE->getSubExpr();
+
if (isa<CallExpr>(E) || isa<CXXTemporaryObjectExpr>(E))
Elidable = true;
}
@@ -2833,60 +2860,6 @@
Elidable, move(ExprArgs));
}
-static bool
-CheckConstructArgumentTypes(Sema &SemaRef, SourceLocation ConstructLoc,
- CXXConstructExpr *E) {
- CXXConstructorDecl *Ctor = E->getConstructor();
- const FunctionProtoType *Proto = Ctor->getType()->getAsFunctionProtoType();
-
- unsigned NumArgs = E->getNumArgs();
- unsigned NumArgsInProto = Proto->getNumArgs();
- unsigned NumRequiredArgs = Ctor->getMinRequiredArguments();
-
- for (unsigned i = 0; i != NumArgsInProto; ++i) {
- QualType ProtoArgType = Proto->getArgType(i);
-
- Expr *Arg;
-
- if (i < NumRequiredArgs) {
- Arg = E->getArg(i);
-
- // Pass the argument.
- // FIXME: Do this.
- } else {
- // Build a default argument.
- ParmVarDecl *Param = Ctor->getParamDecl(i);
-
- Sema::OwningExprResult ArgExpr =
- SemaRef.BuildCXXDefaultArgExpr(ConstructLoc, Ctor, Param);
- if (ArgExpr.isInvalid())
- return true;
-
- Arg = ArgExpr.takeAs<Expr>();
- }
-
- E->setArg(i, Arg);
- }
-
- // If this is a variadic call, handle args passed through "...".
- if (Proto->isVariadic()) {
- bool Invalid = false;
-
- // Promote the arguments (C99 6.5.2.2p7).
- for (unsigned i = NumArgsInProto; i != NumArgs; i++) {
- Expr *Arg = E->getArg(i);
- Invalid |=
- SemaRef.DefaultVariadicArgumentPromotion(Arg,
- Sema::VariadicConstructor);
- E->setArg(i, Arg);
- }
-
- return Invalid;
- }
-
- return false;
-}
-
/// BuildCXXConstructExpr - Creates a complete call to a constructor,
/// including handling of its default argument expressions.
Sema::OwningExprResult
@@ -2896,18 +2869,8 @@
unsigned NumExprs = ExprArgs.size();
Expr **Exprs = (Expr **)ExprArgs.release();
- ExprOwningPtr<CXXConstructExpr> Temp(this,
- CXXConstructExpr::Create(Context,
- DeclInitType,
- Constructor,
- Elidable,
- Exprs,
- NumExprs));
-
- if (CheckConstructArgumentTypes(*this, ConstructLoc, Temp.get()))
- return ExprError();
-
- return move(Temp);
+ return Owned(CXXConstructExpr::Create(Context, DeclInitType, Constructor,
+ Elidable, Exprs, NumExprs));
}
Sema::OwningExprResult
@@ -2916,17 +2879,12 @@
SourceLocation TyBeginLoc,
MultiExprArg Args,
SourceLocation RParenLoc) {
- CXXTemporaryObjectExpr *E
- = new (Context) CXXTemporaryObjectExpr(Context, Constructor, Ty, TyBeginLoc,
- (Expr **)Args.get(),
- Args.size(), RParenLoc);
+ unsigned NumExprs = Args.size();
+ Expr **Exprs = (Expr **)Args.release();
- ExprOwningPtr<CXXTemporaryObjectExpr> Temp(this, E);
-
- if (CheckConstructArgumentTypes(*this, TyBeginLoc, Temp.get()))
- return ExprError();
-
- return move(Temp);
+ return Owned(new (Context) CXXTemporaryObjectExpr(Context, Constructor, Ty,
+ TyBeginLoc, Exprs,
+ NumExprs, RParenLoc));
}
@@ -3025,20 +2983,23 @@
}
if (VDecl->getType()->isRecordType()) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
CXXConstructorDecl *Constructor
= PerformInitializationByConstructor(DeclInitType,
- (Expr **)Exprs.get(), NumExprs,
+ move(Exprs),
VDecl->getLocation(),
SourceRange(VDecl->getLocation(),
RParenLoc),
VDecl->getDeclName(),
- IK_Direct);
+ IK_Direct,
+ ConstructorArgs);
if (!Constructor)
RealDecl->setInvalidDecl();
else {
VDecl->setCXXDirectInitializer(true);
if (InitializeVarWithConstructor(VDecl, Constructor, DeclInitType,
- move(Exprs)))
+ move_arg(ConstructorArgs)))
RealDecl->setInvalidDecl();
FinalizeVarWithDestructor(VDecl, DeclInitType);
}
@@ -3061,31 +3022,41 @@
/*DirectInit=*/true);
}
-/// PerformInitializationByConstructor - Perform initialization by
-/// constructor (C++ [dcl.init]p14), which may occur as part of
-/// direct-initialization or copy-initialization. We are initializing
-/// an object of type @p ClassType with the given arguments @p
-/// Args. @p Loc is the location in the source code where the
-/// initializer occurs (e.g., a declaration, member initializer,
-/// functional cast, etc.) while @p Range covers the whole
-/// initialization. @p InitEntity is the entity being initialized,
-/// which may by the name of a declaration or a type. @p Kind is the
-/// kind of initialization we're performing, which affects whether
-/// explicit constructors will be considered. When successful, returns
-/// the constructor that will be used to perform the initialization;
-/// when the initialization fails, emits a diagnostic and returns
-/// null.
+/// \brief Perform initialization by constructor (C++ [dcl.init]p14), which
+/// may occur as part of direct-initialization or copy-initialization.
+///
+/// \param ClassType the type of the object being initialized, which must have
+/// class type.
+///
+/// \param ArgsPtr the arguments provided to initialize the object
+///
+/// \param Loc the source location where the initialization occurs
+///
+/// \param Range the source range that covers the entire initialization
+///
+/// \param InitEntity the name of the entity being initialized, if known
+///
+/// \param Kind the type of initialization being performed
+///
+/// \param ConvertedArgs a vector that will be filled in with the
+/// appropriately-converted arguments to the constructor (if initialization
+/// succeeded).
+///
+/// \returns the constructor used to initialize the object, if successful.
+/// Otherwise, emits a diagnostic and returns NULL.
CXXConstructorDecl *
Sema::PerformInitializationByConstructor(QualType ClassType,
- Expr **Args, unsigned NumArgs,
+ MultiExprArg ArgsPtr,
SourceLocation Loc, SourceRange Range,
DeclarationName InitEntity,
- InitializationKind Kind) {
+ InitializationKind Kind,
+ ASTOwningVector<&ActionBase::DeleteExpr> &ConvertedArgs) {
const RecordType *ClassRec = ClassType->getAs<RecordType>();
assert(ClassRec && "Can only initialize a class type here");
-
+ Expr **Args = (Expr **)ArgsPtr.get();
+ unsigned NumArgs = ArgsPtr.size();
+
// C++ [dcl.init]p14:
- //
// If the initialization is direct-initialization, or if it is
// copy-initialization where the cv-unqualified version of the
// source type is the same class as, or a derived class of, the
@@ -3133,8 +3104,9 @@
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, Loc, Best)) {
case OR_Success:
- // We found a constructor. Return it.
- return cast<CXXConstructorDecl>(Best->Function);
+ // We found a constructor. Break out so that we can convert the arguments
+ // appropriately.
+ break;
case OR_No_Viable_Function:
if (InitEntity)
@@ -3167,7 +3139,84 @@
return 0;
}
- return 0;
+ // Convert the arguments, fill in default arguments, etc.
+ CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(Best->Function);
+ if (CompleteConstructorCall(Constructor, move(ArgsPtr), Loc, ConvertedArgs))
+ return 0;
+
+ return Constructor;
+}
+
+/// \brief Given a constructor and the set of arguments provided for the
+/// constructor, convert the arguments and add any required default arguments
+/// to form a proper call to this constructor.
+///
+/// \returns true if an error occurred, false otherwise.
+bool
+Sema::CompleteConstructorCall(CXXConstructorDecl *Constructor,
+ MultiExprArg ArgsPtr,
+ SourceLocation Loc,
+ ASTOwningVector<&ActionBase::DeleteExpr> &ConvertedArgs) {
+ // FIXME: This duplicates a lot of code from Sema::ConvertArgumentsForCall.
+ unsigned NumArgs = ArgsPtr.size();
+ Expr **Args = (Expr **)ArgsPtr.get();
+
+ const FunctionProtoType *Proto
+ = Constructor->getType()->getAs<FunctionProtoType>();
+ assert(Proto && "Constructor without a prototype?");
+ unsigned NumArgsInProto = Proto->getNumArgs();
+ unsigned NumArgsToCheck = NumArgs;
+
+ // If too few arguments are available, we'll fill in the rest with defaults.
+ if (NumArgs < NumArgsInProto) {
+ NumArgsToCheck = NumArgsInProto;
+ ConvertedArgs.reserve(NumArgsInProto);
+ } else {
+ ConvertedArgs.reserve(NumArgs);
+ if (NumArgs > NumArgsInProto)
+ NumArgsToCheck = NumArgsInProto;
+ }
+
+ // Convert arguments
+ for (unsigned i = 0; i != NumArgsToCheck; i++) {
+ QualType ProtoArgType = Proto->getArgType(i);
+
+ Expr *Arg;
+ if (i < NumArgs) {
+ Arg = Args[i];
+
+ // Pass the argument.
+ if (PerformCopyInitialization(Arg, ProtoArgType, "passing"))
+ return true;
+
+ Args[i] = 0;
+ } else {
+ ParmVarDecl *Param = Constructor->getParamDecl(i);
+
+ OwningExprResult DefArg = BuildCXXDefaultArgExpr(Loc, Constructor, Param);
+ if (DefArg.isInvalid())
+ return true;
+
+ Arg = DefArg.takeAs<Expr>();
+ }
+
+ ConvertedArgs.push_back(Arg);
+ }
+
+ // If this is a variadic call, handle args passed through "...".
+ if (Proto->isVariadic()) {
+ // Promote the arguments (C99 6.5.2.2p7).
+ for (unsigned i = NumArgsInProto; i != NumArgs; i++) {
+ Expr *Arg = Args[i];
+ if (DefaultVariadicArgumentPromotion(Arg, VariadicConstructor))
+ return true;
+
+ ConvertedArgs.push_back(Arg);
+ Args[i] = 0;
+ }
+ }
+
+ return false;
}
/// CompareReferenceRelationship - Compare the two types T1 and T2 to
@@ -3330,9 +3379,10 @@
return false;
} else {
// Perform the conversion.
- // FIXME: Binding to a subobject of the lvalue is going to require more
- // AST annotation than this.
- ImpCastExprToType(Init, T1, CastExpr::CK_Unknown, /*isLvalue=*/true);
+ CastExpr::CastKind CK = CastExpr::CK_NoOp;
+ if (DerivedToBase)
+ CK = CastExpr::CK_DerivedToBase;
+ ImpCastExprToType(Init, T1, CK, /*isLvalue=*/true);
}
}
@@ -3489,9 +3539,10 @@
ICS->Standard.RRefBinding = isRValRef;
ICS->Standard.CopyConstructor = 0;
} else {
- // FIXME: Binding to a subobject of the rvalue is going to require more
- // AST annotation than this.
- ImpCastExprToType(Init, T1, CastExpr::CK_Unknown, /*isLvalue=*/false);
+ CastExpr::CastKind CK = CastExpr::CK_NoOp;
+ if (DerivedToBase)
+ CK = CastExpr::CK_DerivedToBase;
+ ImpCastExprToType(Init, T1, CK, /*isLvalue=*/false);
}
return false;
}
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 7fc754f..25dc769 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -250,20 +250,23 @@
if (NumExprs > 1 || !Record->hasTrivialConstructor() ||
!Record->hasTrivialDestructor()) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
CXXConstructorDecl *Constructor
- = PerformInitializationByConstructor(Ty, Exprs, NumExprs,
+ = PerformInitializationByConstructor(Ty, move(exprs),
TypeRange.getBegin(),
SourceRange(TypeRange.getBegin(),
RParenLoc),
DeclarationName(),
- IK_Direct);
+ IK_Direct,
+ ConstructorArgs);
if (!Constructor)
return ExprError();
OwningExprResult Result =
BuildCXXTemporaryObjectExpr(Constructor, Ty, TyBeginLoc,
- move(exprs), RParenLoc);
+ move_arg(ConstructorArgs), RParenLoc);
if (Result.isInvalid())
return ExprError();
@@ -439,14 +442,22 @@
// Skip all the checks.
} else if ((RT = AllocType->getAs<RecordType>()) &&
!AllocType->isAggregateType()) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConvertedConstructorArgs(*this);
+
Constructor = PerformInitializationByConstructor(
- AllocType, ConsArgs, NumConsArgs,
+ AllocType, move(ConstructorArgs),
TypeLoc,
SourceRange(TypeLoc, ConstructorRParen),
RT->getDecl()->getDeclName(),
- NumConsArgs != 0 ? IK_Direct : IK_Default);
+ NumConsArgs != 0 ? IK_Direct : IK_Default,
+ ConvertedConstructorArgs);
if (!Constructor)
return ExprError();
+
+ // Take the converted constructor arguments and use them for the new
+ // expression.
+ NumConsArgs = ConvertedConstructorArgs.size();
+ ConsArgs = (Expr **)ConvertedConstructorArgs.take();
} else {
if (!Init) {
// FIXME: Check that no subpart is const.
@@ -1900,10 +1911,15 @@
switch (Kind) {
default: assert(0 && "Unhandled cast kind!");
case CastExpr::CK_ConstructorConversion: {
- DefaultFunctionArrayConversion(From);
-
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
+ if (CompleteConstructorCall(cast<CXXConstructorDecl>(Method),
+ MultiExprArg(*this, (void **)&From, 1),
+ CastLoc, ConstructorArgs))
+ return ExprError();
+
return BuildCXXConstructExpr(CastLoc, Ty, cast<CXXConstructorDecl>(Method),
- MultiExprArg(*this, (void **)&From, 1));
+ move_arg(ConstructorArgs));
}
case CastExpr::CK_UserDefinedConversion: {
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index c1f7716..328609a 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -172,18 +172,23 @@
if (RD->hasTrivialConstructor() && RD->hasTrivialDestructor())
return false;
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
CXXConstructorDecl *Constructor
- = PerformInitializationByConstructor(DeclType, &Init, 1,
- InitLoc, Init->getSourceRange(),
- InitEntity,
- DirectInit? IK_Direct : IK_Copy);
+ = PerformInitializationByConstructor(DeclType,
+ MultiExprArg(*this,
+ (void **)&Init, 1),
+ InitLoc, Init->getSourceRange(),
+ InitEntity,
+ DirectInit? IK_Direct : IK_Copy,
+ ConstructorArgs);
if (!Constructor)
return true;
OwningExprResult InitResult =
BuildCXXConstructExpr(/*FIXME:ConstructLoc*/SourceLocation(),
DeclType, Constructor,
- MultiExprArg(*this, (void**)&Init, 1));
+ move_arg(ConstructorArgs));
if (InitResult.isInvalid())
return true;
@@ -1810,13 +1815,28 @@
// constructor (12.1), then the default constructor for T is
// called (and the initialization is ill-formed if T has no
// accessible default constructor);
- if (ClassDecl->hasUserDeclaredConstructor())
- // FIXME: Eventually, we'll need to put the constructor decl into the
- // AST.
- return PerformInitializationByConstructor(Type, 0, 0, Loc,
- SourceRange(Loc),
- DeclarationName(),
- IK_Direct);
+ if (ClassDecl->hasUserDeclaredConstructor()) {
+ ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(*this);
+
+ CXXConstructorDecl *Constructor
+ = PerformInitializationByConstructor(Type,
+ MultiExprArg(*this, 0, 0),
+ Loc, SourceRange(Loc),
+ DeclarationName(),
+ IK_Direct,
+ ConstructorArgs);
+ if (!Constructor)
+ return true;
+
+ OwningExprResult Init
+ = BuildCXXConstructExpr(Loc, Type, Constructor,
+ move_arg(ConstructorArgs));
+ if (Init.isInvalid())
+ return true;
+
+ // FIXME: Actually perform the value-initialization!
+ return false;
+ }
}
}