diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/DeclCXX.cpp | 35 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 96 |
3 files changed, 113 insertions, 26 deletions
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 4548273333..334ee795f3 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1116,25 +1116,40 @@ bool CXXConstructorDecl::isDefaultConstructor() const { bool CXXConstructorDecl::isCopyConstructor(unsigned &TypeQuals) const { + return isCopyOrMoveConstructor(TypeQuals) && + getParamDecl(0)->getType()->isLValueReferenceType(); +} + +bool CXXConstructorDecl::isMoveConstructor(unsigned &TypeQuals) const { + return isCopyOrMoveConstructor(TypeQuals) && + getParamDecl(0)->getType()->isRValueReferenceType(); +} + +/// \brief Determine whether this is a copy or move constructor. +bool CXXConstructorDecl::isCopyOrMoveConstructor(unsigned &TypeQuals) const { // C++ [class.copy]p2: // A non-template constructor for class X is a copy constructor // if its first parameter is of type X&, const X&, volatile X& or // const volatile X&, and either there are no other parameters // or else all other parameters have default arguments (8.3.6). + // C++0x [class.copy]p3: + // A non-template constructor for class X is a move constructor if its + // first parameter is of type X&&, const X&&, volatile X&&, or + // const volatile X&&, and either there are no other parameters or else + // all other parameters have default arguments. if ((getNumParams() < 1) || (getNumParams() > 1 && !getParamDecl(1)->hasDefaultArg()) || (getPrimaryTemplate() != 0) || (getDescribedFunctionTemplate() != 0)) return false; - + const ParmVarDecl *Param = getParamDecl(0); - - // Do we have a reference type? Rvalue references don't count. - const LValueReferenceType *ParamRefType = - Param->getType()->getAs<LValueReferenceType>(); + + // Do we have a reference type? + const ReferenceType *ParamRefType = Param->getType()->getAs<ReferenceType>(); if (!ParamRefType) return false; - + // Is it a reference to our class type? ASTContext &Context = getASTContext(); @@ -1144,12 +1159,12 @@ CXXConstructorDecl::isCopyConstructor(unsigned &TypeQuals) const { = Context.getCanonicalType(Context.getTagDeclType(getParent())); if (PointeeType.getUnqualifiedType() != ClassTy) return false; - + // FIXME: other qualifiers? - - // We have a copy constructor. + + // We have a copy or move constructor. TypeQuals = PointeeType.getCVRQualifiers(); - return true; + return true; } bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const { diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 9e8a15739f..00a13b54cf 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -3363,20 +3363,20 @@ static ExprResult CopyObject(Sema &S, if (S.RequireCompleteType(Loc, T, S.PDiag(diag::err_temp_copy_incomplete))) return move(CurInit); - // Perform overload resolution using the class's copy constructors. + // Perform overload resolution using the class's copy/move constructors. DeclContext::lookup_iterator Con, ConEnd; OverloadCandidateSet CandidateSet(Loc); for (llvm::tie(Con, ConEnd) = S.LookupConstructors(Class); Con != ConEnd; ++Con) { - // Only consider copy constructors and constructor templates. Per + // Only consider copy/move constructors and constructor templates. Per // C++0x [dcl.init]p16, second bullet to class types, this // initialization is direct-initialization. CXXConstructorDecl *Constructor = 0; if ((Constructor = dyn_cast<CXXConstructorDecl>(*Con))) { - // Handle copy constructors, only. + // Handle copy/moveconstructors, only. if (!Constructor || Constructor->isInvalidDecl() || - !Constructor->isCopyConstructor() || + !Constructor->isCopyOrMoveConstructor() || !Constructor->isConvertingConstructor(/*AllowExplicit=*/true)) continue; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 7010d48d1c..015dcbb8c9 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1137,6 +1137,86 @@ const VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, return 0; } +/// \brief Perform the initialization of a return value. +/// +/// This routine implements C++0x [class.copy]p33, which attempts to treat +/// returned lvalues as rvalues in certain cases (to prefer move construction), +/// then falls back to treating them as lvalues if that failed. +static ExprResult initializeReturnValue(Sema &S, + const VarDecl *NRVOCandidate, + SourceLocation ReturnLoc, + QualType ResultType, + Expr *RetValExp) { + // C++0x [class.copy]p33: + // When the criteria for elision of a copy operation are met or would + // be met save for the fact that the source object is a function + // parameter, and the object to be copied is designated by an lvalue, + // overload resolution to select the constructor for the copy is first + // performed as if the object were designated by an rvalue. + InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, + ResultType, + NRVOCandidate != 0); + + ExprResult Res = ExprError(); + if (NRVOCandidate || S.getCopyElisionCandidate(ResultType, RetValExp, true)) { + ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, + RetValExp->getType(), CK_LValueToRValue, + RetValExp, VK_XValue); + + Expr *InitExpr = &AsRvalue; + InitializationKind Kind + = InitializationKind::CreateCopy(RetValExp->getLocStart(), + RetValExp->getLocStart()); + InitializationSequence Seq(S, Entity, Kind, &InitExpr, 1); + + // [...] If overload resolution fails, or if the type of the first + // parameter of the selected constructor is not an rvalue reference + // to the object’s type (possibly cv-qualified), overload resolution + // is performed again, considering the object as an lvalue. + if (Seq.getKind() != InitializationSequence::FailedSequence) { + for (InitializationSequence::step_iterator Step = Seq.step_begin(), + StepEnd = Seq.step_end(); + Step != StepEnd; ++Step) { + if (Step->Kind + != InitializationSequence::SK_ConstructorInitialization) + continue; + + CXXConstructorDecl *Constructor + = cast<CXXConstructorDecl>(Step->Function.Function); + + const RValueReferenceType *RRefType + = Constructor->getParamDecl(0)->getType() + ->getAs<RValueReferenceType>(); + + // If we don't meet the criteria, break out now. + if (!RRefType || + !S.Context.hasSameUnqualifiedType(RRefType->getPointeeType(), + ResultType)) + break; + + // Promote "AsRvalue" to the heap, since we now need this + // expression node to persist. + RetValExp = ImplicitCastExpr::Create(S.Context, + RetValExp->getType(), + CK_LValueToRValue, + RetValExp, 0, VK_XValue); + + // Complete type-checking the initialization of the return type + // using the constructor we found. + Res = Seq.Perform(S, Entity, Kind, MultiExprArg(&RetValExp, 1)); + } + } + } + + // Either we didn't meet the criteria for treating an lvalue as an rvalue, + // above, or overload resolution failed. Either way, we need to try + // (again) now with the return value expression as written. + if (Res.isInvalid()) + Res = S.PerformCopyInitialization(Entity, SourceLocation(), RetValExp); + + return Res; +} + /// ActOnBlockReturnStmt - Utility routine to figure out block's return type. /// StmtResult @@ -1193,12 +1273,8 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // In C++ the return statement is handled via a copy initialization. // the C version of which boils down to CheckSingleAssignmentConstraints. NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false); - ExprResult Res = PerformCopyInitialization( - InitializedEntity::InitializeResult(ReturnLoc, - FnRetType, - NRVOCandidate != 0), - SourceLocation(), - Owned(RetValExp)); + ExprResult Res = initializeReturnValue(*this, NRVOCandidate, ReturnLoc, + FnRetType, RetValExp); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); @@ -1291,12 +1367,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // In C++ the return statement is handled via a copy initialization. // the C version of which boils down to CheckSingleAssignmentConstraints. NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false); - ExprResult Res = PerformCopyInitialization( - InitializedEntity::InitializeResult(ReturnLoc, - FnRetType, - NRVOCandidate != 0), - SourceLocation(), - Owned(RetValExp)); + ExprResult Res = initializeReturnValue(*this, NRVOCandidate, ReturnLoc, + FnRetType, RetValExp); if (Res.isInvalid()) { // FIXME: Cleanup temporaries here, anyway? return StmtError(); |