[libcxx-commits] [libcxx] 2e6ae1d - [libcxx] [Coroutine] Conform Coroutine Implementation
Chuanqi Xu via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Nov 15 22:15:36 PST 2021
Author: Chuanqi Xu
Date: 2021-11-16T14:13:13+08:00
New Revision: 2e6ae1d3f2de1aa96ace695ddd6dc58ef259e248
URL: https://github.com/llvm/llvm-project/commit/2e6ae1d3f2de1aa96ace695ddd6dc58ef259e248
DIFF: https://github.com/llvm/llvm-project/commit/2e6ae1d3f2de1aa96ace695ddd6dc58ef259e248.diff
LOG: [libcxx] [Coroutine] Conform Coroutine Implementation
Since coroutine is merged in C++ standard and the support for coroutine
seems relatively stable. It's the time to move the implementation of
coroutine out of the experimental directory and the std::experimental
namespace. This patch creates header <coroutine> with conformed
implementation with C++ standard. To avoid breaking user's code too
fast, the <experimental/coroutine> header is remained. Note that
<experimental/coroutine> is deprecated and it would be removed in
LLVM15.
Reviewed By: Quuxplusone, ldionne
Differential Revision: https://reviews.llvm.org/D109433
Added:
libcxx/include/__coroutine/coroutine_handle.h
libcxx/include/__coroutine/coroutine_traits.h
libcxx/include/__coroutine/noop_coroutine_handle.h
libcxx/include/__coroutine/trivial_awaitables.h
libcxx/include/coroutine
libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_handle.module.verify.cpp
libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_traits.module.verify.cpp
libcxx/test/libcxx/diagnostics/detail.headers/coroutine/noop_coroutine_handle.module.verify.cpp
libcxx/test/libcxx/diagnostics/detail.headers/coroutine/trivial_awaitables.module.verify.cpp
libcxx/test/libcxx/inclusions/coroutine.inclusions.compile.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp
libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp
libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/coroutine.version.pass.cpp
Modified:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/Status/Cxx20Issues.csv
libcxx/docs/Status/SpaceshipProjects.csv
libcxx/include/CMakeLists.txt
libcxx/include/__config
libcxx/include/experimental/__config
libcxx/include/experimental/coroutine
libcxx/include/module.modulemap
libcxx/include/version
libcxx/test/libcxx/double_include.sh.cpp
libcxx/test/libcxx/min_max_macros.compile.pass.cpp
libcxx/test/libcxx/no_assert_include.compile.pass.cpp
libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
libcxx/utils/generate_feature_test_macro_components.py
libcxx/utils/generate_header_inclusion_tests.py
libcxx/utils/generate_header_tests.py
libcxx/utils/libcxx/test/features.py
Removed:
libcxx/test/support/coroutine_types.h
################################################################################
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d58451a55546c..8300b86f565bc 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -224,7 +224,7 @@ Status
------------------------------------------------- -----------------
``__cpp_lib_constexpr_vector`` *unimplemented*
------------------------------------------------- -----------------
- ``__cpp_lib_coroutine`` *unimplemented*
+ ``__cpp_lib_coroutine`` ``201902L``
------------------------------------------------- -----------------
``__cpp_lib_destroying_delete`` ``201806L``
------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 4f80bae0ef1b5..49156fbb9aa96 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -292,7 +292,7 @@
"`3388 <https://wg21.link/LWG3388>`__","``view``\ iterator types have ill-formed ``<=>``\ operators","Prague","","","|ranges|"
"`3389 <https://wg21.link/LWG3389>`__","A move-only iterator still does not have a ``counted_iterator``\ ","Prague","","","|ranges|"
"`3390 <https://wg21.link/LWG3390>`__","``make_move_iterator()``\ cannot be used to construct a ``move_iterator``\ for a move-only iterator","Prague","","","|ranges|"
-"`3393 <https://wg21.link/LWG3393>`__","Missing/incorrect feature test macro for coroutines","Prague","",""
+"`3393 <https://wg21.link/LWG3393>`__","Missing/incorrect feature test macro for coroutines","Prague","|Complete|","14.0"
"`3395 <https://wg21.link/LWG3395>`__","Definition for three-way comparison needs to be updated (US 152)","Prague","","","|spaceship|"
"`3396 <https://wg21.link/LWG3396>`__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","",""
"`3397 <https://wg21.link/LWG3397>`__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","","","|ranges|"
diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index e68f525996eec..e42e06058cac8 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -9,7 +9,7 @@ Section,Description,Dependencies,Assignee,Complete
| `weak_order <https://reviews.llvm.org/D107036>`_
| `partial_order <https://reviews.llvm.org/D107036>`_",None,Arthur O'Dwyer,|In Progress|
| `[alg.three.way] <https://wg21.link/alg.three.way>`_,| `lexicographical_compare_three_way <https://reviews.llvm.org/D80902>`_,[comparisons.three.way],Christopher Di Bella,|In Progress|
-| `[coroutine.handle.compare] <https://wg21.link/coroutine.handle.compare>`_,| coroutine_handle,[comparisons.three.way],Unassigned,|Not Started|
+| `[coroutine.handle.compare] <https://wg21.link/coroutine.handle.compare>`_,| coroutine_handle,[comparisons.three.way],Unassigned,|Complete|
| `[pairs.spec] <https://wg21.link/pairs.spec>`_,| `pair <https://reviews.llvm.org/D107721>`_,[expos.only.func],Kent Ross,|Complete|
| `[syserr.errcat.nonvirtuals] <https://wg21.link/syserr.errcat.nonvirtuals>`_,| error_category,[comparisons.three.way],Unassigned,|Not Started|
| `[syserr.compare] <https://wg21.link/syserr.compare>`_,"| error_code
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index fe03e2507870b..b13b8e138eb21 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -131,6 +131,10 @@ set(files
__concepts/swappable.h
__concepts/totally_ordered.h
__config
+ __coroutine/coroutine_handle.h
+ __coroutine/coroutine_traits.h
+ __coroutine/noop_coroutine_handle.h
+ __coroutine/trivial_awaitables.h
__debug
__errc
__format/format_arg.h
@@ -323,6 +327,7 @@ set(files
complex.h
concepts
condition_variable
+ coroutine
csetjmp
csignal
cstdarg
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 0c6839025bca4..779a33c6e2d97 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1273,8 +1273,8 @@ extern "C" _LIBCPP_FUNC_VIS void __sanitizer_annotate_contiguous_container(
#define _LIBCPP_ENABLE_CXX20_REMOVED_TYPE_TRAITS
#endif // _LIBCPP_ENABLE_CXX20_REMOVED_FEATURES
-#if !defined(__cpp_coroutines) || __cpp_coroutines < 201703L
-#define _LIBCPP_HAS_NO_COROUTINES
+#if !defined(__cpp_impl_coroutine) || __cpp_impl_coroutine < 201902L
+#define _LIBCPP_HAS_NO_CXX20_COROUTINES
#endif
#if !defined(__cpp_impl_three_way_comparison) || __cpp_impl_three_way_comparison < 201907L
diff --git a/libcxx/include/__coroutine/coroutine_handle.h b/libcxx/include/__coroutine/coroutine_handle.h
new file mode 100644
index 0000000000000..bddd45e72e21a
--- /dev/null
+++ b/libcxx/include/__coroutine/coroutine_handle.h
@@ -0,0 +1,229 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_COROUTINE_HANDLE_H
+#define _LIBCPP___COROUTINE_COROUTINE_HANDLE_H
+
+#include <__config>
+#include <__debug>
+#include <__functional/hash.h>
+#include <__memory/addressof.h>
+#include <compare>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CXX20_COROUTINES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// [coroutine.handle]
+template <class _Promise = void>
+struct _LIBCPP_TEMPLATE_VIS coroutine_handle;
+
+template <>
+struct _LIBCPP_TEMPLATE_VIS coroutine_handle<void> {
+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<void*>()(__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 <class _Promise>
+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 <class _Tp>
+struct hash<coroutine_handle<_Tp>> {
+ _LIBCPP_HIDE_FROM_ABI
+ size_t operator()(const coroutine_handle<_Tp>& __v) const noexcept { return hash<void*>()(__v.address()); }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CXX20_COROUTINES)
+
+#endif // _LIBCPP___COROUTINE_COROUTINE_HANDLE_H
diff --git a/libcxx/include/__coroutine/coroutine_traits.h b/libcxx/include/__coroutine/coroutine_traits.h
new file mode 100644
index 0000000000000..bfa69552bd796
--- /dev/null
+++ b/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_COROUTINE_TRAITS_H
+#define _LIBCPP___COROUTINE_COROUTINE_TRAITS_H
+
+#include <__config>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CXX20_COROUTINES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// [coroutine.traits]
+// [coroutine.traits.primary]
+// The header <coroutine> 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<R, ArgTypes...>
+// has the following publicly accessible memebr:
+//
+// using promise_type = typename R::promise_type;
+//
+// Otherwise, coroutine_traits<R, ArgTypes...> has no members.
+template <class _Tp, class = void>
+struct __coroutine_traits_sfinae {};
+
+template <class _Tp>
+struct __coroutine_traits_sfinae<
+ _Tp, typename __void_t<typename _Tp::promise_type>::type>
+{
+ using promise_type = typename _Tp::promise_type;
+};
+
+template <class _Ret, class... _Args>
+struct coroutine_traits
+ : public __coroutine_traits_sfinae<_Ret>
+{
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // __LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CXX20_COROUTINES)
+
+#endif // _LIBCPP___COROUTINE_COROUTINE_TRAITS_H
diff --git a/libcxx/include/__coroutine/noop_coroutine_handle.h b/libcxx/include/__coroutine/noop_coroutine_handle.h
new file mode 100644
index 0000000000000..9dbf21aac5e6b
--- /dev/null
+++ b/libcxx/include/__coroutine/noop_coroutine_handle.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_COROUTINE_HANDLE_H
+#define _LIBCPP___COROUTINE_NOOP_COROUTINE_HANDLE_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_CXX20_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<noop_coroutine_promise> {
+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<noop_coroutine_promise*>(
+ __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_promise> noop_coroutine() noexcept;
+
+ _LIBCPP_HIDE_FROM_ABI coroutine_handle() noexcept {
+ this->__handle_ = __builtin_coro_noop();
+ }
+
+ void* __handle_ = nullptr;
+};
+
+using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+
+// [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_CXX20_COROUTINES)
+
+#endif // _LIBCPP___COROUTINE_NOOP_COROUTINE_HANDLE_H
diff --git a/libcxx/include/__coroutine/trivial_awaitables.h b/libcxx/include/__coroutine/trivial_awaitables.h
new file mode 100644
index 0000000000000..c434f83b78bbb
--- /dev/null
+++ b/libcxx/include/__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_CXX20_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_CXX20_COROUTINES)
+
+#endif // __LIBCPP___COROUTINE_TRIVIAL_AWAITABLES_H
diff --git a/libcxx/include/coroutine b/libcxx/include/coroutine
new file mode 100644
index 0000000000000..4e140ab3fed71
--- /dev/null
+++ b/libcxx/include/coroutine
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_COROUTINE
+#define _LIBCPP_COROUTINE
+
+/**
+ coroutine synopsis
+
+namespace std {
+// [coroutine.traits]
+template <class R, class... ArgTypes>
+ struct coroutine_traits;
+// [coroutine.handle]
+template <class Promise = void>
+ 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 <class T> struct hash;
+template <class P> struct hash<coroutine_handle<P>>;
+// [coroutine.noop]
+struct noop_coroutine_promise;
+template<> struct coroutine_handle<noop_coroutine_promise>;
+using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+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/noop_coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <version>
+
+#ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
+#pragma GCC system_header
+#endif
+
+#endif // _LIBCPP_COROUTINE
diff --git a/libcxx/include/experimental/__config b/libcxx/include/experimental/__config
index f85426d050d1e..9c16474031da5 100644
--- a/libcxx/include/experimental/__config
+++ b/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_HAS_NO_EXPERIMENTAL_COROUTINES
+#endif
+
#define _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES \
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL inline namespace coroutines_v1 {
diff --git a/libcxx/include/experimental/coroutine b/libcxx/include/experimental/coroutine
index 54ec74b9f9844..bac6062c3ffc2 100644
--- a/libcxx/include/experimental/coroutine
+++ b/libcxx/include/experimental/coroutine
@@ -57,7 +57,7 @@ template <class P> struct hash<coroutine_handle<P>>;
#pragma GCC system_header
#endif
-#ifdef _LIBCPP_HAS_NO_COROUTINES
+#ifdef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES
# if defined(_LIBCPP_WARNING)
_LIBCPP_WARNING("<experimental/coroutine> cannot be used with this compiler")
# else
@@ -65,7 +65,7 @@ template <class P> struct hash<coroutine_handle<P>>;
# endif
#endif
-#ifndef _LIBCPP_HAS_NO_COROUTINES
+#ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES
@@ -329,6 +329,6 @@ struct hash<_VSTD_CORO::coroutine_handle<_Tp> > {
_LIBCPP_END_NAMESPACE_STD
-#endif // !defined(_LIBCPP_HAS_NO_COROUTINES)
+#endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES)
#endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 2f96f9aa96ca2..39b8a662d3a88 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -417,6 +417,19 @@ module std [system] {
header "condition_variable"
export *
}
+ module coroutine {
+ requires coroutines
+ header "coroutine"
+ export compare
+ export *
+
+ module __coroutine {
+ module coroutine_handle { private header "__coroutine/coroutine_handle.h" }
+ module coroutine_traits { private header "__coroutine/coroutine_traits.h" }
+ module trivial_awaitables { private header "__coroutine/trivial_awaitables.h" }
+ module noop_coroutine_handle { private header "__coroutine/noop_coroutine_handle.h" }
+ }
+ }
module deque {
header "deque"
export initializer_list
diff --git a/libcxx/include/version b/libcxx/include/version
index a2007e83740a3..a4809eb573b41 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -305,7 +305,7 @@ __cpp_lib_void_t 201411L <type_traits>
# 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
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_handle.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_handle.module.verify.cpp
new file mode 100644
index 0000000000000..e78f90e646020
--- /dev/null
+++ b/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>
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_traits.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/coroutine_traits.module.verify.cpp
new file mode 100644
index 0000000000000..4af6ec50ba038
--- /dev/null
+++ b/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>
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/noop_coroutine_handle.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/noop_coroutine_handle.module.verify.cpp
new file mode 100644
index 0000000000000..6dc58c4515128
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/noop_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/noop_coroutine_handle.h'}}
+#include <__coroutine/noop_coroutine_handle.h>
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/trivial_awaitables.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/coroutine/trivial_awaitables.module.verify.cpp
new file mode 100644
index 0000000000000..0f5e6b1229df0
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/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/trivial_awaitables.h'}}
+#include <__coroutine/trivial_awaitables.h>
diff --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp
index 05286fb72cf02..a7ff19fb29e73 100644
--- a/libcxx/test/libcxx/double_include.sh.cpp
+++ b/libcxx/test/libcxx/double_include.sh.cpp
@@ -62,6 +62,9 @@
#include <complex.h>
#include <concepts>
#include <condition_variable>
+#ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES
+# include <coroutine>
+#endif
#include <csetjmp>
#include <csignal>
#include <cstdarg>
@@ -205,7 +208,7 @@
// experimental headers
#if __cplusplus >= 201103L
# include <experimental/algorithm>
-# if defined(__cpp_coroutines)
+# ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES
# include <experimental/coroutine>
# endif
# include <experimental/deque>
diff --git a/libcxx/test/libcxx/inclusions/coroutine.inclusions.compile.pass.cpp b/libcxx/test/libcxx/inclusions/coroutine.inclusions.compile.pass.cpp
new file mode 100644
index 0000000000000..d1ac6f3933a59
--- /dev/null
+++ b/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
+
+// <coroutine>
+
+// Test that <coroutine> includes all the other headers it's supposed to.
+
+#include <coroutine>
+#include "test_macros.h"
+
+#if !defined(_LIBCPP_COROUTINE)
+ # error "<coroutine> was expected to define _LIBCPP_COROUTINE"
+#endif
+#if TEST_STD_VER > 17 && !defined(_LIBCPP_COMPARE)
+ # error "<coroutine> should include <compare> in C++20 and later"
+#endif
diff --git a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
index d1e2b90ab7f2e..896076a73c75f 100644
--- a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
+++ b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
@@ -88,6 +88,10 @@ TEST_MACROS();
TEST_MACROS();
#include <condition_variable>
TEST_MACROS();
+#ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES
+# include <coroutine>
+TEST_MACROS();
+#endif
#include <csetjmp>
TEST_MACROS();
#include <csignal>
@@ -321,7 +325,7 @@ TEST_MACROS();
#if __cplusplus >= 201103L
# include <experimental/algorithm>
TEST_MACROS();
-# if defined(__cpp_coroutines)
+# ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES
# include <experimental/coroutine>
TEST_MACROS();
# endif
diff --git a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
index 9c80b401dca2b..4be6d4e640be4 100644
--- a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
+++ b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
@@ -57,6 +57,9 @@
#include <complex.h>
#include <concepts>
#include <condition_variable>
+#ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES
+# include <coroutine>
+#endif
#include <csetjmp>
#include <csignal>
#include <cstdarg>
@@ -200,7 +203,7 @@
// experimental headers
#if __cplusplus >= 201103L
# include <experimental/algorithm>
-# if defined(__cpp_coroutines)
+# ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES
# include <experimental/coroutine>
# endif
# include <experimental/deque>
diff --git a/libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp b/libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp
index 2f2bf0175226e..2a05e8ee21d60 100644
--- a/libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp
+++ b/libcxx/test/std/experimental/language.support/support.coroutines/end.to.end/generator.pass.cpp
@@ -16,19 +16,76 @@
#include <cassert>
#include "test_macros.h"
-#include "coroutine_types.h"
-using namespace std::experimental;
+
+template <typename Ty> 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<promise_type> _Coro;
+ bool _Done;
+
+ iterator(std::experimental::coroutine_handle<promise_type> 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<promise_type>::from_promise(*p)) {}
+
+ std::experimental::coroutine_handle<promise_type> 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 @@ struct minig {
private:
explicit minig(promise_type *p)
- : p(coroutine_handle<promise_type>::from_promise(*p)) {}
+ : p(std::experimental::coroutine_handle<promise_type>::from_promise(*p)) {}
- coroutine_handle<promise_type> p;
+ std::experimental::coroutine_handle<promise_type> p;
};
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp
new file mode 100644
index 0000000000000..f81b1dd612877
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.capacity/operator_bool.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// constexpr explicit operator bool() const noexcept
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+constexpr bool do_test() {
+ static_assert(std::is_nothrow_constructible<bool, C>::value, "");
+ static_assert(!std::is_convertible<C, bool>::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<std::coroutine_handle<>>();
+ do_test<std::coroutine_handle<int>>();
+ static_assert(do_test<std::coroutine_handle<>>());
+ static_assert(do_test<std::coroutine_handle<int>>());
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.pass.cpp
new file mode 100644
index 0000000000000..104af4ef305d4
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/equal_comp.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept;
+
+#include <coroutine>
+#include <type_traits>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+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<int *, int *> const TestCases[] = {
+ {nullptr, nullptr},
+ {&i, &i},
+ {nullptr, &i},
+ {&i, nullptr}
+ };
+ for (auto& TC : TestCases) {
+ do_test<std::coroutine_handle<>>(TC.first, TC.second);
+ do_test<std::coroutine_handle<int>>(TC.first, TC.second);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp
new file mode 100644
index 0000000000000..c011ab0e7b7bf
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.compare/less_comp.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
+
+#include <coroutine>
+#include <type_traits>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+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<int *, int *> const TestCases[] = {
+ {nullptr, nullptr},
+ {&i, &i},
+ {nullptr, &i},
+ {&i, nullptr}
+ };
+ for (auto& TC : TestCases) {
+ do_test<std::coroutine_handle<>>(TC.first, TC.second);
+ do_test<std::coroutine_handle<int>>(TC.first, TC.second);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp
new file mode 100644
index 0000000000000..cd98e7814bea2
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.completion/done.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// bool done() const
+
+#include <coroutine>
+#include <type_traits>
+#include <memory>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class Promise>
+void do_test(std::coroutine_handle<Promise> 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<int>{});
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp
new file mode 100644
index 0000000000000..673cf422c9551
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/assign.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// coroutine_handle& operator=(nullptr_t) noexcept
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+void do_test() {
+ int dummy = 42;
+ void* dummy_h = &dummy;
+ {
+ static_assert(std::is_nothrow_assignable<C&, std::nullptr_t>::value, "");
+ static_assert(!std::is_assignable<C&, void*>::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<std::coroutine_handle<>>();
+ do_test<std::coroutine_handle<int>>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.pass.cpp
new file mode 100644
index 0000000000000..233f92258da5b
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.con/construct.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// constexpr coroutine_handle() noexcept
+// constexpr coroutine_handle(nullptr_t) noexcept
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+constexpr bool do_test() {
+ static_assert(std::is_nothrow_constructible<C>::value, "");
+ static_assert(std::is_nothrow_constructible<C, std::nullptr_t>::value, "");
+ {
+ C c;
+ assert(c.address() == nullptr);
+ }
+ {
+ C c = C(nullptr);
+ assert(c.address() == nullptr);
+ }
+ return true;
+}
+
+int main(int, char**)
+{
+ do_test<std::coroutine_handle<>>();
+ do_test<std::coroutine_handle<int>>();
+ static_assert(do_test<std::coroutine_handle<>>());
+ static_assert(do_test<std::coroutine_handle<int>>());
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.pass.cpp
new file mode 100644
index 0000000000000..d41b6e1b14357
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/address.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// constexpr void* address() const noexcept
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+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<std::coroutine_handle<>>();
+ do_test<std::coroutine_handle<int>>();
+ static_assert(do_test<std::coroutine_handle<>>());
+ static_assert(do_test<std::coroutine_handle<int>>());
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.pass.cpp
new file mode 100644
index 0000000000000..05c68b57d5315
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.export/from_address.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// static coroutine_handle from_address(void*) noexcept
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class C>
+void do_test() {
+ {
+ C c = C::from_address(nullptr);
+ static_assert(noexcept(C::from_address(nullptr)), "");
+ static_assert(std::is_same<decltype(C::from_address(nullptr)), C>::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<std::coroutine_handle<>>();
+ do_test<std::coroutine_handle<int>>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.pass.cpp
new file mode 100644
index 0000000000000..44dd4715552aa
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.hash/hash.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// namespace std {
+// template <class P> struct hash<coroutine_handle<P>>;
+// }
+
+#include <coroutine>
+#include <type_traits>
+#include <memory>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+#include <functional>
+
+#include "test_macros.h"
+
+template <class C>
+void do_test(int *LHSVal, int *RHSVal) {
+ const size_t ExpectLHS = std::hash<void*>{}((LHSVal));
+ const size_t ExpectRHS = std::hash<void*>{}((RHSVal));
+ const C LHS = C::from_address((LHSVal));
+ const C RHS = C::from_address((RHSVal));
+ const std::hash<C> 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<C>{}(LHS));
+ }
+}
+
+int main(int, char**)
+{
+ int i, j;
+ std::pair<int *, int *> const TestCases[] = {
+ {nullptr, nullptr},
+ {nullptr, &i},
+ {&i, &i},
+ {&i, &j}
+ };
+ for (auto& TC : TestCases) {
+ do_test<std::coroutine_handle<>>(TC.first, TC.second);
+ do_test<std::coroutine_handle<int>>(TC.first, TC.second);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp
new file mode 100644
index 0000000000000..36a567983b280
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.noop/noop_coroutine.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+// struct noop_coroutine_promise;
+// using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
+// noop_coroutine_handle noop_coroutine() noexcept;
+
+#include <coroutine>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#if __has_builtin(__builtin_coro_noop)
+
+static_assert(std::is_same<std::coroutine_handle<std::noop_coroutine_promise>, std::noop_coroutine_handle>::value, "");
+static_assert(std::is_same<decltype(std::noop_coroutine()), std::noop_coroutine_handle>::value, "");
+
+// template <> struct coroutine_handle<noop_coroutine_promise> : 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)
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.pass.cpp
new file mode 100644
index 0000000000000..d240b64cc9fbd
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.prom/promise.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise>
+// struct coroutine_handle<Promise>;
+
+// Promise& promise() const
+
+#include <coroutine>
+#include <type_traits>
+#include <memory>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#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<const T> can be created from a const
+ // promise_type and that it represents the same coroutine as
+ // coroutine_handle<T>
+ using CH = std::coroutine_handle<promise_type>;
+ using CCH = std::coroutine_handle<const promise_type>;
+ 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 <class Promise>
+void do_test(std::coroutine_handle<Promise>&& 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<int>{});
+ do_test(std::coroutine_handle<const int>{});
+ do_runtime_test();
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp
new file mode 100644
index 0000000000000..ff87a0391a0d9
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/destroy.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// void destroy() const
+
+#include <coroutine>
+#include <type_traits>
+#include <memory>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class H>
+auto has_destroy_imp(H&& h, int) -> decltype(h.destroy(), std::true_type{});
+template <class H>
+auto has_destroy_imp(H&&, long) -> std::false_type;
+
+template <class H>
+constexpr bool has_destroy() {
+ return decltype(has_destroy_imp(std::declval<H>(), 0))::value;
+}
+
+template <class Promise>
+void do_test(std::coroutine_handle<Promise>&& H) {
+ using HType = std::coroutine_handle<Promise>;
+ // FIXME Add a runtime test
+ {
+ ASSERT_SAME_TYPE(decltype(H.destroy()), void);
+ LIBCPP_ASSERT_NOT_NOEXCEPT(H.destroy());
+ static_assert(has_destroy<HType&>(), "");
+ static_assert(has_destroy<HType&&>(), "");
+ }
+ {
+ static_assert(has_destroy<HType const&>(), "");
+ static_assert(has_destroy<HType const&&>(), "");
+ }
+}
+
+int main(int, char**)
+{
+ do_test(std::coroutine_handle<>{});
+ do_test(std::coroutine_handle<int>{});
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp
new file mode 100644
index 0000000000000..91390d43359ed
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/coroutine.handle.resumption/resume.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// <coroutine>
+
+// template <class Promise = void>
+// struct coroutine_handle;
+
+// void operator()() const
+// void resume() const
+
+#include <coroutine>
+#include <type_traits>
+#include <memory>
+#include <utility>
+#include <cstdint>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class H>
+auto has_resume_imp(H&& h, int) -> decltype(h.resume(), std::true_type{});
+template <class H>
+auto has_resume_imp(H&&, long) -> std::false_type;
+
+template <class H>
+constexpr bool has_resume() {
+ return decltype(has_resume_imp(std::declval<H>(), 0))::value;
+}
+
+
+template <class H>
+auto has_call_operator_imp(H&& h, int) -> decltype(h(), std::true_type{});
+template <class H>
+auto has_call_operator_imp(H&&, long) -> std::false_type;
+
+template <class H>
+constexpr bool has_call_operator() {
+ return decltype(has_call_operator_imp(std::declval<H>(), 0))::value;
+}
+
+template <class Promise>
+void do_test(std::coroutine_handle<Promise>&& H) {
+ using HType = std::coroutine_handle<Promise>;
+ // 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<HType&>(), "");
+ static_assert(has_resume<HType&&>(), "");
+ static_assert(has_call_operator<HType&>(), "");
+ static_assert(has_call_operator<HType&&>(), "");
+ }
+ {
+ static_assert(has_resume<HType const&>(), "");
+ static_assert(has_resume<HType const&&>(), "");
+ static_assert(has_call_operator<HType const&>(), "");
+ static_assert(has_call_operator<HType const&&>(), "");
+ }
+}
+
+int main(int, char**)
+{
+ do_test(std::coroutine_handle<>{});
+ do_test(std::coroutine_handle<int>{});
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp
new file mode 100644
index 0000000000000..2ad5224109e74
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.handle/void_handle.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <type_traits>
+
+#include "test_macros.h"
+
+struct A {
+ using promise_type = A*;
+};
+
+struct B {};
+struct C {};
+
+template <>
+struct std::coroutine_traits<A, int> {
+ using promise_type = int*;
+};
+template <class ...Args>
+struct std::coroutine_traits<B, Args...> {
+ using promise_type = B*;
+};
+template <>
+struct std::coroutine_traits<C> {
+ using promise_type = void;
+};
+
+template <class Expect, class T, class ...Args>
+void check_type() {
+ using P = typename std::coroutine_traits<T, Args...>::promise_type ;
+ static_assert(std::is_same<P, Expect>::value, "");
+};
+
+int main(int, char**)
+{
+ check_type<A*, A>();
+ check_type<int*, A, int>();
+ check_type<B*, B>();
+ check_type<void, C>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.pass.cpp
new file mode 100644
index 0000000000000..2eb9a6c457d57
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.traits/promise_type.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
+// UNSUPPORTED: libcpp-no-coroutines
+
+#include <coroutine>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T, class = typename T::promise_type>
+constexpr bool has_promise_type(int) { return true; }
+template <class>
+constexpr bool has_promise_type(long) { return false; }
+template <class T>
+constexpr bool has_promise_type() { return has_promise_type<T>(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<A, int> {
+ using promise_type = int*;
+};
+template <class ...Args>
+struct std::coroutine_traits<B, Args...> {
+ using promise_type = B*;
+};
+template <>
+struct std::coroutine_traits<C> {
+ using promise_type = void;
+};
+
+template <class Expect, class T, class ...Args>
+void check_type() {
+ using Traits = std::coroutine_traits<T, Args...>;
+ static_assert(has_promise_type<Traits>(), "");
+ static_assert(std::is_same<typename Traits::promise_type, Expect>::value, "");
+}
+
+template <class T, class ...Args>
+void check_no_type() {
+ using Traits = std::coroutine_traits<T, Args...>;
+ static_assert(!has_promise_type<Traits>(), "");
+}
+
+int main(int, char**)
+{
+ {
+ check_type<A*, A>();
+ check_type<int*, A, int>();
+ check_type<B*, B>();
+ check_type<void, C>();
+ }
+ {
+ check_no_type<D>();
+ check_no_type<E>();
+ check_no_type<C, int>();
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp
new file mode 100644
index 0000000000000..31b7e0ec7f79a
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_always.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+#include <utility>
+
+#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<class T>
+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<decltype(s.await_ready()), bool>::value, "");
+ assert(s.await_ready() == false);
+ assert(cs.await_ready() == false);
+ }
+ {
+ ASSERT_NOEXCEPT(s.await_suspend(h));
+ static_assert(std::is_same<decltype(s.await_suspend(h)), void>::value, "");
+ s.await_suspend(h);
+ cs.await_suspend(h);
+ }
+ {
+ ASSERT_NOEXCEPT(s.await_resume());
+ static_assert(std::is_same<decltype(s.await_resume()), void>::value, "");
+ s.await_resume();
+ cs.await_resume();
+ }
+ {
+ static_assert(std::is_nothrow_default_constructible<std::suspend_always>::value, "");
+ static_assert(std::is_nothrow_copy_constructible<std::suspend_always>::value, "");
+ static_assert(std::is_nothrow_move_constructible<std::suspend_always>::value, "");
+ static_assert(std::is_nothrow_copy_assignable<std::suspend_always>::value, "");
+ static_assert(std::is_nothrow_move_assignable<std::suspend_always>::value, "");
+ static_assert(std::is_trivially_copyable<std::suspend_always>::value, "");
+ static_assert(check_suspend_constexpr(), "");
+ }
+ {
+ static_assert(test_trivial_awaitable_constexpr<std::suspend_always>(false));
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp b/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp
new file mode 100644
index 0000000000000..83439299572f0
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/coroutine.trivial.awaitables/suspend_never.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <type_traits>
+#include <cassert>
+#include <utility>
+
+#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<class T>
+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<decltype(s.await_ready()), bool>::value, "");
+ assert(s.await_ready() == true);
+ assert(cs.await_ready() == true);
+ }
+ {
+ LIBCPP_STATIC_ASSERT(noexcept(s.await_suspend(h)), "");
+ static_assert(std::is_same<decltype(s.await_suspend(h)), void>::value, "");
+ s.await_suspend(h);
+ cs.await_suspend(h);
+ }
+ {
+ LIBCPP_STATIC_ASSERT(noexcept(s.await_resume()), "");
+ static_assert(std::is_same<decltype(s.await_resume()), void>::value, "");
+ s.await_resume();
+ cs.await_resume();
+ }
+ {
+ static_assert(std::is_nothrow_default_constructible<std::suspend_never>::value, "");
+ static_assert(std::is_nothrow_copy_constructible<std::suspend_never>::value, "");
+ static_assert(std::is_nothrow_move_constructible<std::suspend_never>::value, "");
+ static_assert(std::is_nothrow_copy_assignable<std::suspend_never>::value, "");
+ static_assert(std::is_nothrow_move_assignable<std::suspend_never>::value, "");
+ static_assert(std::is_trivially_copyable<std::suspend_never>::value, "");
+ static_assert(check_suspend_constexpr(), "");
+ }
+ {
+ static_assert(test_trivial_awaitable_constexpr<std::suspend_never>(true));
+ }
+ {
+ // suppress unused warnings for the global constexpr test variable
+ ((void)constexpr_sn);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp
new file mode 100644
index 0000000000000..67b9e49f3802c
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/await_result.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() {
+ std::coroutine_handle<promise_type>{};
+ 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 <typename F> void await_suspend(F) {}
+};
+
+
+struct A {
+ ~A() {}
+ bool await_ready() { return true; }
+ int await_resume() { return 42; }
+ template <typename F> 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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp
new file mode 100644
index 0000000000000..893b524181767
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/bool_await_suspend.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// See https://llvm.org/PR33271
+// UNSUPPORTED: ubsan
+
+#include <coroutine>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() {
+ return std::coroutine_handle<promise_type>::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<promise_type> hh) : h(hh) {}
+ std::coroutine_handle<promise_type> h;
+};
+
+struct NoSuspend {
+ bool await_ready() { return false; }
+ void await_resume() {}
+ template <typename F> bool await_suspend(F) { return false; }
+};
+
+struct DoSuspend {
+ bool await_ready() { return false; }
+ void await_resume() {}
+ template <typename F> 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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp
new file mode 100644
index 0000000000000..7db7ebcea1677
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/expected.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <cassert>
+#include <memory>
+
+#include "test_macros.h"
+
+struct error_tag { };
+
+template <typename T, typename Error = int>
+struct expected {
+
+ struct Data {
+ Data() : val(), error() { }
+ Data(T v, Error e) : val(v), error(e) { }
+ T val;
+ Error error;
+ };
+ std::shared_ptr<Data> data;
+
+ expected(T val) : data(std::make_shared<Data>(val, Error())) {}
+ expected(error_tag, Error error) : data(std::make_shared<Data>(T(), error)) {}
+ expected(std::shared_ptr<Data> p) : data(p) {}
+
+ struct promise_type {
+ std::shared_ptr<Data> data;
+ expected get_return_object() { data = std::make_shared<Data>(); 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<promise_type> h) {
+ h.promise().data->error = data->error;
+ h.destroy();
+ }
+
+ T const& value() { return data->val; }
+ Error const& error() { return data->error; }
+};
+
+expected<int> g() { return {0}; }
+expected<int> h() { return {error_tag{}, 42}; }
+
+extern "C" void print(int);
+
+bool f1_started, f1_resumed = false;
+expected<int> f1() {
+ f1_started = true;
+ (void)(co_await g());
+ f1_resumed = true;
+ co_return 100;
+}
+
+bool f2_started, f2_resumed = false;
+expected<int> 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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp
new file mode 100644
index 0000000000000..d392e711dd9d2
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/fullexpr-dtor.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <cassert>
+
+#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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp
new file mode 100644
index 0000000000000..4b02f2a8b4c8b
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/generator.pass.cpp
@@ -0,0 +1,161 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+// See https://llvm.org/PR33271
+// UNSUPPORTED: ubsan
+
+#include <coroutine>
+#include <vector>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <typename Ty> 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<promise_type> Coro_;
+ bool Done_;
+
+ iterator(std::coroutine_handle<promise_type> 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<promise_type>::from_promise(*promise)) {}
+
+ std::coroutine_handle<promise_type> 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<promise_type>::from_promise(*promise)) {}
+
+ std::coroutine_handle<promise_type> p;
+};
+
+
+minig mini_count(int n) {
+ for (int i = 0; i < n; i++) {
+ co_yield i;
+ }
+}
+
+generator<int> count(int n) {
+ for (int i = 0; i < n; ++i)
+ co_yield i;
+}
+
+generator<int> range(int from, int n) {
+ for (int i = from; i < n; ++i)
+ co_yield i;
+}
+
+void test_count() {
+ const std::vector<int> expect = {0, 1, 2, 3, 4};
+ std::vector<int> 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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp
new file mode 100644
index 0000000000000..22380af50e0d7
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/go.pass.cpp
@@ -0,0 +1,174 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <cassert>
+#include <coroutine>
+#include <memory>
+
+#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 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<goroutine::promise_type> 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;
+channel c[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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp
new file mode 100644
index 0000000000000..fde3096885bc5
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/multishot_func.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <cassert>
+
+#include "test_macros.h"
+
+// This file tests, multishot, movable std::function like thing using coroutine
+// for compile-time type erasure and unerasure.
+template <typename R> 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 <typename F>
+ 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 <typename F> static func Create(F f) {
+ for (;;) {
+ co_yield f;
+ }
+ }
+
+ template <typename F> func(F f) : func(Create(f)) {}
+
+ ~func() {
+ if (h)
+ h.destroy();
+ }
+
+private:
+ func(promise_type *promise)
+ : h(std::coroutine_handle<promise_type>::from_promise(*promise)) {}
+ std::coroutine_handle<promise_type> h;
+};
+
+int Do(int acc, int n, func<int> 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;
+}
diff --git a/libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp b/libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp
new file mode 100644
index 0000000000000..68c11d4f8554e
--- /dev/null
+++ b/libcxx/test/std/language.support/support.coroutines/end.to.end/oneshot_func.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-no-coroutines
+
+#include <coroutine>
+#include <vector>
+#include <cassert>
+
+#include "test_macros.h"
+
+// This file tests, one shot, movable std::function like thing using coroutine
+// for compile-time type erasure and unerasure.
+
+template <typename R> 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 <typename F> static func Create(F f) { co_return f(); }
+
+ template <typename F> func(F f) : func(Create(f)) {}
+
+ ~func() {
+ if (h)
+ h.destroy();
+ }
+
+private:
+ func(promise_type *promise)
+ : h(std::coroutine_handle<promise_type>::from_promise(*promise)) {}
+ std::coroutine_handle<promise_type> h;
+};
+
+int main(int, char**) {
+ func<int> f = func<int>::Create([]() { return 44; });
+ assert(f() == 44);
+
+ return 0;
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/coroutine.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/coroutine.version.pass.cpp
new file mode 100644
index 0000000000000..1527fa3d3deef
--- /dev/null
+++ b/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
+
+// <coroutine>
+
+// Test the feature test macros defined by <coroutine>
+
+/* Constant Value
+ __cpp_lib_coroutine 201902L [C++20]
+*/
+
+#include <coroutine>
+#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; }
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index 784e6279158ef..846abbd97bee9 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/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
diff --git a/libcxx/test/support/coroutine_types.h b/libcxx/test/support/coroutine_types.h
deleted file mode 100644
index 04b27a161eb34..0000000000000
--- a/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 <experimental/coroutine>
-
-template <typename Ty> 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<promise_type> _Coro;
- bool _Done;
-
- iterator(std::experimental::coroutine_handle<promise_type> 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<promise_type>::from_promise(*p)) {}
-
- std::experimental::coroutine_handle<promise_type> p;
-};
-
-#endif // SUPPORT_COROUTINE_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 4853ec43f9332..d7c9ad94441d7 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -258,7 +258,6 @@ def add_version_header(tc):
"name": "__cpp_lib_coroutine",
"values": { "c++20": 201902 },
"headers": ["coroutine"],
- "unimplemented": True,
}, {
"name": "__cpp_lib_destroying_delete",
"values": { "c++20": 201806 },
diff --git a/libcxx/utils/generate_header_inclusion_tests.py b/libcxx/utils/generate_header_inclusion_tests.py
index 7b5a2db0ed5c1..842b048e398c8 100755
--- a/libcxx/utils/generate_header_inclusion_tests.py
+++ b/libcxx/utils/generate_header_inclusion_tests.py
@@ -29,7 +29,7 @@ def get_libcxx_paths():
"chrono": ["compare"],
"cinttypes": ["cstdint"],
"complex.h": ["complex"],
- # TODO "coroutine": ["compare"],
+ "coroutine": ["compare"],
"deque": ["compare", "initializer_list"],
"filesystem": ["compare"],
"forward_list": ["compare", "initializer_list"],
diff --git a/libcxx/utils/generate_header_tests.py b/libcxx/utils/generate_header_tests.py
index 19e7278320a8b..cd78b4a7c1a25 100755
--- a/libcxx/utils/generate_header_tests.py
+++ b/libcxx/utils/generate_header_tests.py
@@ -55,7 +55,8 @@ def get_libcxx_paths():
"cwchar": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"],
"wchar.h": ["ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS"],
- "experimental/coroutine": ["if defined(__cpp_coroutines)"],
+ "experimental/coroutine": ["ifndef _LIBCPP_HAS_NO_EXPERIMENTAL_COROUTINES"],
+ "coroutine": ["ifndef _LIBCPP_HAS_NO_CXX20_COROUTINES"],
"experimental/regex": ["ifndef _LIBCPP_HAS_NO_LOCALIZATION"],
}
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 9bde800d64588..cc2f5dafb2c89 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -38,6 +38,7 @@
Feature(name='fdelayed-template-parsing', when=lambda cfg: hasCompileFlag(cfg, '-fdelayed-template-parsing')),
Feature(name='libcpp-no-structured-bindings', when=lambda cfg: '__cpp_structured_bindings' not in featureTestMacros(cfg)),
Feature(name='libcpp-no-concepts', when=lambda cfg: featureTestMacros(cfg).get('__cpp_concepts', 0) < 201907),
+ Feature(name='libcpp-no-coroutines', when=lambda cfg: featureTestMacros(cfg).get('__cpp_impl_coroutine', 0) < 201902),
Feature(name='has-fobjc-arc', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc') and
sys.platform.lower().strip() == 'darwin'), # TODO: this doesn't handle cross-compiling to Apple platforms.
Feature(name='objective-c++', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc')),
More information about the libcxx-commits
mailing list