aaronpuchert updated this revision to Diff 225149.
aaronpuchert added a comment.
Apply clang-format.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D68845/new/
https://reviews.llvm.org/D68845
Files:
clang/lib/Sema/SemaCoroutine.cpp
clang/test/SemaCXX/coroutine-rvo.cpp
Index: clang/test/SemaCXX/coroutine-rvo.cpp
===================================================================
--- clang/test/SemaCXX/coroutine-rvo.cpp
+++ 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 <class Promise = void> struct coroutine_handle {
@@ -39,10 +39,14 @@
};
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 <typename T>
@@ -52,18 +56,93 @@
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<MoveOnly> f() {
- MoveOnly value;
+task<NoCopyNoMove> local2val() {
+ NoCopyNoMove value;
+ co_return value;
+}
+
+task<NoCopyNoMove &> local2ref() {
+ NoCopyNoMove value;
+ co_return value;
+}
+
+// We need the move constructor for construction of the coroutine.
+task<MoveOnly> param2val(MoveOnly value) {
+ co_return value;
+}
+
+task<NoCopyNoMove> lvalue2val(NoCopyNoMove &value) {
+ co_return value; // expected-error{{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
+}
+
+task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
+ co_return value;
+}
+
+task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
co_return value;
}
-int main() {
- f();
- return 0;
+task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
+ co_return value;
+}
+
+struct To {
+ operator MoveOnly() &&;
+};
+task<MoveOnly> conversion_operator() {
+ To t;
+ co_return t;
+}
+
+struct Construct {
+ Construct(MoveOnly);
+};
+task<Construct> converting_constructor() {
+ MoveOnly w;
+ co_return w;
+}
+
+struct Derived : MoveOnly {};
+task<MoveOnly> derived2base() {
+ Derived result;
+ co_return result;
+}
+
+struct RetThis {
+ task<RetThis> foo() && {
+ co_return *this; // expected-error{{rvalue reference to type 'RetThis' cannot bind to lvalue of type 'RetThis'}}
+ }
+};
+
+template <typename, typename>
+struct is_same { static constexpr bool value = false; };
+
+template <typename T>
+struct is_same<T, T> { static constexpr bool value = true; };
+
+template <typename T>
+struct template_return_task {
+ struct promise_type {
+ auto initial_suspend() { return suspend_never{}; }
+ auto final_suspend() { return suspend_never{}; }
+ auto get_return_object() { return template_return_task{}; }
+ static void unhandled_exception();
+ template <typename U>
+ void return_value(U &&value) {
+ static_assert(is_same<T, U>::value);
+ }
+ };
+};
+
+template_return_task<MoveOnly> param2template(MoveOnly value) {
+ co_return value; // We should deduce U = MoveOnly.
}
-// expected-no-diagnostics
+template_return_task<NoCopyNoMove &> lvalue2template(NoCopyNoMove &value) {
+ co_return value; // We should deduce U = NoCopyNoMove&.
+}
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -851,6 +851,72 @@
return BuildCoreturnStmt(Loc, E);
}
+// [class.copy.elision]p3: If the expression in a [...] co_return statement
+// is a (possibly parenthesized) id-expression that names an implicitly
+// movable entity declared in the body or parameter-declaration-clause of
+// the innermost enclosing function or lambda-expression, [...] overload
+// resolution to select [...] the return_value overload to call is first
+// performed as if the expression or operand were an rvalue. If the first
+// overload resolution fails or was not performed, overload resolution is
+// performed again, considering the expression or operand as an lvalue.
+static bool shouldMoveReturnExpr(Sema &S, RecordDecl *PromiseTypeDecl,
+ Expr *E) {
+ // Check if we're allowed to implicitly move.
+ VarDecl *ImplicitMoveCandidate =
+ S.getCopyElisionCandidate(E->getType(), E, Sema::CES_Default);
+ if (!ImplicitMoveCandidate)
+ return false;
+ if (ImplicitMoveCandidate->getType()->isLValueReferenceType())
+ return false;
+
+ ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, E->getType(), CK_NoOp, E,
+ VK_XValue);
+
+ // Lookup return_value methods in the promise type, and check if any accepts
+ // the moved expression. If it does, we do the move.
+ DeclarationNameInfo NameInfo(&S.PP.getIdentifierTable().get("return_value"),
+ SourceLocation{});
+ LookupResult LR(S, NameInfo, Sema::LookupMemberName);
+ CXXScopeSpec SS;
+ S.LookupQualifiedName(LR, PromiseTypeDecl, SS);
+ for (NamedDecl *D : LR) {
+ if (const auto *USD = dyn_cast<UsingShadowDecl>(D))
+ D = USD->getTargetDecl();
+ if (const auto *CMD = dyn_cast<CXXMethodDecl>(D)) {
+ if (CMD->isDeleted())
+ continue;
+ if (CMD->getNumParams() < 1)
+ continue;
+ for (unsigned param = 1; param < CMD->getNumParams(); ++param)
+ if (!CMD->getParamDecl(param)->hasDefaultArg())
+ continue;
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(
+ S.Context, CMD->getParamDecl(0));
+ if (S.CanPerformCopyInitialization(Entity, &AsRvalue))
+ return true;
+ } else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) {
+ TemplateDeductionInfo Info(SourceLocation{});
+ FunctionDecl *Specialization = nullptr;
+ Sema::TemplateDeductionResult Result = S.DeduceTemplateArguments(
+ FTD, {}, {&AsRvalue}, Specialization, Info, false,
+ [&](ArrayRef<QualType> ParamTypes) {
+ QualType ParamType = ParamTypes[0];
+ if (!ParamType->isDependentType()) {
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(
+ S.Context, ParamType, false);
+ if (!S.CanPerformCopyInitialization(Entity, &AsRvalue))
+ return true;
+ }
+ return false;
+ });
+ if (Result == Sema::TDK_Success)
+ return true;
+ }
+ // TODO: handle VarDecls. They can be function pointers or function objects.
+ }
+ return false;
+}
+
StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
bool IsImplicit) {
auto *FSI = checkCoroutineContext(*this, Loc, "co_return", IsImplicit);
@@ -864,26 +930,15 @@
E = R.get();
}
- // 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();
- }
- }
-
- // 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<InitListExpr>(E) || !E->getType()->isVoidType())) {
- PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
+ Expr *Arg = E;
+ if (const RecordType *PromiseT = Promise->getType()->getAs<RecordType>())
+ if (shouldMoveReturnExpr(*this, PromiseT->getDecl(), E))
+ Arg = ImplicitCastExpr::Create(Context, E->getType(), CK_NoOp, E,
+ nullptr, VK_XValue);
+ PC = buildPromiseCall(*this, Promise, Loc, "return_value", Arg);
} else {
E = MakeFullDiscardedValueExpr(E).get();
PC = buildPromiseCall(*this, Promise, Loc, "return_void", None);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits