diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -866,20 +866,13 @@ // Move the return value if we can if (E) { - auto NRVOCandidate = this->getCopyElisionCandidate(E->getType(), E, CES_AsIfByStdMove); - if (NRVOCandidate) { - InitializedEntity Entity = - InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate); - ExprResult MoveResult = this->PerformMoveOrCopyInitialization( - Entity, NRVOCandidate, E->getType(), E); - if (MoveResult.get()) - E = MoveResult.get(); - } + VarDecl *NRVOCandidate = + getCopyElisionCandidate(E->getType(), E, CES_Default); + if (NRVOCandidate && !NRVOCandidate->getType()->isLValueReferenceType()) + E = ImplicitCastExpr::Create(Context, E->getType(), CK_NoOp, E, {}, + VK_XValue); } - // FIXME: If the operand is a reference to a variable that's about to go out - // of scope, we should treat the operand as an xvalue for this overload - // resolution. VarDecl *Promise = FSI->CoroutinePromise; ExprResult PC; if (E && (isa(E) || !E->getType()->isVoidType())) { diff --git a/clang/test/SemaCXX/coroutine-rvo.cpp b/clang/test/SemaCXX/coroutine-rvo.cpp --- a/clang/test/SemaCXX/coroutine-rvo.cpp +++ b/clang/test/SemaCXX/coroutine-rvo.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -stdlib=libc++ -std=c++1z -fcoroutines-ts -fsyntax-only +// RUN: %clang_cc1 -verify -std=c++17 -fcoroutines-ts -fsyntax-only %s namespace std::experimental { template struct coroutine_handle { @@ -38,11 +38,17 @@ void await_resume() noexcept; }; +struct Default {}; + struct MoveOnly { - MoveOnly() {}; + MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; - MoveOnly(MoveOnly&&) noexcept {}; - ~MoveOnly() {}; + MoveOnly(MoveOnly&&) = default; +}; + +struct NoCopyNoMove { + NoCopyNoMove() = default; + NoCopyNoMove(const NoCopyNoMove&) = delete; }; template @@ -52,18 +58,31 @@ auto final_suspend() { return suspend_never{}; } auto get_return_object() { return task{}; } static void unhandled_exception() {} - void return_value(T&& value) {} + void return_value(T&& value) {} // expected-note 2{{passing argument}} }; }; -task f() { - MoveOnly value; +task local2val() { + NoCopyNoMove value; co_return value; } -int main() { - f(); - return 0; +task param2val(MoveOnly value) { + co_return value; } -// expected-no-diagnostics +task lvalue2val(Default& value) { + co_return value; // expected-error{{rvalue reference to type 'Default' cannot bind to lvalue of type 'Default'}} +} + +task rvalue2val(NoCopyNoMove&& value) { + co_return value; +} + +task lvalue2ref(NoCopyNoMove& value) { + co_return value; +} + +task rvalue2ref(Default&& value) { + co_return value; // expected-error{{non-const lvalue reference to type 'Default' cannot bind to a temporary of type 'Default'}} +}