Index: libcxx/docs/Status/SpaceshipProjects.csv =================================================================== --- libcxx/docs/Status/SpaceshipProjects.csv +++ libcxx/docs/Status/SpaceshipProjects.csv @@ -9,7 +9,7 @@ | `weak_order `_ | `partial_order `_",None,Arthur O'Dwyer,|In Progress| | `[alg.three.way] `_,| `lexicographical_compare_three_way `_,[comparisons.three.way],Christopher Di Bella,|In Progress| -| `[coroutine.handle.compare] `_,| coroutine_handle,[comparisons.three.way],Unassigned,|Not Started| +| `[coroutine.handle.compare] `_,| coroutine_handle,[comparisons.three.way],Unassigned,|Complete| | `[pairs.spec] `_,| `pair `_,[expos.only.func],Kent Ross,|In Progress| | `[syserr.errcat.nonvirtuals] `_,| error_category,[comparisons.three.way],Unassigned,|Not Started| | `[syserr.compare] `_,"| error_code Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -315,6 +315,7 @@ complex.h concepts condition_variable + coroutine csetjmp csignal cstdarg Index: libcxx/include/coroutine =================================================================== --- /dev/null +++ libcxx/include/coroutine @@ -0,0 +1,371 @@ +// -*- C++ -*- +//===----------------------------- coroutine -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_COROUTINE +#define _LIBCPP_COROUTINE + +/** + coroutine synopsis + +namespace std { +inline namespace coroutines_v1 { + +// [coroutine.traits] +template + struct coroutine_traits; +// [coroutine.handle] +template + struct coroutine_handle; +// [coroutine.handle.compare] +constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept; +constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept; +// [coroutine.handle.hash] +template struct hash; +template struct hash>; +// [coroutine.noop] +struct noop_coroutine_promise; +template<> struct coroutine_handle; +using noop_coroutine_handle = coroutine_handle; +noop_coroutine_handle noop_coroutine() noexcept; +// [coroutine.trivial.awaitables] +struct suspend_never; +struct suspend_always; + +} // namespace coroutines_v1 +} // namespace std + + */ + +#include <__debug> +#include <__functional/hash.h> +#include +#include +#include <__config> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_COROUTINES +# if defined(_LIBCPP_WARNING) + _LIBCPP_WARNING(" cannot be used with this compiler") +# else +# warning cannot be used with this compiler +# endif +#endif + +#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +// [coroutine.traits] +// [coroutine.traits.primary] +// The header defines the primary template coroutine_­traits such that +// if ArgTypes is a parameter pack of types and if the qualified-id R::promise_­type +// is valid and denotes a type ([temp.deduct]), then coroutine_­traits +// has the following publicly accessible member: +// +// using promise_type = typename R::promise_type; +// +// Otherwise, coroutine_­traits has no members. +template +struct __coroutine_traits_sfinae {}; + +template +struct __coroutine_traits_sfinae< + _Tp, typename __void_t::type> +{ + using promise_type = typename _Tp::promise_type; +}; + +template +struct coroutine_traits + : public __coroutine_traits_sfinae<_Ret> +{ +}; + +// [coroutine.handle] +template +struct _LIBCPP_TEMPLATE_VIS coroutine_handle; + +template <> +struct _LIBCPP_TEMPLATE_VIS coroutine_handle { +public: + // [coroutine.handle.con], construct/reset + _LIBCPP_HIDE_FROM_ABI + constexpr coroutine_handle() noexcept {} + + _LIBCPP_HIDE_FROM_ABI + constexpr coroutine_handle(nullptr_t) noexcept {} + + _LIBCPP_HIDE_FROM_ABI + coroutine_handle& operator=(nullptr_t) noexcept { + __handle_ = nullptr; + return *this; + } + + // [coroutine.handle.export.import], export/import + _LIBCPP_HIDE_FROM_ABI + constexpr void* address() const noexcept { return __handle_; } + + _LIBCPP_HIDE_FROM_ABI + static constexpr coroutine_handle from_address(void* __addr) noexcept { + coroutine_handle __tmp; + __tmp.__handle_ = __addr; + return __tmp; + } + + // [coroutine.handle.observers], observers + _LIBCPP_HIDE_FROM_ABI + constexpr explicit operator bool() const noexcept { return __handle_; } + + _LIBCPP_HIDE_FROM_ABI + bool done() const { + _LIBCPP_ASSERT(__is_suspended(), "done() can be called only on suspended coroutines"); + return __builtin_coro_done(__handle_); + } + + // [coroutine.handle.resumption], resumption + _LIBCPP_HIDE_FROM_ABI + void operator()() const { resume(); } + + _LIBCPP_HIDE_FROM_ABI + void resume() const { + _LIBCPP_ASSERT(__is_suspended(), "resume() can be called only on suspended coroutines"); + _LIBCPP_ASSERT(!done(), "resume() has undefined behavior when the coroutine is done"); + __builtin_coro_resume(__handle_); + } + + _LIBCPP_HIDE_FROM_ABI + void destroy() const { + _LIBCPP_ASSERT(__is_suspended(), "destroy() can be called only on suspended coroutines"); + __builtin_coro_destroy(__handle_); + } + +private: + bool __is_suspended() const noexcept { + // FIXME actually implement a check for if the coro is suspended. + return __handle_; + } + + void* __handle_ = nullptr; +}; + +// [coroutine.handle.compare] +#if defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) + +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return __x.address() == __y.address(); +} +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return less()(__x.address(), __y.address()); +} +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return __y < __x; +} +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return !(__x > __y); +} +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return !(__x < __y); +} + +#else + +inline _LIBCPP_HIDE_FROM_ABI +constexpr bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return __x.address() == __y.address(); +} +inline _LIBCPP_HIDE_FROM_ABI +constexpr strong_ordering operator<=>(coroutine_handle<> __x, coroutine_handle<> __y) noexcept { + return __x.address() <=> __y.address(); +} + +#endif // defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) + +template +struct _LIBCPP_TEMPLATE_VIS coroutine_handle { +public: + // [coroutine.handle.con], construct/reset + _LIBCPP_HIDE_FROM_ABI + constexpr coroutine_handle() noexcept = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr coroutine_handle(nullptr_t) noexcept {} + + _LIBCPP_HIDE_FROM_ABI + static coroutine_handle from_promise(_Promise& __promise) { + using _RawPromise = typename remove_cv<_Promise>::type; + coroutine_handle __tmp; + __tmp.__handle_ = + __builtin_coro_promise(_VSTD::addressof(const_cast<_RawPromise&>(__promise)), alignof(_Promise), true); + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + coroutine_handle& operator=(nullptr_t) noexcept { + __handle_ = nullptr; + return *this; + } + + // [coroutine.handle.export.import], export/import + _LIBCPP_HIDE_FROM_ABI + constexpr void* address() const noexcept { return __handle_; } + + _LIBCPP_HIDE_FROM_ABI + static constexpr coroutine_handle from_address(void* __addr) noexcept { + coroutine_handle __tmp; + __tmp.__handle_ = __addr; + return __tmp; + } + + // [coroutine.handle.conv], conversion + _LIBCPP_HIDE_FROM_ABI + constexpr operator coroutine_handle<>() const noexcept { + return coroutine_handle<>::from_address(address()); + } + + // [coroutine.handle.observers], observers + _LIBCPP_HIDE_FROM_ABI + constexpr explicit operator bool() const noexcept { return __handle_; } + + _LIBCPP_HIDE_FROM_ABI + bool done() const { + _LIBCPP_ASSERT(__is_suspended(), "done() can be called only on suspended coroutines"); + return __builtin_coro_done(__handle_); + } + + // [coroutine.handle.resumption], resumption + _LIBCPP_HIDE_FROM_ABI + void operator()() const { resume(); } + + _LIBCPP_HIDE_FROM_ABI + void resume() const { + _LIBCPP_ASSERT(__is_suspended(), "resume() can be called only on suspended coroutines"); + _LIBCPP_ASSERT(!done(), "resume() has undefined behavior when the coroutine is done"); + __builtin_coro_resume(__handle_); + } + + _LIBCPP_HIDE_FROM_ABI + void destroy() const { + _LIBCPP_ASSERT(__is_suspended(), "destroy() can be called only on suspended coroutines"); + __builtin_coro_destroy(__handle_); + } + + // [coroutine.handle.promise], promise access + _LIBCPP_HIDE_FROM_ABI + _Promise& promise() const { + return *static_cast<_Promise*>(__builtin_coro_promise(this->__handle_, alignof(_Promise), false)); + } + +private: + bool __is_suspended() const noexcept { + // FIXME actually implement a check for if the coro is suspended. + return __handle_; + } + void* __handle_ = nullptr; +}; + +#if __has_builtin(__builtin_coro_noop) +// [coroutine.noop] +// [coroutine.promise.noop] +struct noop_coroutine_promise {}; + +// [coroutine.handle.noop] +template <> +struct _LIBCPP_TEMPLATE_VIS coroutine_handle { +public: + // [coroutine.handle.noop.conv], conversion + _LIBCPP_HIDE_FROM_ABI + constexpr operator coroutine_handle<>() const noexcept { + return coroutine_handle<>::from_address(address()); + } + + // [coroutine.handle.noop.observers], observers + _LIBCPP_HIDE_FROM_ABI + constexpr explicit operator bool() const noexcept { return true; } + _LIBCPP_HIDE_FROM_ABI + constexpr bool done() const noexcept { return false; } + + // [coroutine.handle.noop.resumption], resumption + _LIBCPP_HIDE_FROM_ABI + constexpr void operator()() const noexcept {} + _LIBCPP_HIDE_FROM_ABI + constexpr void resume() const noexcept {} + _LIBCPP_HIDE_FROM_ABI + constexpr void destroy() const noexcept {} + + // [coroutine.handle.noop.promise], promise access + _LIBCPP_HIDE_FROM_ABI + noop_coroutine_promise& promise() const noexcept { + return *static_cast( + __builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false)); + } + + // [coroutine.handle.noop.address], address + _LIBCPP_HIDE_FROM_ABI + constexpr void* address() const noexcept { return __handle_; } + +private: + _LIBCPP_HIDE_FROM_ABI + friend coroutine_handle noop_coroutine() noexcept; + + _LIBCPP_HIDE_FROM_ABI coroutine_handle() noexcept { + this->__handle_ = __builtin_coro_noop(); + } + + void* __handle_ = nullptr; +}; + +using noop_coroutine_handle = coroutine_handle; + +// [coroutine.noop.coroutine] +inline _LIBCPP_HIDE_FROM_ABI +noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); } + +#endif // __has_builtin(__builtin_coro_noop) + +// [coroutine.trivial.awaitables] +struct suspend_never { + _LIBCPP_HIDE_FROM_ABI + constexpr bool await_ready() const noexcept { return true; } + _LIBCPP_HIDE_FROM_ABI + constexpr void await_suspend(coroutine_handle<>) const noexcept {} + _LIBCPP_HIDE_FROM_ABI + constexpr void await_resume() const noexcept {} +}; + +struct suspend_always { + _LIBCPP_HIDE_FROM_ABI + constexpr bool await_ready() const noexcept { return false; } + _LIBCPP_HIDE_FROM_ABI + constexpr void await_suspend(coroutine_handle<>) const noexcept {} + _LIBCPP_HIDE_FROM_ABI + constexpr void await_resume() const noexcept {} +}; + +template +struct hash > { + using __arg_type = coroutine_handle<_Tp>; + _LIBCPP_HIDE_FROM_ABI + size_t operator()(const __arg_type& __v) const noexcept { return hash()(__v.address()); } +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // __LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +#endif // _LIBCPP_COROUTINE Index: libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp =================================================================== --- libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp +++ libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp @@ -17,7 +17,7 @@ #include #include "test_macros.h" -#include "coroutine_types.h" +#include "coroutine_experimental_types.h" using namespace std::experimental; Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.pass.cpp @@ -0,0 +1,62 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// namespace std { +// template struct hash>; +// } + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +void do_test(uintptr_t LHSVal, uintptr_t RHSVal) { + const size_t ExpectLHS = std::hash{}(reinterpret_cast(LHSVal)); + const size_t ExpectRHS = std::hash{}(reinterpret_cast(RHSVal)); + const C LHS = C::from_address(reinterpret_cast(LHSVal)); + const C RHS = C::from_address(reinterpret_cast(RHSVal)); + const std::hash h; + // FIXME: libc++'s implementation hash's the result of LHS.address(), so we + // expect that value. However this is not required. + assert(h(LHS) == ExpectLHS); + assert(h(RHS) == ExpectRHS); + assert((h(LHS) == h(RHS)) == (LHSVal == RHSVal)); + { + ASSERT_SAME_TYPE(decltype(h(LHS)), size_t); + ASSERT_NOEXCEPT(std::hash{}(LHS)); + } +} + +int main(int, char**) +{ + std::pair const TestCases[] = { + {0, 0}, + {0, 8}, + {8, 8}, + {8, 16} + }; + for (auto& TC : TestCases) { + do_test>(TC.first, TC.second); + do_test>(TC.first, TC.second); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp @@ -0,0 +1,57 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// constexpr explicit operator bool() const noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void do_test() { + static_assert(std::is_nothrow_constructible::value, ""); + static_assert(!std::is_convertible::value, ""); + { + constexpr C c; ((void)c); + static_assert(bool(c) == false, ""); + } + { // null case + const C c = {}; ((void)c); + ASSERT_NOEXCEPT(bool(c)); + if (c) + assert(false); + else + assert(true); + assert(c.address() == nullptr); + assert(bool(c) == false); + } + { // non-null case + char dummy = 42; + C c = C::from_address((void*)&dummy); + assert(c.address() == &dummy); + assert(bool(c) == true); + } +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.pass.cpp @@ -0,0 +1,58 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept; + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +void do_test(uintptr_t LHSVal, uintptr_t RHSVal) { + const C LHS = C::from_address(reinterpret_cast(LHSVal)); + const C RHS = C::from_address(reinterpret_cast(RHSVal)); + const bool ExpectIsEqual = (LHSVal == RHSVal); + assert((LHS == RHS) == ExpectIsEqual); + assert((RHS == LHS) == ExpectIsEqual); + assert((LHS != RHS) == !ExpectIsEqual); + assert((RHS != LHS) == !ExpectIsEqual); + { + static_assert(noexcept(LHS == RHS), ""); + static_assert(noexcept(LHS != RHS), ""); + ASSERT_SAME_TYPE(decltype(LHS == RHS), bool); + ASSERT_SAME_TYPE(decltype(LHS != RHS), bool); + } +} + +int main(int, char**) +{ + std::pair const TestCases[] = { + {0, 0}, + {16, 16}, + {0, 16}, + {16, 0} + }; + for (auto& TC : TestCases) { + do_test>(TC.first, TC.second); + do_test>(TC.first, TC.second); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp @@ -0,0 +1,65 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept; + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +void do_test(uintptr_t LHSVal, uintptr_t RHSVal) { + const C LHS = C::from_address(reinterpret_cast(LHSVal)); + const C RHS = C::from_address(reinterpret_cast(RHSVal)); + assert((LHS < RHS) == (LHSVal < RHSVal)); + assert((RHS < LHS) == (RHSVal < LHSVal)); + assert((LHS > RHS) == (LHSVal > RHSVal)); + assert((RHS > LHS) == (RHSVal > LHSVal)); + assert((LHS <= RHS) == (LHSVal <= RHSVal)); + assert((RHS <= LHS) == (RHSVal <= LHSVal)); + assert((LHS >= RHS) == (LHSVal >= RHSVal)); + assert((RHS >= LHS) == (RHSVal >= LHSVal)); + { + static_assert(noexcept(LHS < RHS), ""); + static_assert(noexcept(LHS > RHS), ""); + static_assert(noexcept(LHS <= RHS), ""); + static_assert(noexcept(LHS >= RHS), ""); + ASSERT_SAME_TYPE(decltype(LHS < RHS), bool); + ASSERT_SAME_TYPE(decltype(LHS > RHS), bool); + ASSERT_SAME_TYPE(decltype(LHS <= RHS), bool); + ASSERT_SAME_TYPE(decltype(LHS >= RHS), bool); + } +} + +int main(int, char**) +{ + std::pair const TestCases[] = { + {0, 0}, + {16, 16}, + {0, 16}, + {16, 0} + }; + for (auto& TC : TestCases) { + do_test>(TC.first, TC.second); + do_test>(TC.first, TC.second); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp @@ -0,0 +1,43 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// bool done() const + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +void do_test(std::coroutine_handle const& H) { + // FIXME Add a runtime test + { + ASSERT_SAME_TYPE(decltype(H.done()), bool); + LIBCPP_ASSERT_NOT_NOEXCEPT(H.done()); + } +} + +int main(int, char**) +{ + do_test(std::coroutine_handle<>{}); + do_test(std::coroutine_handle{}); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp @@ -0,0 +1,55 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// coroutine_handle& operator=(nullptr_t) noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void do_test() { + int dummy = 42; + void* dummy_h = &dummy; + { + C c; ((void)c); + static_assert(std::is_nothrow_assignable::value, ""); + static_assert(!std::is_assignable::value, ""); + } + { + C c = C::from_address(dummy_h); + assert(c.address() == &dummy); + c = nullptr; + assert(c.address() == nullptr); + c = nullptr; + assert(c.address() == nullptr); + } + { + C c; + C& cr = (c = nullptr); + assert(&c == &cr); + } +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.pass.cpp @@ -0,0 +1,54 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// constexpr coroutine_handle() noexcept +// constexpr coroutine_handle(nullptr_t) noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void do_test() { + { + constexpr C c; + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(c.address() == nullptr, ""); + } + { + constexpr C c(nullptr); + static_assert(std::is_nothrow_constructible::value, ""); + static_assert(c.address() == nullptr, ""); + } + { + C c; + assert(c.address() == nullptr); + } + { + C c(nullptr); + assert(c.address() == nullptr); + } +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.pass.cpp @@ -0,0 +1,50 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// constexpr void* address() const noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void do_test() { + { + constexpr C c; ((void)c); + static_assert(c.address() == nullptr, ""); + } + { + const C c = {}; ((void)c); + ASSERT_NOEXCEPT(c.address()); + ASSERT_SAME_TYPE(decltype(c.address()), void*); + assert(c.address() == nullptr); + } + { + char dummy = 42; + C c = C::from_address((void*)&dummy); + assert(c.address() == &dummy); + } +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.pass.cpp @@ -0,0 +1,58 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// static coroutine_handle from_address(void*) noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +void do_test() { + { + C c = C::from_address(nullptr); + static_assert(noexcept(C::from_address(nullptr)), ""); + static_assert(std::is_same::value, ""); + assert(c.address() == nullptr); + } + { + char dummy = 42; + C c = C::from_address((void*)&dummy); + assert(c.address() == &dummy); + } + { + C::from_address((int*)nullptr); + C::from_address((void*)nullptr); + C::from_address((char*)nullptr); + } + { + char dummy = 42; + C c = C::from_address(&dummy); + int* p = (int*)c.address(); + std::coroutine_handle<> c2 = std::coroutine_handle<>::from_address(p); + assert(c2 == c); + } +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp @@ -0,0 +1,76 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 +// UNSUPPORTED: ubsan + +// +// struct noop_coroutine_promise; +// using noop_coroutine_handle = coroutine_handle; +// noop_coroutine_handle noop_coroutine() noexcept; + +#include +#include +#include + +#include "test_macros.h" + +#if __has_builtin(__builtin_coro_noop) + +static_assert(std::is_same, std::noop_coroutine_handle>::value, ""); +static_assert(std::is_same::value, ""); + +// template <> struct coroutine_handle : coroutine_handle<> +// { +// // [coroutine.handle.noop.observers], observers +// constexpr explicit operator bool() const noexcept; +// constexpr bool done() const noexcept; + +// // [coroutine.handle.noop.resumption], resumption +// constexpr void operator()() const noexcept; +// constexpr void resume() const noexcept; +// constexpr void destroy() const noexcept; + +// // [coroutine.handle.noop.promise], promise access +// noop_coroutine_promise& promise() const noexcept; + +// // [coroutine.handle.noop.address], address +// constexpr void* address() const noexcept; + +int main(int, char**) +{ + auto h = std::noop_coroutine(); + std::coroutine_handle<> base = h; + + assert(h); + assert(base); + + assert(!h.done()); + assert(!base.done()); + + h.resume(); + h.destroy(); + h(); + static_assert(h.done() == false, ""); + static_assert(h, ""); + + h.promise(); + assert(h.address() == base.address()); + assert(h==base); + assert(h.address() != nullptr); + assert(std::coroutine_handle<>::from_address(h.address()) == base); + + return 0; +} + +#else + +int main(int, char**) { return 0; } + +#endif // __has_builtin(__builtin_coro_noop) \ No newline at end of file Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp @@ -0,0 +1,83 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// Promise& promise() const + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +struct MyCoro { + struct promise_type { + void unhandled_exception() {} + void return_void() {} + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + MyCoro get_return_object() { + do_runtime_test(); + return {}; + } + void do_runtime_test() { + // Test that a coroutine_handle can be created from a const + // promise_type and that it represents the same coroutine as + // coroutine_handle + using CH = std::coroutine_handle; + using CCH = std::coroutine_handle; + const auto &cthis = *this; + CH h = CH::from_promise(*this); + CCH h2 = CCH::from_promise(*this); + CCH h3 = CCH::from_promise(cthis); + assert(&h.promise() == this); + assert(&h2.promise() == this); + assert(&h3.promise() == this); + assert(h.address() == h2.address()); + assert(h2.address() == h3.address()); + } + }; +}; + +MyCoro do_runtime_test() { + co_await std::suspend_never{}; +} + +template +void do_test(std::coroutine_handle&& H) { + + // FIXME Add a runtime test + { + ASSERT_SAME_TYPE(decltype(H.promise()), Promise&); + LIBCPP_ASSERT_NOT_NOEXCEPT(H.promise()); + } + { + auto const& CH = H; + ASSERT_SAME_TYPE(decltype(CH.promise()), Promise&); + LIBCPP_ASSERT_NOT_NOEXCEPT(CH.promise()); + } +} + +int main(int, char**) +{ + do_test(std::coroutine_handle{}); + do_test(std::coroutine_handle{}); + do_runtime_test(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp @@ -0,0 +1,60 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// void destroy() const + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +auto has_destroy_imp(H&& h, int) -> decltype(h.destroy(), std::true_type{}); +template +auto has_destroy_imp(H&&, long) -> std::false_type; + +template +constexpr bool has_destroy() { + return decltype(has_destroy_imp(std::declval(), 0))::value; +} + +template +void do_test(std::coroutine_handle&& H) { + using HType = std::coroutine_handle; + // FIXME Add a runtime test + { + ASSERT_SAME_TYPE(decltype(H.destroy()), void); + LIBCPP_ASSERT_NOT_NOEXCEPT(H.destroy()); + static_assert(has_destroy(), ""); + static_assert(has_destroy(), ""); + } + { + static_assert(has_destroy(), ""); + static_assert(has_destroy(), ""); + } +} + +int main(int, char**) +{ + do_test(std::coroutine_handle<>{}); + do_test(std::coroutine_handle{}); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp @@ -0,0 +1,78 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// template +// struct coroutine_handle; + +// void operator()() const +// void resume() const + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +auto has_resume_imp(H&& h, int) -> decltype(h.resume(), std::true_type{}); +template +auto has_resume_imp(H&&, long) -> std::false_type; + +template +constexpr bool has_resume() { + return decltype(has_resume_imp(std::declval(), 0))::value; +} + + +template +auto has_call_operator_imp(H&& h, int) -> decltype(h(), std::true_type{}); +template +auto has_call_operator_imp(H&&, long) -> std::false_type; + +template +constexpr bool has_call_operator() { + return decltype(has_call_operator_imp(std::declval(), 0))::value; +} + +template +void do_test(std::coroutine_handle&& H) { + using HType = std::coroutine_handle; + // FIXME Add a runtime test + { + ASSERT_SAME_TYPE(decltype(H.resume()), void); + ASSERT_SAME_TYPE(decltype(H()), void); + LIBCPP_ASSERT_NOT_NOEXCEPT(H.resume()); + LIBCPP_ASSERT_NOT_NOEXCEPT(H()); + static_assert(has_resume(), ""); + static_assert(has_resume(), ""); + static_assert(has_call_operator(), ""); + static_assert(has_call_operator(), ""); + } + { + static_assert(has_resume(), ""); + static_assert(has_resume(), ""); + static_assert(has_call_operator(), ""); + static_assert(has_call_operator(), ""); + } +} + +int main(int, char**) +{ + do_test(std::coroutine_handle<>{}); + do_test(std::coroutine_handle{}); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp @@ -0,0 +1,52 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include + +#include "test_macros.h" + +struct A { + using promise_type = A*; +}; + +struct B {}; +struct C {}; + +namespace std { + template <> + struct coroutine_traits<::A, int> { + using promise_type = int*; + }; + template + struct coroutine_traits<::B, Args...> { + using promise_type = B*; + }; + template <> + struct coroutine_traits<::C> { + using promise_type = void; + }; +} + +template +void check_type() { + using P = typename std::coroutine_traits::promise_type ; + static_assert(std::is_same::value, ""); +}; + +int main(int, char**) +{ + check_type(); + check_type(); + check_type(); + check_type(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.pass.cpp @@ -0,0 +1,78 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include + +#include "test_macros.h" + +template +constexpr bool has_promise_type(int) { return true; } +template +constexpr bool has_promise_type(long) { return false; } +template +constexpr bool has_promise_type() { return has_promise_type(0); } + +struct A { + using promise_type = A*; +}; + +struct B {}; +struct C {}; +struct D { +private: + using promise_type = void; +}; +struct E {}; + +namespace std { + template <> + struct coroutine_traits<::A, int> { + using promise_type = int*; + }; + template + struct coroutine_traits<::B, Args...> { + using promise_type = B*; + }; + template <> + struct coroutine_traits<::C> { + using promise_type = void; + }; +} + +template +void check_type() { + using Traits = std::coroutine_traits; + static_assert(has_promise_type(), ""); + static_assert(std::is_same::value, ""); +} + +template +void check_no_type() { + using Traits = std::coroutine_traits; + static_assert(!has_promise_type(), ""); +} + +int main(int, char**) +{ + { + check_type(); + check_type(); + check_type(); + check_type(); + } + { + check_no_type(); + check_no_type(); + check_no_type(); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp @@ -0,0 +1,84 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include + +#include "test_macros.h" + +using SuspendT = std::suspend_always; + +TEST_SAFE_STATIC SuspendT safe_sa; +constexpr SuspendT constexpr_sa; + +constexpr bool check_suspend_constexpr() { + SuspendT s{}; + const SuspendT scopy(s); ((void)scopy); + SuspendT smove(std::move(s)); ((void)smove); + s = scopy; + s = std::move(smove); + return true; +} + +template +constexpr bool test_trivial_awaitable_constexpr(bool expected) { + T t; + assert(t.await_ready() == expected); + t.await_suspend(nullptr); + t.await_resume(); + return true; +} + +int main(int, char**) +{ + using H = std::coroutine_handle<>; + using S = SuspendT; + H h{}; + S s{}; + S const& cs = s; + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_ready()), ""); + static_assert(std::is_same::value, ""); + assert(s.await_ready() == false); + assert(cs.await_ready() == false); + } + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_suspend(h)), ""); + static_assert(std::is_same::value, ""); + s.await_suspend(h); + cs.await_suspend(h); + } + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_resume()), ""); + static_assert(std::is_same::value, ""); + s.await_resume(); + cs.await_resume(); + } + { + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(std::is_nothrow_copy_constructible::value, ""); + static_assert(std::is_nothrow_move_constructible::value, ""); + static_assert(std::is_nothrow_copy_assignable::value, ""); + static_assert(std::is_nothrow_move_assignable::value, ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(check_suspend_constexpr(), ""); + } + { + static_assert(test_trivial_awaitable_constexpr(false)); + } + { + // suppress unused warnings for the global constexpr test variable + ((void)constexpr_sa); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp @@ -0,0 +1,85 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include + +#include "test_macros.h" + +// Test that the type is in the correct namespace +using SuspendT = std::suspend_never; + +TEST_SAFE_STATIC SuspendT safe_sn; +constexpr SuspendT constexpr_sn; + +constexpr bool check_suspend_constexpr() { + SuspendT s{}; + const SuspendT scopy(s); ((void)scopy); + SuspendT smove(std::move(s)); ((void)smove); + s = scopy; + s = std::move(smove); + return true; +} + +template +constexpr bool test_trivial_awaitable_constexpr(bool expected) { + T t; + assert(t.await_ready() == expected); + t.await_suspend(nullptr); + t.await_resume(); + return true; +} + +int main(int, char**) +{ + using H = std::coroutine_handle<>; + using S = SuspendT; + H h{}; + S s{}; + S const& cs = s; + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_ready()), ""); + static_assert(std::is_same::value, ""); + assert(s.await_ready() == true); + assert(cs.await_ready() == true); + } + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_suspend(h)), ""); + static_assert(std::is_same::value, ""); + s.await_suspend(h); + cs.await_suspend(h); + } + { + LIBCPP_STATIC_ASSERT(noexcept(s.await_resume()), ""); + static_assert(std::is_same::value, ""); + s.await_resume(); + cs.await_resume(); + } + { + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(std::is_nothrow_copy_constructible::value, ""); + static_assert(std::is_nothrow_move_constructible::value, ""); + static_assert(std::is_nothrow_copy_assignable::value, ""); + static_assert(std::is_nothrow_move_assignable::value, ""); + static_assert(std::is_trivially_copyable::value, ""); + static_assert(check_suspend_constexpr(), ""); + } + { + static_assert(test_trivial_awaitable_constexpr(true)); + } + { + // suppress unused warnings for the global constexpr test variable + ((void)constexpr_sn); + } + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp @@ -0,0 +1,72 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include + +#include "test_macros.h" + +using namespace std; + +struct coro_t { + struct promise_type { + coro_t get_return_object() { + coroutine_handle{}; + return {}; + } + suspend_never initial_suspend() { return {}; } + suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + static void unhandled_exception() {} + }; +}; + +struct B { + ~B() {} + bool await_ready() { return true; } + B await_resume() { return {}; } + template void await_suspend(F) {} +}; + + +struct A { + ~A() {} + bool await_ready() { return true; } + int await_resume() { return 42; } + template void await_suspend(F) {} +}; + +int last_value = -1; +void set_value(int x) { + last_value = x; +} + +coro_t f(int n) { + if (n == 0) { + set_value(0); + co_return; + } + int val = co_await A{}; + ((void)val); + set_value(42); +} + +coro_t g() { B val = co_await B{}; } + +int main(int, char**) { + last_value = -1; + f(0); + assert(last_value == 0); + f(1); + assert(last_value == 42); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp @@ -0,0 +1,72 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// See https://llvm.org/PR33271 +// UNSUPPORTED: ubsan + +#include +#include + +#include "test_macros.h" + +using namespace std; + +struct coro_t { + struct promise_type { + coro_t get_return_object() { + return coroutine_handle::from_promise(*this); + } + suspend_never initial_suspend() { return {}; } + suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + coro_t(coroutine_handle hh) : h(hh) {} + coroutine_handle h; +}; + +struct NoSuspend { + bool await_ready() { return false; } + void await_resume() {} + template bool await_suspend(F) { return false; } +}; + +struct DoSuspend { + bool await_ready() { return false; } + void await_resume() {} + template bool await_suspend(F) { return true; } +}; + +bool f_started, f_resumed = false; +coro_t f() { + f_started = true; + co_await DoSuspend{}; + f_resumed = true; +} + +bool g_started, g_resumed = false; +coro_t g() { + g_started = true; + co_await NoSuspend{}; + g_resumed = true; +} + +int main(int, char**) { + assert(!f_started && !f_resumed && !g_started && !g_resumed); + auto fret = f(); + assert(f_started && !f_resumed); + fret.h.destroy(); + assert(f_started && !f_resumed); + g(); + assert(g_started && g_resumed); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp @@ -0,0 +1,89 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include + +#include "test_macros.h" +using namespace std; + +struct error_tag { }; + +template +struct expected { + + struct Data { + Data() : val(), error() { } + Data(T v, Error e) : val(v), error(e) { } + T val; + Error error; + }; + std::shared_ptr data; + + expected(T val) : data(std::make_shared(val, Error())) {} + expected(error_tag, Error error) : data(std::make_shared(T(), error)) {} + expected(std::shared_ptr p) : data(p) {} + + struct promise_type { + std::shared_ptr data; + expected get_return_object() { data = std::make_shared(); return {data}; } + suspend_never initial_suspend() { return {}; } + suspend_never final_suspend() noexcept { return {}; } + void return_value(T v) { data->val = v; data->error = {}; } + void unhandled_exception() {} + }; + + bool await_ready() { return !data->error; } + T await_resume() { return data->val; } + void await_suspend(coroutine_handle h) { + h.promise().data->error = data->error; + h.destroy(); + } + + T const& value() { return data->val; } + Error const& error() { return data->error; } +}; + +expected g() { return {0}; } +expected h() { return {error_tag{}, 42}; } + +extern "C" void print(int); + +bool f1_started, f1_resumed = false; +expected f1() { + f1_started = true; + (void)(co_await g()); + f1_resumed = true; + co_return 100; +} + +bool f2_started, f2_resumed = false; +expected f2() { + f2_started = true; + (void)(co_await h()); + f2_resumed = true; + co_return 200; +} + +int main(int, char**) { + auto c1 = f1(); + assert(f1_started && f1_resumed); + assert(c1.value() == 100); + assert(c1.error() == 0); + + auto c2 = f2(); + assert(f2_started && !f2_resumed); + assert(c2.value() == 0); + assert(c2.error() == 42); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp @@ -0,0 +1,118 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include + +#include "test_macros.h" + +using namespace std; + +int alive = 0; +int ctor_called = 0; +int dtor_called = 0; +void reset() { + assert(alive == 0); + alive = 0; + ctor_called = 0; + dtor_called = 0; +} +struct Noisy { + Noisy() { ++alive; ++ctor_called; } + ~Noisy() { --alive; ++dtor_called; } +#if TEST_STD_VER > 14 + Noisy(Noisy const&) = delete; +#else + // FIXME: This test depends on copy elision taking place in C++14 + // (pre-C++17 guaranteed copy elision) + Noisy(Noisy const&); +#endif +}; + +struct Bug { + bool await_ready() { return true; } + void await_suspend(std::coroutine_handle<>) {} + Noisy await_resume() { return {}; } +}; +struct coro2 { + struct promise_type { + suspend_never initial_suspend() { return{}; } + suspend_never final_suspend() noexcept { return {}; } + coro2 get_return_object() { return{}; } + void return_void() {} + Bug yield_value(int) { return {}; } + void unhandled_exception() {} + }; +}; + +// Checks that destructors are correctly invoked for the object returned by +// coawait. +coro2 a() { + reset(); + { + auto x = co_await Bug{}; + assert(alive == 1); + assert(ctor_called == 1); + assert(dtor_called == 0); + ((void)x); + } + assert(alive == 0); + assert(dtor_called == 1); +} + +coro2 b() { + reset(); + { + (void)(co_await Bug{}); + assert(ctor_called == 1); + assert(dtor_called == 1); + assert(alive == 0); + } + assert(ctor_called == 1); + assert(dtor_called == 1); + assert(alive == 0); + +} + +coro2 c() { + reset(); + { + auto x = co_yield 42; + assert(alive == 1); + assert(ctor_called == 1); + assert(dtor_called == 0); + } + assert(alive == 0); + assert(ctor_called == 1); + assert(dtor_called == 1); +} + +coro2 d() { + reset(); + { + (void)(co_yield 42); + assert(ctor_called == 1); + assert(dtor_called == 1); + assert(alive == 0); + } + assert(alive == 0); + assert(ctor_called == 1); + assert(dtor_called == 1); +} + +int main(int, char**) { + a(); + b(); + c(); + d(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp @@ -0,0 +1,105 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// See https://llvm.org/PR33271 +// UNSUPPORTED: ubsan + +#include +#include +#include + +#include "test_macros.h" +#include "coroutine_types.h" + +using namespace std; + +struct minig { + struct promise_type { + int current_value; + suspend_always yield_value(int value) { + this->current_value = value; + return {}; + } + suspend_always initial_suspend() { return {}; } + suspend_always final_suspend() noexcept { return {}; } + minig get_return_object() { return minig{this}; }; + void return_void() {} + void unhandled_exception() {} + }; + + bool move_next() { + p.resume(); + return !p.done(); + } + int current_value() { return p.promise().current_value; } + + minig(minig &&rhs) : p(rhs.p) { rhs.p = nullptr; } + + ~minig() { + if (p) + p.destroy(); + } + +private: + explicit minig(promise_type *p) + : p(coroutine_handle::from_promise(*p)) {} + + coroutine_handle p; +}; + + +minig mini_count(int n) { + for (int i = 0; i < n; i++) { + co_yield i; + } +} + +generator count(int n) { + for (int i = 0; i < n; ++i) + co_yield i; +} + +generator range(int from, int n) { + for (int i = from; i < n; ++i) + co_yield i; +} + +void test_count() { + const std::vector expect = {0, 1, 2, 3, 4}; + std::vector got; + for (auto x : count(5)) + got.push_back(x); + assert(expect == got); +} + +void test_range() { + int sum = 0; + for (auto v: range(1, 20)) + sum += v; + assert(sum == 190); +} + +void test_mini_generator() { + int sum = 0; + auto g = mini_count(5); + while (g.move_next()) { + sum += g.current_value(); + } + assert(sum == 10); +} + +int main(int, char**) { + test_count(); + test_range(); + test_mini_generator(); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp @@ -0,0 +1,177 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include + +#include "test_macros.h" + +using namespace std; + +bool cancel = false; + +struct goroutine +{ + static int const N = 10; + static int count; + static coroutine_handle<> stack[N]; + + static void schedule(coroutine_handle<>& rh) + { + assert(count < N); + stack[count++] = rh; + rh = nullptr; + } + + ~goroutine() {} + + static void go(goroutine) {} + + static void run_one() + { + assert(count > 0); + stack[--count](); + } + + struct promise_type + { + suspend_never initial_suspend() { + return {}; + } + suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + goroutine get_return_object() { + return{}; + } + void unhandled_exception() {} + }; +}; +int goroutine::count; +coroutine_handle<> goroutine::stack[N]; + +coroutine_handle workaround; + +class channel; + +struct push_awaiter { + channel* ch; + bool await_ready() {return false; } + void await_suspend(coroutine_handle<> rh); + void await_resume() {} +}; + +struct pull_awaiter { + channel * ch; + + bool await_ready(); + void await_suspend(coroutine_handle<> rh); + int await_resume(); +}; + +class channel +{ + using T = int; + + friend struct push_awaiter; + friend struct pull_awaiter; + + T const* pvalue = nullptr; + coroutine_handle<> reader = nullptr; + coroutine_handle<> writer = nullptr; +public: + push_awaiter push(T const& value) + { + assert(pvalue == nullptr); + assert(!writer); + pvalue = &value; + + return { this }; + } + + pull_awaiter pull() + { + assert(!reader); + + return { this }; + } + + void sync_push(T const& value) + { + assert(!pvalue); + pvalue = &value; + assert(reader); + reader(); + assert(!pvalue); + reader = nullptr; + } + + auto sync_pull() + { + while (!pvalue) goroutine::run_one(); + auto result = *pvalue; + pvalue = nullptr; + if (writer) + { + auto wr = writer; + writer = nullptr; + wr(); + } + return result; + } +}; + +void push_awaiter::await_suspend(coroutine_handle<> rh) +{ + ch->writer = rh; + if (ch->reader) goroutine::schedule(ch->reader); +} + + +bool pull_awaiter::await_ready() { + return !!ch->writer; +} +void pull_awaiter::await_suspend(coroutine_handle<> rh) { + ch->reader = rh; +} +int pull_awaiter::await_resume() { + auto result = *ch->pvalue; + ch->pvalue = nullptr; + if (ch->writer) { + //goroutine::schedule(ch->writer); + auto wr = ch->writer; + ch->writer = nullptr; + wr(); + } + return result; +} + +goroutine pusher(channel& left, channel& right) +{ + for (;;) { + auto val = co_await left.pull(); + co_await right.push(val + 1); + } +} + +const int N = 100; +channel* c = new channel[N + 1]; + +int main(int, char**) { + for (int i = 0; i < N; ++i) + goroutine::go(pusher(c[i], c[i + 1])); + + c[0].sync_push(0); + int result = c[N].sync_pull(); + + assert(result == 100); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp @@ -0,0 +1,90 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include + +#include "test_macros.h" + +using namespace std; + +// This file tests, multishot, movable std::function like thing using coroutine +// for compile-time type erasure and unerasure. +template struct func { + struct Input {R a, b;}; + + struct promise_type { + Input* I; + R result; + func get_return_object() { return {this}; } + suspend_always initial_suspend() { return {}; } + suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + template + suspend_always yield_value(F&& f) { + result = f(I->a, I->b); + return {}; + } + void unhandled_exception() {} + }; + + R operator()(Input I) { + h.promise().I = &I; + h.resume(); + R result = h.promise().result; + return result; + }; + + func() {} + func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; } + func(func const &) = delete; + + func &operator=(func &&rhs) { + if (this != &rhs) { + if (h) + h.destroy(); + h = rhs.h; + rhs.h = nullptr; + } + return *this; + } + + template static func Create(F f) { + for (;;) { + co_yield f; + } + } + + template func(F f) : func(Create(f)) {} + + ~func() { + if (h) + h.destroy(); + } + +private: + func(promise_type *promise) + : h(coroutine_handle::from_promise(*promise)) {} + coroutine_handle h; +}; + +int Do(int acc, int n, func f) { + for (int i = 0; i < n; ++i) + acc = f({acc, i}); + return acc; +} + +int main(int, char**) { + int result = Do(1, 10, [](int a, int b) {return a + b;}); + assert(result == 46); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp @@ -0,0 +1,86 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +#include +#include +#include + +#include "test_macros.h" + +using namespace std; + +// This file tests, one shot, movable std::function like thing using coroutine +// for compile-time type erasure and unerasure. + +template struct func { + struct promise_type { + R result; + func get_return_object() { return {this}; } + suspend_always initial_suspend() { return {}; } + suspend_always final_suspend() noexcept { return {}; } + void return_value(R v) { result = v; } + void unhandled_exception() {} + }; + + R operator()() { + h.resume(); + R result = h.promise().result; + h.destroy(); + h = nullptr; + return result; + }; + + func() {} + func(func &&rhs) : h(rhs.h) { rhs.h = nullptr; } + func(func const &) = delete; + + func &operator=(func &&rhs) { + if (this != &rhs) { + if (h) + h.destroy(); + h = rhs.h; + rhs.h = nullptr; + } + return *this; + } + + template static func Create(F f) { co_return f(); } + + template func(F f) : func(Create(f)) {} + + ~func() { + if (h) + h.destroy(); + } + +private: + func(promise_type *promise) + : h(coroutine_handle::from_promise(*promise)) {} + coroutine_handle h; +}; + +std::vector yielded_values = {}; +int yield(int x) { yielded_values.push_back(x); return x + 1; } +float fyield(int x) { yielded_values.push_back(x); return static_cast(x + 2); } + +void Do1(func f) { yield(f()); } +void Do2(func f) { yield(static_cast(f())); } + +int main(int, char**) { + Do1([] { return yield(43); }); + assert((yielded_values == std::vector{43, 44})); + + yielded_values = {}; + Do2([] { return fyield(44); }); + assert((yielded_values == std::vector{44, 46})); + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/includes.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/includes.pass.cpp @@ -0,0 +1,26 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// + +// Test that includes + +#include + +int main(int, char**) { + // std::nothrow is not implicitly defined by the compiler when the include is + // missing, unlike other parts of . Therefore we use std::nothrow to + // test for #include + + (void)std::nothrow; + + return 0; +} Index: libcxx/test/std/language.support/support.coroutines/lit.local.cfg =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.coroutines/lit.local.cfg @@ -0,0 +1,7 @@ +# If the compiler doesn't support coroutines mark all of the tests under +# this directory as unsupported. Otherwise add the required `-fcoroutines-ts` +# flag. +if 'fcoroutines-ts' not in config.available_features: + config.unsupported = True +else: + config.test_format.addCompileFlags(config, '-fcoroutines-ts') Index: libcxx/test/support/coroutine_experimental_types.h =================================================================== --- /dev/null +++ libcxx/test/support/coroutine_experimental_types.h @@ -0,0 +1,74 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_COROUTINE_EXPERIMENTAL_TYPES_H +#define SUPPORT_COROUTINE_EXPERIMENTAL_TYPES_H + +#include + +template struct generator { + struct promise_type { + Ty current_value; + std::experimental::suspend_always yield_value(Ty value) { + this->current_value = value; + return {}; + } + std::experimental::suspend_always initial_suspend() { return {}; } + std::experimental::suspend_always final_suspend() noexcept { return {}; } + generator get_return_object() { return generator{this}; }; + void return_void() {} + void unhandled_exception() {} + }; + + struct iterator { + std::experimental::coroutine_handle _Coro; + bool _Done; + + iterator(std::experimental::coroutine_handle Coro, bool Done) + : _Coro(Coro), _Done(Done) {} + + iterator &operator++() { + _Coro.resume(); + _Done = _Coro.done(); + return *this; + } + + bool operator==(iterator const &_Right) const { + return _Done == _Right._Done; + } + + bool operator!=(iterator const &_Right) const { return !(*this == _Right); } + + Ty const &operator*() const { return _Coro.promise().current_value; } + + Ty const *operator->() const { return &(operator*()); } + }; + + iterator begin() { + p.resume(); + return {p, p.done()}; + } + + iterator end() { return {p, true}; } + + generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; } + + ~generator() { + if (p) + p.destroy(); + } + +private: + explicit generator(promise_type *p) + : p(std::experimental::coroutine_handle::from_promise(*p)) {} + + std::experimental::coroutine_handle p; +}; + +#endif // SUPPORT_COROUTINE_EXPERIMENTAL_TYPES_H Index: libcxx/test/support/coroutine_types.h =================================================================== --- libcxx/test/support/coroutine_types.h +++ libcxx/test/support/coroutine_types.h @@ -10,27 +10,27 @@ #ifndef SUPPORT_COROUTINE_TYPES_H #define SUPPORT_COROUTINE_TYPES_H -#include +#include template struct generator { struct promise_type { Ty current_value; - std::experimental::suspend_always yield_value(Ty value) { + std::suspend_always yield_value(Ty value) { this->current_value = value; return {}; } - std::experimental::suspend_always initial_suspend() { return {}; } - std::experimental::suspend_always final_suspend() noexcept { return {}; } + std::suspend_always initial_suspend() { return {}; } + std::suspend_always final_suspend() noexcept { return {}; } generator get_return_object() { return generator{this}; }; void return_void() {} void unhandled_exception() {} }; struct iterator { - std::experimental::coroutine_handle _Coro; + std::coroutine_handle _Coro; bool _Done; - iterator(std::experimental::coroutine_handle Coro, bool Done) + iterator(std::coroutine_handle Coro, bool Done) : _Coro(Coro), _Done(Done) {} iterator &operator++() { @@ -66,9 +66,9 @@ private: explicit generator(promise_type *p) - : p(std::experimental::coroutine_handle::from_promise(*p)) {} + : p(std::coroutine_handle::from_promise(*p)) {} - std::experimental::coroutine_handle p; + std::coroutine_handle p; }; #endif // SUPPORT_COROUTINE_TYPES_H Index: libcxx/utils/generate_header_tests.py =================================================================== --- libcxx/utils/generate_header_tests.py +++ libcxx/utils/generate_header_tests.py @@ -51,6 +51,7 @@ "strstream": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], "experimental/coroutine": ["if defined(__cpp_coroutines)"], + "coroutine": ["if defined(__cpp_coroutines)"], "experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], }