2021-11-04 11:50:30 +08:00
|
|
|
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
|
2018-10-08 11:08:39 +08:00
|
|
|
|
2021-11-04 11:50:30 +08:00
|
|
|
namespace std {
|
2018-10-08 11:08:39 +08:00
|
|
|
template <class Promise = void> struct coroutine_handle {
|
|
|
|
coroutine_handle() = default;
|
|
|
|
static coroutine_handle from_address(void *) noexcept;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <> struct coroutine_handle<void> {
|
|
|
|
static coroutine_handle from_address(void *) noexcept;
|
|
|
|
coroutine_handle() = default;
|
|
|
|
template <class PromiseType>
|
|
|
|
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class... Args>
|
|
|
|
struct void_t_imp {
|
|
|
|
using type = void;
|
|
|
|
};
|
|
|
|
template <class... Args>
|
|
|
|
using void_t = typename void_t_imp<Args...>::type;
|
|
|
|
|
|
|
|
template <class T, class = void>
|
|
|
|
struct traits_sfinae_base {};
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
|
|
|
|
using promise_type = typename T::promise_type;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Ret, class... Args>
|
|
|
|
struct coroutine_traits : public traits_sfinae_base<Ret> {};
|
2021-11-04 11:50:30 +08:00
|
|
|
} // namespace std
|
2018-10-08 11:08:39 +08:00
|
|
|
|
|
|
|
struct suspend_never {
|
|
|
|
bool await_ready() noexcept;
|
2021-11-04 11:50:30 +08:00
|
|
|
void await_suspend(std::coroutine_handle<>) noexcept;
|
2018-10-08 11:08:39 +08:00
|
|
|
void await_resume() noexcept;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MoveOnly {
|
2021-03-19 10:32:06 +08:00
|
|
|
MoveOnly() = default;
|
2018-10-08 11:08:39 +08:00
|
|
|
MoveOnly(const MoveOnly&) = delete;
|
2021-03-19 10:32:06 +08:00
|
|
|
MoveOnly(MoveOnly &&) = default;
|
2018-10-08 11:08:39 +08:00
|
|
|
};
|
|
|
|
|
2021-03-24 04:20:07 +08:00
|
|
|
struct NoCopyNoMove {
|
|
|
|
NoCopyNoMove() = default;
|
2021-03-19 10:32:06 +08:00
|
|
|
NoCopyNoMove(const NoCopyNoMove &) = delete;
|
2021-03-24 04:20:07 +08:00
|
|
|
};
|
|
|
|
|
2018-10-08 11:08:39 +08:00
|
|
|
template <typename T>
|
|
|
|
struct task {
|
|
|
|
struct promise_type {
|
|
|
|
auto initial_suspend() { return suspend_never{}; }
|
[Coroutines] Ensure co_await promise.final_suspend() does not throw
Summary:
This patch addresses https://bugs.llvm.org/show_bug.cgi?id=46256
The spec of coroutine requires that the expression co_await promise.final_suspend() shall not be potentially-throwing.
To check this, we recursively look at every call (including Call, MemberCall, OperatorCall and Constructor) in all code
generated by the final suspend, and ensure that the callees are declared with noexcept. We also look at any returned data
type that requires explicit destruction, and check their destructors for noexcept.
This patch does not check declarations with dependent types yet, which will be done in future patches.
Updated all tests to add noexcept to the required functions, and added a dedicated test for this patch.
This patch might start to cause existing codebase fail to compile because most people may not have been strict in tagging
all the related functions noexcept.
Reviewers: lewissbaker, modocache, junparser
Reviewed By: modocache
Subscribers: arphaman, junparser, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D82029
2020-06-16 07:27:41 +08:00
|
|
|
auto final_suspend() noexcept { return suspend_never{}; }
|
2018-10-08 11:08:39 +08:00
|
|
|
auto get_return_object() { return task{}; }
|
|
|
|
static void unhandled_exception() {}
|
2021-03-24 04:20:07 +08:00
|
|
|
void return_value(T &&value) {} // expected-note 4{{passing argument}}
|
2018-10-08 11:08:39 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-03-24 04:20:07 +08:00
|
|
|
task<NoCopyNoMove> local2val() {
|
|
|
|
NoCopyNoMove value;
|
2021-03-19 10:32:06 +08:00
|
|
|
co_return value;
|
2021-03-24 04:20:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
task<NoCopyNoMove &> local2ref() {
|
|
|
|
NoCopyNoMove value;
|
2021-03-19 10:32:06 +08:00
|
|
|
co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
|
2021-03-24 04:20:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2021-03-19 10:32:06 +08:00
|
|
|
co_return value;
|
2021-03-24 04:20:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
|
2018-10-08 11:08:39 +08:00
|
|
|
co_return value;
|
|
|
|
}
|
|
|
|
|
2021-03-24 04:20:07 +08:00
|
|
|
task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
|
2021-03-19 10:32:06 +08:00
|
|
|
co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
|
2021-03-24 04:20:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 generic_task {
|
|
|
|
struct promise_type {
|
|
|
|
auto initial_suspend() { return suspend_never{}; }
|
|
|
|
auto final_suspend() noexcept { return suspend_never{}; }
|
|
|
|
auto get_return_object() { return generic_task{}; }
|
|
|
|
static void unhandled_exception();
|
|
|
|
template <typename U>
|
|
|
|
void return_value(U &&value) {
|
|
|
|
static_assert(is_same<T, U>::value);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
generic_task<MoveOnly> param2template(MoveOnly value) {
|
|
|
|
co_return value; // We should deduce U = MoveOnly.
|
2018-10-08 11:08:39 +08:00
|
|
|
}
|
|
|
|
|
2021-03-24 04:20:07 +08:00
|
|
|
generic_task<NoCopyNoMove &> lvalue2template(NoCopyNoMove &value) {
|
|
|
|
co_return value; // We should deduce U = NoCopyNoMove&.
|
|
|
|
}
|