From 5fa1f91e3331905df3aaa71173693f8a8e64e922 Mon Sep 17 00:00:00 2001 From: Jonathan Emmett Date: Thu, 11 Mar 2021 11:41:23 -0500 Subject: [PATCH 1/4] Downlevel standard coroutine support Allow `` to be included if `_DOWNLEVEL_COROUTINES_SUPPORTED` is defined even if `__cpp_lib_coroutine` is not. Allows use of standard coroutines in C++14 and C++17 modes with a compiler that permits it. Comparison operators are explicitly defined instead of <=> in these modes. Corresponding compiler changes are expected to come in 16.10. --- stl/inc/coroutine | 34 +++++++++++++++++++---- tests/std/tests/P0912R5_coroutine/env.lst | 19 ++++++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/stl/inc/coroutine b/stl/inc/coroutine index ad2c2f30dd8..76e3f4c7518 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -11,16 +11,18 @@ #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED #pragma message("The contents of are not available with /await.") -#pragma message("Remove /await for standard coroutines or use for legacy /await support.") +#pragma message("Remove /await or use /await:strict for standard coroutines. Use for legacy /await support.") #else // ^^^ /await ^^^ / vvv no /await vvv -#ifndef __cpp_lib_coroutine -#pragma message("The contents of are available only with C++20 or later.") -#else // ^^^ __cpp_lib_coroutine not defined / __cpp_lib_coroutine defined vvv +#if !defined(__cpp_lib_coroutine) && !defined(_DOWNLEVEL_COROUTINES_SUPPORTED) +#pragma message("The contents of are available only with C++20 or later or /await:strict.") +#else // ^^^ is not available / is available vvv #ifndef _ALLOW_COROUTINE_ABI_MISMATCH #pragma detect_mismatch("_COROUTINE_ABI", "2") #endif // _ALLOW_COROUTINE_ABI_MISMATCH +#ifdef __cpp_lib_coroutine #include +#endif // __cpp_lib_coroutine #include #pragma pack(push, _CRT_PACKING) @@ -156,6 +158,7 @@ _NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const corou return _Left.address() == _Right.address(); } +#ifdef __cpp_lib_coroutine _NODISCARD constexpr strong_ordering operator<=>( const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { #ifdef __cpp_lib_concepts @@ -164,6 +167,27 @@ _NODISCARD constexpr strong_ordering operator<=>( return _Left.address() <=> _Right.address(); #endif // __cpp_lib_concepts } +#else // ^^^ __cpp_lib_coroutine defined / __cpp_lib_coroutine not defined vvv +_NODISCARD constexpr bool operator!=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left == _Right); +} + +_NODISCARD constexpr bool operator<(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return less{}(_Left.address(), _Right.address()); +} + +_NODISCARD constexpr bool operator>(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return _Right < _Left; +} + +_NODISCARD constexpr bool operator<=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left > _Right); +} + +_NODISCARD constexpr bool operator>=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left < _Right); +} +#endif // __cpp_lib_coroutine template struct hash> { @@ -246,7 +270,7 @@ _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_coroutine +#endif // is available #endif // _RESUMABLE_FUNCTIONS_SUPPORTED #endif // _STL_COMPILER_PREPROCESSOR #endif // _COROUTINE_ diff --git a/tests/std/tests/P0912R5_coroutine/env.lst b/tests/std/tests/P0912R5_coroutine/env.lst index 642f530ffad..9c126ed726f 100644 --- a/tests/std/tests/P0912R5_coroutine/env.lst +++ b/tests/std/tests/P0912R5_coroutine/env.lst @@ -1,4 +1,21 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/EHsc /MD /await:strict /std:c++14" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++14 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /await:strict /std:c++17" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++17 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /std:c++latest /permissive" +PM_CL="/EHsc /MD /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive-" From d6f0f34ba005eb30a9c3668403af18a077394e89 Mon Sep 17 00:00:00 2001 From: Jonathan Emmett Date: Thu, 11 Mar 2021 12:22:20 -0500 Subject: [PATCH 2/4] Split long line --- stl/inc/coroutine | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/coroutine b/stl/inc/coroutine index 76e3f4c7518..de6d512074e 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -11,7 +11,8 @@ #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED #pragma message("The contents of are not available with /await.") -#pragma message("Remove /await or use /await:strict for standard coroutines. Use for legacy /await support.") +#pragma message("Remove /await or use /await:strict for standard coroutines.") +#pragma message("Use for legacy /await support.") #else // ^^^ /await ^^^ / vvv no /await vvv #if !defined(__cpp_lib_coroutine) && !defined(_DOWNLEVEL_COROUTINES_SUPPORTED) #pragma message("The contents of are available only with C++20 or later or /await:strict.") From c7446b8d8286d96f0eaa3eb6842a997b20d8d6bc Mon Sep 17 00:00:00 2001 From: Jonathan Emmett Date: Thu, 11 Mar 2021 19:18:06 -0500 Subject: [PATCH 3/4] Update tests/std/tests/P0912R5_coroutine/env.lst Add /permissive- to the /Zc:preprocessor config. Co-authored-by: Stephan T. Lavavej --- tests/std/tests/P0912R5_coroutine/env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0912R5_coroutine/env.lst b/tests/std/tests/P0912R5_coroutine/env.lst index 9c126ed726f..f873150e6bb 100644 --- a/tests/std/tests/P0912R5_coroutine/env.lst +++ b/tests/std/tests/P0912R5_coroutine/env.lst @@ -13,7 +13,7 @@ PM_CL="/EHsc /MTd /await:strict /std:c++17 /permissive- /Zc:preprocessor" PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive- /analyze:only /analyze:autolog-" PM_CL="/EHsc /MD /std:c++latest /permissive" PM_CL="/EHsc /MD /std:c++latest /permissive-" -PM_CL="/EHsc /MTd /std:c++latest /Zc:preprocessor" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /Zc:preprocessor" PM_CL="/EHsc /MD /std:c++latest /permissive- /analyze:only /analyze:autolog-" PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" From 378a56522739a251d622fad2b4f3e56035e49a20 Mon Sep 17 00:00:00 2001 From: Jonathan Emmett Date: Thu, 11 Mar 2021 20:11:16 -0500 Subject: [PATCH 4/4] Rework downlevel coroutines: - Move detection of downlevel coroutines to yvals_core.h in definition of __cpp_lib_coroutine. - Rework inclusion of in terms of __cpp_lib_coroutine, guard inclusion and spaceship definition in _HAS_CXX20. - C++14-ify the coroutine test. --- stl/inc/coroutine | 12 ++++----- stl/inc/yvals_core.h | 8 +++--- tests/std/tests/P0912R5_coroutine/test.cpp | 29 ++++++++++++---------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/stl/inc/coroutine b/stl/inc/coroutine index de6d512074e..a9f8f729737 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -14,16 +14,16 @@ #pragma message("Remove /await or use /await:strict for standard coroutines.") #pragma message("Use for legacy /await support.") #else // ^^^ /await ^^^ / vvv no /await vvv -#if !defined(__cpp_lib_coroutine) && !defined(_DOWNLEVEL_COROUTINES_SUPPORTED) +#ifndef __cpp_lib_coroutine #pragma message("The contents of are available only with C++20 or later or /await:strict.") #else // ^^^ is not available / is available vvv #ifndef _ALLOW_COROUTINE_ABI_MISMATCH #pragma detect_mismatch("_COROUTINE_ABI", "2") #endif // _ALLOW_COROUTINE_ABI_MISMATCH -#ifdef __cpp_lib_coroutine +#if _HAS_CXX20 #include -#endif // __cpp_lib_coroutine +#endif // _HAS_CXX20 #include #pragma pack(push, _CRT_PACKING) @@ -159,7 +159,7 @@ _NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const corou return _Left.address() == _Right.address(); } -#ifdef __cpp_lib_coroutine +#if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>( const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { #ifdef __cpp_lib_concepts @@ -168,7 +168,7 @@ _NODISCARD constexpr strong_ordering operator<=>( return _Left.address() <=> _Right.address(); #endif // __cpp_lib_concepts } -#else // ^^^ __cpp_lib_coroutine defined / __cpp_lib_coroutine not defined vvv +#else // ^^^ <=> exists / <=> does not exist vvv _NODISCARD constexpr bool operator!=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { return !(_Left == _Right); } @@ -188,7 +188,7 @@ _NODISCARD constexpr bool operator<=(const coroutine_handle<> _Left, const corou _NODISCARD constexpr bool operator>=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { return !(_Left < _Right); } -#endif // __cpp_lib_coroutine +#endif // _HAS_CXX20 template struct hash> { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a4d0d1a1aaf..973143b93e3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1220,10 +1220,6 @@ #define __cpp_lib_constexpr_vector 201907L #endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) -#ifdef __cpp_impl_coroutine // TRANSITION, Clang coroutine support -#define __cpp_lib_coroutine 201902L -#endif // __cpp_impl_coroutine - #define __cpp_lib_destroying_delete 201806L #define __cpp_lib_endian 201907L #define __cpp_lib_erase_if 202002L @@ -1296,6 +1292,10 @@ #define __cpp_lib_shared_ptr_arrays 201611L // P0497R0 Fixing shared_ptr For Arrays #endif // _HAS_CXX20 +#if defined(__cpp_impl_coroutine) || defined(_DOWNLEVEL_COROUTINES_SUPPORTED) // TRANSITION, Clang coroutine support +#define __cpp_lib_coroutine 201902L +#endif // __cpp_impl_coroutine + // EXPERIMENTAL #define __cpp_lib_experimental_erase_if 201411L #define __cpp_lib_experimental_filesystem 201406L diff --git a/tests/std/tests/P0912R5_coroutine/test.cpp b/tests/std/tests/P0912R5_coroutine/test.cpp index 0aa48651cb6..2edce782cee 100644 --- a/tests/std/tests/P0912R5_coroutine/test.cpp +++ b/tests/std/tests/P0912R5_coroutine/test.cpp @@ -9,6 +9,8 @@ #include using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + int g_tasks_destroyed{0}; struct Task { @@ -33,7 +35,8 @@ struct Task { void await_resume() noexcept {} coroutine_handle<> await_suspend(coroutine_handle h) noexcept { - if (auto& pre = h.promise().previous; pre) { + auto& pre = h.promise().previous; + if (pre) { return pre; // resume awaiting coroutine } @@ -97,50 +100,50 @@ Task triangular_number(const int n) { void test_noop_handle() { // Validate noop_coroutine_handle const noop_coroutine_handle noop = noop_coroutine(); - static_assert(noexcept(noop_coroutine())); + STATIC_ASSERT(noexcept(noop_coroutine())); const coroutine_handle<> as_void = noop; - static_assert(noexcept(static_cast>(noop_coroutine()))); + STATIC_ASSERT(noexcept(static_cast>(noop_coroutine()))); assert(noop); assert(as_void); - static_assert(noexcept(static_cast(noop))); - static_assert(noexcept(static_cast(as_void))); + STATIC_ASSERT(noexcept(static_cast(noop))); + STATIC_ASSERT(noexcept(static_cast(as_void))); assert(!noop.done()); assert(!as_void.done()); - static_assert(noexcept(noop.done())); - static_assert(noexcept(as_void.done())); + STATIC_ASSERT(noexcept(noop.done())); + STATIC_ASSERT(noexcept(as_void.done())); assert(noop); assert(as_void); noop(); as_void(); - static_assert(noexcept(noop())); + STATIC_ASSERT(noexcept(noop())); assert(noop); assert(as_void); noop.resume(); as_void.resume(); - static_assert(noexcept(noop.resume())); + STATIC_ASSERT(noexcept(noop.resume())); assert(noop); assert(as_void); noop.destroy(); as_void.destroy(); - static_assert(noexcept(noop.destroy())); + STATIC_ASSERT(noexcept(noop.destroy())); assert(noop); assert(as_void); assert(&noop.promise() != nullptr); - static_assert(noexcept(noop.promise())); + STATIC_ASSERT(noexcept(noop.promise())); assert(noop); assert(as_void); assert(noop.address() != nullptr); assert(noop.address() == as_void.address()); - static_assert(noexcept(noop.address())); - static_assert(noexcept(as_void.address())); + STATIC_ASSERT(noexcept(noop.address())); + STATIC_ASSERT(noexcept(as_void.address())); } int main() {