Library support for C++20 coroutines (#894)

* Library support for C++20 coroutines

Implements most of <coroutine>. Usefulness of this header is dependent
on a compatible compiler (e.g. Visual Studio 2019 16.8 Preview 1 or
later) that defines `__cpp_impl_coroutine`. With such a compiler
this header exposes the coroutine library support in the `std` namespace
without the need for an `/await` switch.

This implementation is not yet complete:

- noop coroutines are not yet implemented
- symmetric transfer is not yet implemented

The value of `__cpp_lib_coroutine` is defined to a value less than the
Standard-mandated value to represent the incomplete feature.

Co-authored-by: Daniel Marshall <xandan@gmail.com>
This commit is contained in:
Jonathan Emmett 2020-06-17 12:08:37 -04:00 коммит произвёл GitHub
Родитель c64e60bae8
Коммит 0cdf5fbfac
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 197 добавлений и 4 удалений

Просмотреть файл

@ -28,6 +28,7 @@ set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/inc/complex
${CMAKE_CURRENT_LIST_DIR}/inc/concepts
${CMAKE_CURRENT_LIST_DIR}/inc/condition_variable
${CMAKE_CURRENT_LIST_DIR}/inc/coroutine
${CMAKE_CURRENT_LIST_DIR}/inc/csetjmp
${CMAKE_CURRENT_LIST_DIR}/inc/csignal
${CMAKE_CURRENT_LIST_DIR}/inc/cstdalign

Просмотреть файл

@ -48,6 +48,7 @@
#include <compare>
#include <complex>
#include <concepts>
#include <coroutine>
#include <deque>
#include <exception>
#include <filesystem>

169
stl/inc/coroutine Normal file
Просмотреть файл

@ -0,0 +1,169 @@
// coroutine standard header (core)
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#pragma once
#ifndef _COROUTINE_
#define _COROUTINE_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
#pragma message("The contents of <coroutine> are not available with /await.")
#pragma message("Remove /await for standard coroutines or use <experimental/coroutine> for legacy /await support.")
#else // ^^^ /await ^^^ / vvv no /await vvv
#ifndef __cpp_lib_coroutine
#pragma message("The contents of <coroutine> are available only with C++20 or later.")
#else // ^^^ __cpp_lib_coroutine not defined / __cpp_lib_coroutine defined vvv
#ifndef _ALLOW_COROUTINE_ABI_MISMATCH
#pragma detect_mismatch("_COROUTINE_ABI", "2")
#endif // _ALLOW_COROUTINE_ABI_MISMATCH
#include <compare>
#include <type_traits>
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new
_STD_BEGIN
// STRUCT TEMPLATE coroutine_traits
template <class _Ret, class = void>
struct _Coroutine_traits {};
template <class _Ret>
struct _Coroutine_traits<_Ret, void_t<typename _Ret::promise_type>> {
using promise_type = typename _Ret::promise_type;
};
template <class _Ret, class...>
struct coroutine_traits : _Coroutine_traits<_Ret> {};
// STRUCT TEMPLATE coroutine_handle
template <class = void>
struct coroutine_handle;
template <>
struct coroutine_handle<void> {
constexpr coroutine_handle() noexcept = default;
constexpr coroutine_handle(nullptr_t) noexcept {}
coroutine_handle& operator=(nullptr_t) noexcept {
_Ptr = nullptr;
return *this;
}
_NODISCARD constexpr void* address() const noexcept {
return _Ptr;
}
_NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
coroutine_handle _Result;
_Result._Ptr = _Addr;
return _Result;
}
constexpr explicit operator bool() const noexcept {
return _Ptr != nullptr;
}
_NODISCARD bool done() const noexcept { // strengthened
return __builtin_coro_done(_Ptr);
}
void operator()() const noexcept { // strengthened
__builtin_coro_resume(_Ptr);
}
void resume() const noexcept { // strengthened
__builtin_coro_resume(_Ptr);
}
void destroy() const noexcept { // strengthened
__builtin_coro_destroy(_Ptr);
}
protected:
void* _Ptr = nullptr;
};
template <class _Promise>
struct coroutine_handle : coroutine_handle<> {
using coroutine_handle<>::coroutine_handle;
_NODISCARD static coroutine_handle from_promise(_Promise& _Prom) noexcept { // strengthened
const auto _Prom_ptr = const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Prom)));
const auto _Frame_ptr = __builtin_coro_promise(_Prom_ptr, 0, true);
coroutine_handle _Result;
_Result._Ptr = _Frame_ptr;
return _Result;
}
coroutine_handle& operator=(nullptr_t) noexcept {
_Ptr = nullptr;
return *this;
}
_NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
coroutine_handle _Result;
_Result._Ptr = _Addr;
return _Result;
}
_NODISCARD _Promise& promise() const noexcept { // strengthened
return *reinterpret_cast<_Promise*>(__builtin_coro_promise(_Ptr, 0, false));
}
};
_NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
return _Left.address() == _Right.address();
}
_NODISCARD constexpr strong_ordering operator<=>(
const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
return compare_three_way()(_Left.address(), _Right.address());
}
template <class _Promise>
struct hash<coroutine_handle<_Promise>> {
_NODISCARD size_t operator()(const coroutine_handle<_Promise>& _Coro) noexcept {
return _Hash_representation(_Coro.address());
}
};
// STRUCT suspend_never
struct suspend_never {
_NODISCARD constexpr bool await_ready() const noexcept {
return true;
}
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
// STRUCT suspend_always
struct suspend_always {
_NODISCARD constexpr bool await_ready() const noexcept {
return false;
}
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // __cpp_lib_coroutine
#endif // _RESUMABLE_FUNCTIONS_SUPPORTED
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _COROUTINE_

Просмотреть файл

@ -9,14 +9,19 @@
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#ifndef __cpp_coroutines
#error <experimental/generator> requires /std:c++latest or /await compiler option
#endif // __cpp_coroutines
#if !defined(__cpp_coroutines) && !defined(__cpp_impl_coroutine)
#error <experimental/generator> requires /std:c++latest or /await compiler options
#endif // !defined(__cpp_coroutines) && !defined(__cpp_impl_coroutine)
#ifdef _CPPUNWIND
#include <exception>
#endif
#include <memory>
#ifdef __cpp_coroutines
#include <experimental/resumable>
#else // __cpp_coroutines
#include <coroutine>
#endif // __cpp_coroutines
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)

Просмотреть файл

@ -31,6 +31,11 @@ support Clang. You can define _SILENCE_CLANG_COROUTINE_MESSAGE to silence this m
unsupported.
#endif // defined(__clang__) && !defined(_SILENCE_CLANG_COROUTINE_MESSAGE)
#if defined(_MSC_VER) && defined(__cpp_impl_coroutine)
#error The <experimental/coroutine> and <experimental/resumable> headers are only supported \
with /await and implement pre-C++20 coroutine support. Use <coroutine> for standard C++20 coroutines.
#endif // defined(_MSC_VER) && defined(__cpp_impl_coroutine)
// intrinsics used in implementation of coroutine_handle
extern "C" size_t _coro_resume(void*);
extern "C" void _coro_destroy(void*);
@ -39,6 +44,10 @@ extern "C" size_t _coro_done(void*);
#pragma intrinsic(_coro_destroy)
#pragma intrinsic(_coro_done)
#ifndef _ALLOW_COROUTINE_ABI_MISMATCH
#pragma detect_mismatch("_COROUTINE_ABI", "1")
#endif // _ALLOW_COROUTINE_ABI_MISMATCH
_STD_BEGIN
namespace experimental {

Просмотреть файл

@ -170,6 +170,8 @@
// P0896R4 Ranges
// (partially implemented)
// P0898R3 Standard Library Concepts
// P0912R5 Library Support For Coroutines
// (partially implemented, missing noop coroutines)
// P0919R3 Heterogeneous Lookup For Unordered Containers
// P0966R1 string::reserve() Should Not Shrink
// P1006R1 constexpr For pointer_traits<T*>::pointer_to()
@ -1164,6 +1166,10 @@
#define __cpp_lib_constexpr_tuple 201811L
#define __cpp_lib_constexpr_utility 201811L
#ifdef __cpp_impl_coroutine // TRANSITION, VS 2019 16.8 Preview 1
#define __cpp_lib_coroutine 197000L
#endif // __cpp_impl_coroutine
#ifdef __cpp_impl_destroying_delete
#define __cpp_lib_destroying_delete 201806L
#endif // __cpp_impl_destroying_delete

Просмотреть файл

@ -3,7 +3,8 @@
RUNALL_INCLUDE ..\prefix.lst
RUNALL_CROSSLIST
PM_CL="/EHsc /MT /d1await:strict /std:c++latest"
# PM_CL="/EHsc /MT /std:c++latest" # TRANSITION, VS 2019 16.8 Preview 1
PM_CL="/EHsc /MT /await /std:c++latest"
PM_CL="/BE /c /EHsc /MD /await /std:c++latest /permissive-"
PM_CL="/BE /c /EHsc /MD /await /std:c++latest"
PM_CL="/BE /c /EHsc /MD /std:c++latest"

Просмотреть файл

@ -18,6 +18,7 @@ PM_CL="/DMEOW_HEADER=compare"
PM_CL="/DMEOW_HEADER=complex"
PM_CL="/DMEOW_HEADER=concepts"
PM_CL="/DMEOW_HEADER=condition_variable"
PM_CL="/DMEOW_HEADER=coroutine"
PM_CL="/DMEOW_HEADER=deque"
PM_CL="/DMEOW_HEADER=exception"
PM_CL="/DMEOW_HEADER=execution"