[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