Index: clang/lib/Frontend/InitPreprocessor.cpp =================================================================== --- clang/lib/Frontend/InitPreprocessor.cpp +++ clang/lib/Frontend/InitPreprocessor.cpp @@ -600,7 +600,7 @@ //Builder.defineMacro("__cpp_consteval", "201811L"); Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L"); Builder.defineMacro("__cpp_constinit", "201907L"); - //Builder.defineMacro("__cpp_coroutines", "201902L"); + Builder.defineMacro("__cpp_impl_coroutine", "201902L"); Builder.defineMacro("__cpp_designated_initializers", "201707L"); Builder.defineMacro("__cpp_impl_three_way_comparison", "201907L"); //Builder.defineMacro("__cpp_modules", "201907L"); Index: libcxx/docs/FeatureTestMacroTable.rst =================================================================== --- libcxx/docs/FeatureTestMacroTable.rst +++ libcxx/docs/FeatureTestMacroTable.rst @@ -224,7 +224,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_vector`` *unimplemented* ------------------------------------------------- ----------------- - ``__cpp_lib_coroutine`` *unimplemented* + ``__cpp_lib_coroutine`` ``201902L`` ------------------------------------------------- ----------------- ``__cpp_lib_destroying_delete`` ``201806L`` ------------------------------------------------- ----------------- Index: libcxx/docs/Status/Cxx20Issues.csv =================================================================== --- libcxx/docs/Status/Cxx20Issues.csv +++ libcxx/docs/Status/Cxx20Issues.csv @@ -292,7 +292,7 @@ "`3388 `__","``view``\ iterator types have ill-formed ``<=>``\ operators","Prague","","","|ranges|" "`3389 `__","A move-only iterator still does not have a ``counted_iterator``\ ","Prague","","","|ranges|" "`3390 `__","``make_move_iterator()``\ cannot be used to construct a ``move_iterator``\ for a move-only iterator","Prague","","","|ranges|" -"`3393 `__","Missing/incorrect feature test macro for coroutines","Prague","","" +"`3393 `__","Missing/incorrect feature test macro for coroutines","Prague","|Complete|","14.0" "`3395 `__","Definition for three-way comparison needs to be updated (US 152)","Prague","","","|spaceship|" "`3396 `__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","","" "`3397 `__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","","","|ranges|" 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,|Complete| | `[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 @@ -131,6 +131,10 @@ __concepts/swappable.h __concepts/totally_ordered.h __config + __coroutine/coroutine_handle.h + __coroutine/coroutine_noop.h + __coroutine/coroutine_traits.h + __coroutine/coroutine_trivial_awaitables.h __debug __errc __format/format_arg.h @@ -324,6 +328,7 @@ complex.h concepts condition_variable + coroutine csetjmp csignal cstdarg Index: libcxx/include/__config =================================================================== --- libcxx/include/__config +++ libcxx/include/__config @@ -1273,7 +1273,7 @@ #define _LIBCPP_ENABLE_CXX20_REMOVED_TYPE_TRAITS #endif // _LIBCPP_ENABLE_CXX20_REMOVED_FEATURES -#if !defined(__cpp_coroutines) || __cpp_coroutines < 201703L +#if !defined(__cpp_impl_coroutine) || __cpp_impl_coroutine < 201902L #define _LIBCPP_HAS_NO_COROUTINES #endif Index: libcxx/include/__coroutine/coroutine_handle.h =================================================================== --- /dev/null +++ libcxx/include/__coroutine/coroutine_handle.h @@ -0,0 +1,228 @@ +//===----------------------------------------------------------------------===// +// +// 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_HANDLE_H +#define _LIBCPP___COROUTINE_HANDLE_H + +#include <__config> +#include <__debug> +#include <__functional/hash.h> +#include <__memory/addressof.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +// [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 = default; + + _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_ != nullptr; + } + + _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 { + // FIXME actually implement a check for if the coro is suspended. + return __handle_ != nullptr; + } + + 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 compare_three_way()(__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_ != nullptr; + } + + _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 { + // FIXME actually implement a check for if the coro is suspended. + return __handle_ != nullptr; + } + void* __handle_ = nullptr; +}; + +// [coroutine.handle.hash] +template +struct hash> { + _LIBCPP_HIDE_FROM_ABI + size_t operator()(const coroutine_handle<_Tp>& __v) const noexcept { return hash()(__v.address()); } +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +#endif // _LIBCPP___COROUTINE_HANDLE_H Index: libcxx/include/__coroutine/coroutine_noop.h =================================================================== --- /dev/null +++ libcxx/include/__coroutine/coroutine_noop.h @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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_NOOP_H +#define _LIBCPP___COROUTINE_NOOP_H + +#include <__config> +#include <__coroutine/coroutine_handle.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +#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) + +_LIBCPP_END_NAMESPACE_STD + +#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +#endif // _LIBCPP___COROUTINE_NOOP_H Index: libcxx/include/__coroutine/coroutine_traits.h =================================================================== --- /dev/null +++ libcxx/include/__coroutine/coroutine_traits.h @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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_TRAITS_H +#define _LIBCPP___COROUTINE_TRAITS_H + +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +// [coroutine.traits] +// [coroutine.traits.primary] +// The header defined 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 memebr: +// +// 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> +{ +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +#endif // _LIBCPP___COROUTINE_TRAITS_H Index: libcxx/include/__coroutine/coroutine_trivial_awaitables.h =================================================================== --- /dev/null +++ libcxx/include/__coroutine/coroutine_trivial_awaitables.h @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// 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_TRIVIAL_AWAITABLES_H +#define __LIBCPP___COROUTINE_TRIVIAL_AWAITABLES_H + +#include <__config> +#include <__coroutine/coroutine_handle.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +// [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 {} +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_COROUTINES) + +#endif // __LIBCPP___COROUTINE_TRIVIAL_AWAITABLES_H Index: libcxx/include/coroutine =================================================================== --- /dev/null +++ libcxx/include/coroutine @@ -0,0 +1,51 @@ +// -*- 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 _LIBCPP_COROUTINE +#define _LIBCPP_COROUTINE + +/** + coroutine synopsis + +namespace std { +// [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 std + + */ + +#include <__config> +#include <__coroutine/coroutine_handle.h> +#include <__coroutine/coroutine_noop.h> +#include <__coroutine/coroutine_traits.h> +#include <__coroutine/coroutine_trivial_awaitables.h> + +#ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +#pragma GCC system_header +#endif + +#endif // _LIBCPP_COROUTINE Index: libcxx/include/experimental/__config =================================================================== --- libcxx/include/experimental/__config +++ libcxx/include/experimental/__config @@ -45,6 +45,10 @@ #define _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM \ } } _LIBCPP_END_NAMESPACE_EXPERIMENTAL +#if !defined(__cpp_coroutines) || __cpp_coroutines < 201703L +#define _LIBCPP_LEGACY_HAS_NO_COROUTINES +#endif + #define _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES \ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL inline namespace coroutines_v1 { Index: libcxx/include/experimental/coroutine =================================================================== --- libcxx/include/experimental/coroutine +++ libcxx/include/experimental/coroutine @@ -57,7 +57,7 @@ #pragma GCC system_header #endif -#ifdef _LIBCPP_HAS_NO_COROUTINES +#ifdef _LIBCPP_LEGACY_HAS_NO_COROUTINES # if defined(_LIBCPP_WARNING) _LIBCPP_WARNING(" cannot be used with this compiler") # else @@ -65,7 +65,7 @@ # endif #endif -#ifndef _LIBCPP_HAS_NO_COROUTINES +#ifndef _LIBCPP_LEGACY_HAS_NO_COROUTINES _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES @@ -329,6 +329,6 @@ _LIBCPP_END_NAMESPACE_STD -#endif // !defined(_LIBCPP_HAS_NO_COROUTINES) +#endif // !defined(_LIBCPP_LEGACY_HAS_NO_COROUTINES) #endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */ Index: libcxx/include/module.modulemap =================================================================== --- libcxx/include/module.modulemap +++ libcxx/include/module.modulemap @@ -417,6 +417,18 @@ header "condition_variable" export * } + module coroutine { + requires coroutines + header "coroutine" + export * + + module __coroutine { + module coroutine_handle { private header "__coroutine/coroutine_handle.h" } + module coroutine_noop { private header "__coroutine/coroutine_noop.h" } + module coroutine_traits { private header "__coroutine/coroutine_traits.h" } + module coroutine_trivial_awaitables { private header "__coroutine/coroutine_trivial_awaitables.h" } + } + } module deque { header "deque" export initializer_list Index: libcxx/include/version =================================================================== --- libcxx/include/version +++ libcxx/include/version @@ -305,7 +305,7 @@ # define __cpp_lib_constexpr_tuple 201811L # define __cpp_lib_constexpr_utility 201811L // # define __cpp_lib_constexpr_vector 201907L -// # define __cpp_lib_coroutine 201902L +# define __cpp_lib_coroutine 201902L # if _LIBCPP_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L # define __cpp_lib_destroying_delete 201806L # endif Index: libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_handle.module.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_handle.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__coroutine/coroutine_handle.h'}} +#include <__coroutine/coroutine_handle.h> Index: libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_noop.module.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_noop.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__coroutine/coroutine_noop.h'}} +#include <__coroutine/coroutine_noop.h> Index: libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_traits.module.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_traits.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__coroutine/coroutine_traits.h'}} +#include <__coroutine/coroutine_traits.h> Index: libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_trivial_awaitables.module.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_trivial_awaitables.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__coroutine/coroutine_trivial_awaitables.h'}} +#include <__coroutine/coroutine_trivial_awaitables.h> Index: libcxx/test/libcxx/double_include.sh.cpp =================================================================== --- libcxx/test/libcxx/double_include.sh.cpp +++ libcxx/test/libcxx/double_include.sh.cpp @@ -62,6 +62,9 @@ #include #include #include +#if defined(__cpp_impl_coroutine) +# include +#endif #include #include #include Index: libcxx/test/libcxx/inclusions/coroutine.inclusions.compile.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/inclusions/coroutine.inclusions.compile.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_header_inclusion_tests.py +// and should not be edited manually. +// +// clang-format off + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// + +// Test that includes all the other headers it's supposed to. + +#include +#include "test_macros.h" + +#if !defined(_LIBCPP_COROUTINE) + # error " was expected to define _LIBCPP_COROUTINE" +#endif +#if TEST_STD_VER > 17 && !defined(_LIBCPP_COMPARE) + # error " should include in C++20 and later" +#endif Index: libcxx/test/libcxx/min_max_macros.compile.pass.cpp =================================================================== --- libcxx/test/libcxx/min_max_macros.compile.pass.cpp +++ libcxx/test/libcxx/min_max_macros.compile.pass.cpp @@ -88,6 +88,10 @@ TEST_MACROS(); #include TEST_MACROS(); +#if defined(__cpp_impl_coroutine) +# include +TEST_MACROS(); +#endif #include TEST_MACROS(); #include Index: libcxx/test/libcxx/no_assert_include.compile.pass.cpp =================================================================== --- libcxx/test/libcxx/no_assert_include.compile.pass.cpp +++ libcxx/test/libcxx/no_assert_include.compile.pass.cpp @@ -57,6 +57,9 @@ #include #include #include +#if defined(__cpp_impl_coroutine) +# include +#endif #include #include #include 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 @@ -16,19 +16,76 @@ #include #include "test_macros.h" -#include "coroutine_types.h" -using namespace std::experimental; + +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; +}; struct minig { struct promise_type { int current_value; - suspend_always yield_value(int value) { + std::experimental::suspend_always yield_value(int value) { this->current_value = value; return {}; } - suspend_always initial_suspend() { return {}; } - suspend_always final_suspend() noexcept { return {}; } + std::experimental::suspend_always initial_suspend() { return {}; } + std::experimental::suspend_always final_suspend() noexcept { return {}; } minig get_return_object() { return minig{this}; }; void return_void() {} void unhandled_exception() {} @@ -49,9 +106,9 @@ private: explicit minig(promise_type *p) - : p(coroutine_handle::from_promise(*p)) {} + : p(std::experimental::coroutine_handle::from_promise(*p)) {} - coroutine_handle p; + std::experimental::coroutine_handle p; }; 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,61 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// template +// struct coroutine_handle; + +// namespace std { +// template struct hash>; +// } + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +void do_test(int *LHSVal, int *RHSVal) { + const size_t ExpectLHS = std::hash{}((LHSVal)); + const size_t ExpectRHS = std::hash{}((RHSVal)); + const C LHS = C::from_address((LHSVal)); + const C RHS = C::from_address((RHSVal)); + const std::hash h; + + LIBCPP_ASSERT(h(LHS) == ExpectLHS); + LIBCPP_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**) +{ + int i, j; + std::pair const TestCases[] = { + {nullptr, nullptr}, + {nullptr, &i}, + {&i, &i}, + {&i, &j} + }; + 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,55 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// template +// struct coroutine_handle; + +// constexpr explicit operator bool() const noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +constexpr bool do_test() { + static_assert(std::is_nothrow_constructible::value, ""); + static_assert(!std::is_convertible::value, ""); + { + constexpr C c; + static_assert(bool(c) == false, ""); + } + { // null case + const C c = {}; + ASSERT_NOEXCEPT(bool(c)); + 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); + } + return true; +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + static_assert(do_test>()); + static_assert(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 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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(int *LHSVal, int *RHSVal) { + const C LHS = C::from_address(LHSVal); + const C RHS = C::from_address(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**) +{ + int i; + std::pair const TestCases[] = { + {nullptr, nullptr}, + {&i, &i}, + {nullptr, &i}, + {&i, nullptr} + }; + 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 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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(int *LHSVal, int *RHSVal) { + const C LHS = C::from_address(LHSVal); + const C RHS = C::from_address(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**) +{ + int i; + std::pair const TestCases[] = { + {nullptr, nullptr}, + {&i, &i}, + {nullptr, &i}, + {&i, nullptr} + }; + 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,42 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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,53 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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; + { + 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,48 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// template +// struct coroutine_handle; + +// constexpr coroutine_handle() noexcept +// constexpr coroutine_handle(nullptr_t) noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +constexpr bool do_test() { + static_assert(std::is_nothrow_constructible::value, ""); + static_assert(std::is_nothrow_constructible::value, ""); + { + C c; + assert(c.address() == nullptr); + } + { + C c = C(nullptr); + assert(c.address() == nullptr); + } + return true; +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + static_assert(do_test>()); + static_assert(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,52 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// template +// struct coroutine_handle; + +// constexpr void* address() const noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +constexpr bool 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); + } + return true; +} + +int main(int, char**) +{ + do_test>(); + do_test>(); + static_assert(do_test>()); + static_assert(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,57 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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,75 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 +// 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) 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,82 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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,59 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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,77 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// + +// 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,49 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include + +#include "test_macros.h" + +struct A { + using promise_type = A*; +}; + +struct B {}; +struct C {}; + +template <> +struct std::coroutine_traits { + using promise_type = int*; +}; +template +struct std::coroutine_traits { + using promise_type = B*; +}; +template <> +struct std::coroutine_traits { + 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,75 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#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 {}; + +template <> +struct std::coroutine_traits { + using promise_type = int*; +}; +template +struct std::coroutine_traits { + using promise_type = B*; +}; +template <> +struct std::coroutine_traits { + 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,74 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include +#include + +#include "test_macros.h" + +TEST_SAFE_STATIC std::suspend_always safe_sa; + +constexpr bool check_suspend_constexpr() { + std::suspend_always s{}; + const std::suspend_always scopy(s); + std::suspend_always smove(std::move(s)); + 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**) +{ + std::coroutine_handle<> h{}; + std::suspend_always s{}; + std::suspend_always const& cs = s; + { + ASSERT_NOEXCEPT(s.await_ready()); + static_assert(std::is_same::value, ""); + assert(s.await_ready() == false); + assert(cs.await_ready() == false); + } + { + ASSERT_NOEXCEPT(s.await_suspend(h)); + static_assert(std::is_same::value, ""); + s.await_suspend(h); + cs.await_suspend(h); + } + { + 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)); + } + + 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,83 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include +#include + +#include "test_macros.h" + +// Test that the type 'std::suspend_never' is in the correct namespace + +TEST_SAFE_STATIC std::suspend_never safe_sn; +constexpr std::suspend_never constexpr_sn; + +constexpr bool check_suspend_constexpr() { + std::suspend_never s{}; + const std::suspend_never scopy(s); ((void)scopy); + std::suspend_never 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 = std::suspend_never; + 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,69 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include + +#include "test_macros.h" + +struct coro_t { + struct promise_type { + coro_t get_return_object() { + std::coroutine_handle{}; + return {}; + } + std::suspend_never initial_suspend() { return {}; } + std::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,69 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// See https://llvm.org/PR33271 +// UNSUPPORTED: ubsan + +#include +#include + +#include "test_macros.h" + +struct coro_t { + struct promise_type { + coro_t get_return_object() { + return std::coroutine_handle::from_promise(*this); + } + std::suspend_never initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() {} + }; + coro_t(std::coroutine_handle hh) : h(hh) {} + std::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,87 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include +#include + +#include "test_macros.h" + +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}; } + std::suspend_never initial_suspend() { return {}; } + std::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(std::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,109 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include + +#include "test_macros.h" + +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; } + Noisy(Noisy const&) = delete; +}; + +struct Bug { + bool await_ready() { return true; } + void await_suspend(std::coroutine_handle<>) {} + Noisy await_resume() { return {}; } +}; +struct coro2 { + struct promise_type { + std::suspend_never initial_suspend() { return {}; } + std::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(); + { + 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(); + { + 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,160 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +// See https://llvm.org/PR33271 +// UNSUPPORTED: ubsan + +#include +#include +#include + +#include "test_macros.h" + +template struct generator { + struct promise_type { + Ty current_value; + std::suspend_always yield_value(Ty value) { + this->current_value = value; + 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::coroutine_handle Coro_; + bool Done_; + + iterator(std::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 *promise) + : p(std::coroutine_handle::from_promise(*promise)) {} + + std::coroutine_handle p; +}; + +struct minig { + struct promise_type { + int current_value; + std::suspend_always yield_value(int value) { + this->current_value = value; + return {}; + } + std::suspend_always initial_suspend() { return {}; } + std::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 *promise) + : p(std::coroutine_handle::from_promise(*promise)) {} + + std::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,175 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include +#include + +#include "test_macros.h" + +bool cancel = false; + +struct goroutine +{ + static int const N = 10; + static int count; + static std::coroutine_handle<> stack[N]; + + static void schedule(std::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 + { + std::suspend_never initial_suspend() { + return {}; + } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + goroutine get_return_object() { + return{}; + } + void unhandled_exception() {} + }; +}; +int goroutine::count; +std::coroutine_handle<> goroutine::stack[N]; + +std::coroutine_handle workaround; + +class channel; + +struct push_awaiter { + channel* ch; + bool await_ready() {return false; } + void await_suspend(std::coroutine_handle<> rh); + void await_resume() {} +}; + +struct pull_awaiter { + channel * ch; + + bool await_ready(); + void await_suspend(std::coroutine_handle<> rh); + int await_resume(); +}; + +class channel +{ + using T = int; + + friend struct push_awaiter; + friend struct pull_awaiter; + + T const* pvalue = nullptr; + std::coroutine_handle<> reader = nullptr; + std::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(std::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(std::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; +auto c = std::make_unique(N + 1); + +int main(int, char**) { + for (int i = 0; i < N; ++i) + 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,87 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include + +#include "test_macros.h" + +// 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}; } + std::suspend_always initial_suspend() { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + template + std::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(std::coroutine_handle::from_promise(*promise)) {} + std::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,72 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++14, c++17 + +#include +#include +#include + +#include "test_macros.h" + +// 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}; } + std::suspend_always initial_suspend() { return {}; } + std::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(std::coroutine_handle::from_promise(*promise)) {} + std::coroutine_handle h; +}; + +int main(int, char**) { + func f = func::Create([]() { return 44; }); + assert(f() == 44); + + return 0; +} Index: libcxx/test/std/language.support/support.limits/support.limits.general/coroutine.version.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/language.support/support.limits/support.limits.general/coroutine.version.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_coroutine 201902L [C++20] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 20 + +# ifndef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should be defined in c++20" +# endif +# if __cpp_lib_coroutine != 201902L +# error "__cpp_lib_coroutine should have the value 201902L in c++20" +# endif + +#elif TEST_STD_VER > 20 + +# ifndef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should be defined in c++2b" +# endif +# if __cpp_lib_coroutine != 201902L +# error "__cpp_lib_coroutine should have the value 201902L in c++2b" +# endif + +#endif // TEST_STD_VER > 20 + +int main(int, char**) { return 0; } Index: libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp =================================================================== --- libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -2462,17 +2462,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_coroutine -# error "__cpp_lib_coroutine should be defined in c++20" -# endif -# if __cpp_lib_coroutine != 201902L -# error "__cpp_lib_coroutine should have the value 201902L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_coroutine -# error "__cpp_lib_coroutine should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should be defined in c++20" +# endif +# if __cpp_lib_coroutine != 201902L +# error "__cpp_lib_coroutine should have the value 201902L in c++20" # endif # if TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L @@ -3613,17 +3607,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_coroutine -# error "__cpp_lib_coroutine should be defined in c++2b" -# endif -# if __cpp_lib_coroutine != 201902L -# error "__cpp_lib_coroutine should have the value 201902L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_coroutine -# error "__cpp_lib_coroutine should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_coroutine +# error "__cpp_lib_coroutine should be defined in c++2b" +# endif +# if __cpp_lib_coroutine != 201902L +# error "__cpp_lib_coroutine should have the value 201902L in c++2b" # endif # if TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L Index: libcxx/test/support/coroutine_types.h =================================================================== --- libcxx/test/support/coroutine_types.h +++ /dev/null @@ -1,74 +0,0 @@ -// -*- 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_TYPES_H -#define SUPPORT_COROUTINE_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_TYPES_H Index: libcxx/utils/generate_feature_test_macro_components.py =================================================================== --- libcxx/utils/generate_feature_test_macro_components.py +++ libcxx/utils/generate_feature_test_macro_components.py @@ -258,7 +258,6 @@ "name": "__cpp_lib_coroutine", "values": { "c++20": 201902 }, "headers": ["coroutine"], - "unimplemented": True, }, { "name": "__cpp_lib_destroying_delete", "values": { "c++20": 201806 }, Index: libcxx/utils/generate_header_inclusion_tests.py =================================================================== --- libcxx/utils/generate_header_inclusion_tests.py +++ libcxx/utils/generate_header_inclusion_tests.py @@ -29,7 +29,7 @@ "chrono": ["compare"], "cinttypes": ["cstdint"], "complex.h": ["complex"], - # TODO "coroutine": ["compare"], + "coroutine": ["compare"], "deque": ["compare", "initializer_list"], "filesystem": ["compare"], "forward_list": ["compare", "initializer_list"], Index: libcxx/utils/generate_header_tests.py =================================================================== --- libcxx/utils/generate_header_tests.py +++ libcxx/utils/generate_header_tests.py @@ -56,6 +56,7 @@ "wchar.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"], "experimental/coroutine": ["if defined(__cpp_coroutines)"], + "coroutine": ["if defined(__cpp_impl_coroutine)"], "experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"], }