diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-09-09 23:08:42 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-09-09 23:08:42 +0000 |
commit | 39da0b8145eaec7da7004f9b3645c5c9f4f63b1d (patch) | |
tree | 736478849d6a4c9123a029dc251b7b242111088d /lib/Sema/SemaDeclCXX.cpp | |
parent | 84ec96c8a810df59de4052368beab29f13456707 (diff) |
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
Diffstat (limited to 'lib/Sema/SemaDeclCXX.cpp')
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 291 |
1 files changed, 171 insertions, 120 deletions
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 64dc41e31a..8fd3a70116 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -781,10 +781,24 @@ Sema::BuildMemberInitializer(FieldDecl *Member, Expr **Args, 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 @@ Sema::BuildBaseInitializer(QualType BaseType, Expr **Args, 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 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, 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 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, 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 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, 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 @@ Sema::BuildCXXTemporaryObjectExpr(CXXConstructorDecl *Constructor, SourceLocation TyBeginLoc, MultiExprArg Args, SourceLocation RParenLoc) { - CXXTemporaryObjectExpr *E - = new (Context) CXXTemporaryObjectExpr(Context, Constructor, Ty, TyBeginLoc, - (Expr **)Args.get(), - Args.size(), RParenLoc); - - ExprOwningPtr<CXXTemporaryObjectExpr> Temp(this, E); + unsigned NumExprs = Args.size(); + Expr **Exprs = (Expr **)Args.release(); - 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 @@ void Sema::AddCXXDirectInitializerToDecl(DeclPtrTy Dcl, } 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 @@ void Sema::AddCXXDirectInitializerToDecl(DeclPtrTy Dcl, /*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 @@ Sema::PerformInitializationByConstructor(QualType ClassType, 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 @@ Sema::PerformInitializationByConstructor(QualType ClassType, 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 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, 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 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, 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; } |