[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
This commit is contained in:
Chuanqi Xu 2021-11-16 14:05:34 +08:00
parent 1585b13024
commit 2e6ae1d3f2
55 changed files with 2660 additions and 118 deletions
libcxx
docs
include
test
utils

View File

@ -224,7 +224,7 @@ Status
------------------------------------------------- -----------------
``__cpp_lib_constexpr_vector`` *unimplemented*
------------------------------------------------- -----------------
``__cpp_lib_coroutine`` *unimplemented*
``__cpp_lib_coroutine`` ``201902L``
------------------------------------------------- -----------------
``__cpp_lib_destroying_delete`` ``201806L``
------------------------------------------------- -----------------

View File

@ -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|"

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -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

1 Section Description Dependencies Assignee Complete
9 | `[pairs.spec] <https://wg21.link/pairs.spec>`_ | `pair <https://reviews.llvm.org/D107721>`_ [expos.only.func] Kent Ross |Complete|
10 | `[syserr.errcat.nonvirtuals] <https://wg21.link/syserr.errcat.nonvirtuals>`_ | error_category [comparisons.three.way] Unassigned |Not Started|
11 | `[syserr.compare] <https://wg21.link/syserr.compare>`_ | error_code | error_condition None Unassigned |Not Started|
12 | `[tuple.rel] <https://wg21.link/tuple.rel>`_ | `tuple <https://reviews.llvm.org/D108250>`_ [expos.only.func] Kent Ross |Complete|
13 | `[optional.relops] <https://wg21.link/optional.relops>`_ | `[optional.nullops] <https://wg21.link/optional.nullops>`_ | `[optional.comp.with.t] <https://wg21.link/optional.comp.with.t>`_ | optional | nullopt None Kent Ross |In Progress|
14 | `[variant.relops] <https://wg21.link/variant.relops>`_ | `[variant.monostate.relops] <https://wg21.link/variant.monostate.relops>`_ | monostate | variant None Kent Ross |In Progress|
15 | `[unique.ptr.special] <https://wg21.link/unique.ptr.special>`_ | unique_ptr [comparisons.three.way] Unassigned |Not Started|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

52
libcxx/include/coroutine Normal file
View File

@ -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

View File

@ -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 {

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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; }

View File

@ -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

View File

@ -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

View File

@ -258,7 +258,6 @@ feature_test_macros = [ add_version_header(x) for x in [
"name": "__cpp_lib_coroutine",
"values": { "c++20": 201902 },
"headers": ["coroutine"],
"unimplemented": True,
}, {
"name": "__cpp_lib_destroying_delete",
"values": { "c++20": 201806 },

View File

@ -29,7 +29,7 @@ mandatory_inclusions = {
"chrono": ["compare"],
"cinttypes": ["cstdint"],
"complex.h": ["complex"],
# TODO "coroutine": ["compare"],
"coroutine": ["compare"],
"deque": ["compare", "initializer_list"],
"filesystem": ["compare"],
"forward_list": ["compare", "initializer_list"],

View File

@ -55,7 +55,8 @@ header_markup = {
"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"],
}

View File

@ -38,6 +38,7 @@ DEFAULT_FEATURES = [
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')),