Commit c452ce03 authored by Shai Szulanski's avatar Shai Szulanski Committed by Facebook GitHub Bot

Make task -> semiFuture -> task preserve void type

Summary:
Task -> semi has to lift void to unit because semifutures don't support void.
Semi -> task (via co_await) does not drop unit. It probably should because roundtrip properties are nice, but on the off chance someone actually wants to pass a unit around that would become impossible.
But for the result type the Try<void> is already implicitly convertible to Try<Unit>, as those types aren't meaningfully different (and not at all in this context where empty Try is not possible). So we can return Try<void> from co_awaitTry instead of Try<Unit>, and then generic code that wants `co_yield co_result(co_await co_awaitTry(SemiFuture<T|Unit>))` from a `Task<T|void>` will work.

Reviewed By: andriigrynenko

Differential Revision: D23004090

fbshipit-source-id: 5254e2930ac02952abd92fd2856c2a7d3e952e20
parent 15dd0f0c
......@@ -50,6 +50,12 @@ Try<T>::Try(typename std::enable_if<
}
}
Try<void>::Try(const Try<Unit>& t) noexcept : hasValue_(!t.hasException()) {
if (t.hasException()) {
new (&e_) exception_wrapper(t.exception());
}
}
template <class T>
Try<T>& Try<T>::operator=(Try<T>&& t) noexcept(
std::is_nothrow_move_constructible<T>::value) {
......
......@@ -399,6 +399,9 @@ class Try<void> {
explicit Try(exception_wrapper e) noexcept
: hasValue_(false), e_(std::move(e)) {}
/// Implicit conversion from Try<Unit> to Try<void>
/* implicit */ inline Try(const Try<Unit>& t) noexcept;
// Copy assigner
inline Try& operator=(const Try<void>& t) noexcept;
......
......@@ -177,6 +177,12 @@ class TaskPromise : public TaskPromiseBase {
if constexpr (std::is_same_v<remove_cvref_t<U>, Try<StorageType>>) {
DCHECK(value.hasValue() || value.hasException());
result_ = static_cast<U&&>(value);
} else if constexpr (
std::is_same_v<remove_cvref_t<U>, Try<void>> &&
std::is_same_v<remove_cvref_t<T>, Unit>) {
// special-case to make task -> semifuture -> task preserve void type
DCHECK(value.hasValue() || value.hasException());
result_ = static_cast<Try<Unit>>(static_cast<U&&>(value));
} else {
static_assert(
std::is_convertible<U&&, StorageType>::value,
......@@ -234,6 +240,10 @@ class TaskPromise<void> : public TaskPromiseBase {
result_ = std::move(result.result());
return final_suspend();
}
auto yield_value(co_result<Unit>&& result) {
result_ = std::move(result.result());
return final_suspend();
}
private:
Try<void> result_;
......
......@@ -349,6 +349,26 @@ TEST_F(TaskTest, FutureTailCall) {
})));
}
TEST_F(TaskTest, FutureRoundtrip) {
auto task = []() -> folly::coro::Task<void> { co_return; }();
auto semi = std::move(task).semi();
task = [&]() -> folly::coro::Task<void> {
co_yield folly::coro::co_result(
co_await folly::coro::co_awaitTry(std::move(semi)));
}();
folly::coro::blockingWait(std::move(task));
task = []() -> folly::coro::Task<void> {
co_yield folly::coro::co_error(std::runtime_error(""));
}();
semi = std::move(task).semi();
task = [&]() -> folly::coro::Task<void> {
co_yield folly::coro::co_result(
co_await folly::coro::co_awaitTry(std::move(semi)));
}();
EXPECT_THROW(folly::coro::blockingWait(std::move(task)), std::runtime_error);
}
// NOTE: This function is unused.
// We just want to make sure this compiles without errors or warnings.
folly::coro::Task<void>
......
......@@ -2617,8 +2617,8 @@ class FutureAwaitable {
return std::move(result_).value();
}
Try<T> await_resume_try() {
return std::move(result_);
Try<drop_unit_t<T>> await_resume_try() {
return static_cast<Try<drop_unit_t<T>>>(std::move(result_));
}
FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(
......
......@@ -371,6 +371,11 @@ TEST(Try, nothrow) {
EXPECT_TRUE((std::is_nothrow_constructible<Try<Unit>, Try<void>&&>::value));
EXPECT_TRUE(
(std::is_nothrow_constructible<Try<Unit>, Try<void> const&>::value));
// conversion ctor - unit to void
EXPECT_TRUE((std::is_nothrow_constructible<Try<void>, Try<Unit>&&>::value));
EXPECT_TRUE(
(std::is_nothrow_constructible<Try<void>, Try<Unit> const&>::value));
}
TEST(Try, MoveDereference) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment