diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-01-21 19:38:21 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-01-21 19:38:21 +0000 |
commit | cc15f010672a13b38104a32e3cefc7adc07ffbf7 (patch) | |
tree | 6d229587f576842290d3770ef4aa8518cd7b1abc /lib/Sema/SemaStmt.cpp | |
parent | 4a46c77813af1241139b81a086b539e4d734cb86 (diff) |
Implement the preference for move-construction over copy-construction
when returning an NRVO candidate expression. For example, this
properly picks the move constructor when dealing with code such as
MoveOnlyType f() { MoveOnlyType mot; return mot; }
The previously-XFAIL'd rvalue-references test case now works, and has
been moved into the appropriate paragraph-specific test case.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123992 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaStmt.cpp')
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 96 |
1 files changed, 84 insertions, 12 deletions
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(); |