blob: 68dfa93b60080782a35b940b7bb07f274fa04f93 [file] [log] [blame]
/*
* Copyright (c) 2022 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if __cplusplus < 202002L
#error This library requires the use of C++20.
#endif
#if __has_include(<version>)
#include <version>
#else
#include <ciso646> // For stdlib feature-test macros when <version> is not available
#endif
#include <cassert>
#include <version>
#define STDEXEC_STRINGIZE(_ARG) #_ARG
#define STDEXEC_CAT_(_XP, ...) _XP##__VA_ARGS__
#define STDEXEC_CAT(_XP, ...) STDEXEC_CAT_(_XP, __VA_ARGS__)
#define STDEXEC_EXPAND(...) __VA_ARGS__
#define STDEXEC_EVAL(_MACRO, ...) _MACRO(__VA_ARGS__)
#define STDEXEC_EAT(...)
#define STDEXEC_NOT(_XP) STDEXEC_CAT(STDEXEC_NOT_, _XP)
enum
{
STDEXEC_NOT_0 = 1,
STDEXEC_NOT_1 = 0
};
#define STDEXEC_IIF_0(_YP, ...) __VA_ARGS__
#define STDEXEC_IIF_1(_YP, ...) _YP
#define STDEXEC_IIF(_XP, _YP, ...) \
STDEXEC_EVAL(STDEXEC_CAT(STDEXEC_IIF_, _XP), _YP, __VA_ARGS__)
#define STDEXEC_COUNT(...) \
STDEXEC_EXPAND(STDEXEC_COUNT_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
#define STDEXEC_COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _NP, ...) _NP
#define STDEXEC_CHECK(...) STDEXEC_EXPAND(STDEXEC_CHECK_(__VA_ARGS__, 0, ))
#define STDEXEC_CHECK_(_XP, _NP, ...) _NP
#define STDEXEC_PROBE(...) STDEXEC_PROBE_(__VA_ARGS__, 1)
#define STDEXEC_PROBE_(_XP, _NP, ...) _XP, _NP,
////////////////////////////////////////////////////////////////////////////////
// STDEXEC_FOR_EACH
// Inspired by "Recursive macros with C++20 __VA_OPT__", by David Mazières
// https://www.scs.stanford.edu/~dm/blog/va-opt.html
#define STDEXEC_EXPAND_R(...) \
STDEXEC_EXPAND_R1( \
STDEXEC_EXPAND_R1(STDEXEC_EXPAND_R1(STDEXEC_EXPAND_R1(__VA_ARGS__)))) \
/**/
#define STDEXEC_EXPAND_R1(...) \
STDEXEC_EXPAND_R2( \
STDEXEC_EXPAND_R2(STDEXEC_EXPAND_R2(STDEXEC_EXPAND_R2(__VA_ARGS__)))) \
/**/
#define STDEXEC_EXPAND_R2(...) \
STDEXEC_EXPAND_R3( \
STDEXEC_EXPAND_R3(STDEXEC_EXPAND_R3(STDEXEC_EXPAND_R3(__VA_ARGS__)))) \
/**/
#define STDEXEC_EXPAND_R3(...) \
STDEXEC_EXPAND( \
STDEXEC_EXPAND(STDEXEC_EXPAND(STDEXEC_EXPAND(__VA_ARGS__)))) \
/**/
#define STDEXEC_PARENS ()
#define STDEXEC_FOR_EACH(_MACRO, ...) \
__VA_OPT__(STDEXEC_EXPAND_R(STDEXEC_FOR_EACH_HELPER(_MACRO, __VA_ARGS__))) \
/**/
#define STDEXEC_FOR_EACH_HELPER(_MACRO, _A1, ...) \
_MACRO(_A1) \
__VA_OPT__(STDEXEC_FOR_EACH_AGAIN STDEXEC_PARENS(_MACRO, __VA_ARGS__)) /**/
#define STDEXEC_FOR_EACH_AGAIN() STDEXEC_FOR_EACH_HELPER
////////////////////////////////////////////////////////////////////////////////////////////////////
// If tail is non-empty, expand to the tail. Otherwise, expand to the head
#define STDEXEC_HEAD_OR_TAIL(_XP, ...) \
STDEXEC_EXPAND __VA_OPT__((__VA_ARGS__)STDEXEC_EAT)(_XP)
// If tail is non-empty, expand to nothing. Otherwise, expand to the head
#define STDEXEC_HEAD_OR_NULL(_XP, ...) \
STDEXEC_EXPAND __VA_OPT__(() STDEXEC_EAT)(_XP)
// When used with no arguments, these macros expand to 1 if the current
// compiler corresponds to the macro name; 0, otherwise. When used with
// arguments, they expand to the arguments if if the current compiler
// corresponds to the macro name; nothing, otherwise.
#if defined(__NVCC__)
#define STDEXEC_NVCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(__NVCOMPILER)
#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(__EDG__)
#define STDEXEC_EDG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(__clang__)
#define STDEXEC_CLANG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#if defined(_MSC_VER)
#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#endif
#elif defined(__GNUC__)
#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(_MSC_VER)
#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#endif
#ifndef STDEXEC_NVCC
#define STDEXEC_NVCC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_NVHPC
#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_EDG
#define STDEXEC_EDG(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_CLANG
#define STDEXEC_CLANG(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_CLANG_CL
#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_GCC
#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_MSVC
#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef __CUDACC__
#define STDEXEC_CUDA(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#else
#define STDEXEC_CUDA(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
// For portably declaring attributes on functions and types
// Usage:
//
// STDEXEC_ATTRIBUTE((attr1, attr2, ...))
// void foo() { ... }
#define STDEXEC_ATTRIBUTE(_XP) \
STDEXEC_FOR_EACH(STDEXEC_ATTR, STDEXEC_EXPAND _XP)
#define STDEXEC_ATTR(_ATTR) \
STDEXEC_CAT(STDEXEC_ATTR_WHICH_, \
STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_ATTR_, _ATTR))) \
(_ATTR)
// unknown attributes are treated like C++-style attributes
#define STDEXEC_ATTR_WHICH_0(_ATTR) [[_ATTR]]
// custom handling for specific attribute types
#ifdef __CUDACC__
#define STDEXEC_ATTR_WHICH_1(_ATTR) __host__
#else
#define STDEXEC_ATTR_WHICH_1(_ATTR)
#endif
#define STDEXEC_ATTR_host STDEXEC_PROBE(~, 1)
#define STDEXEC_ATTR___host__ STDEXEC_PROBE(~, 1)
#ifdef __CUDACC__
#define STDEXEC_ATTR_WHICH_2(_ATTR) __device__
#else
#define STDEXEC_ATTR_WHICH_2(_ATTR)
#endif
#define STDEXEC_ATTR_device STDEXEC_PROBE(~, 2)
#define STDEXEC_ATTR___device__ STDEXEC_PROBE(~, 2)
#if STDEXEC_NVHPC()
// NVBUG #4067067: NVHPC does not fully support [[no_unique_address]]
#define STDEXEC_ATTR_WHICH_3(_ATTR) /*nothing*/
#elif STDEXEC_MSVC()
// MSVCBUG
// https://developercommunity.visualstudio.com/t/Incorrect-codegen-when-using-msvc::no_/10452874
#define STDEXEC_ATTR_WHICH_3(_ATTR) // [[msvc::no_unique_address]]
#elif STDEXEC_CLANG_CL()
// clang-cl does not support: https://reviews.llvm.org/D110485
#define STDEXEC_ATTR_WHICH_3(_ATTR) // [[msvc::no_unique_address]]
#else
#define STDEXEC_ATTR_WHICH_3(_ATTR) [[no_unique_address]]
#endif
#define STDEXEC_ATTR_no_unique_address STDEXEC_PROBE(~, 3)
#if STDEXEC_MSVC()
#define STDEXEC_ATTR_WHICH_4(_ATTR) __forceinline
#elif STDEXEC_CLANG()
#define STDEXEC_ATTR_WHICH_4(_ATTR) \
__attribute__((__always_inline__, __artificial__, __nodebug__)) inline
#elif defined(__GNUC__)
#define STDEXEC_ATTR_WHICH_4(_ATTR) \
__attribute__((__always_inline__, __artificial__)) inline
#else
#define STDEXEC_ATTR_WHICH_4(_ATTR) /*nothing*/
#endif
#define STDEXEC_ATTR_always_inline STDEXEC_PROBE(~, 4)
////////////////////////////////////////////////////////////////////////////////////////////////////
// warning push/pop portability macros
#if STDEXEC_NVCC()
#define STDEXEC_PRAGMA_PUSH() _Pragma("nv_diagnostic push")
#define STDEXEC_PRAGMA_POP() _Pragma("nv_diagnostic pop")
#define STDEXEC_PRAGMA_IGNORE_EDG(...) \
_Pragma(STDEXEC_STRINGIZE(nv_diag_suppress __VA_ARGS__))
#elif STDEXEC_NVHPC() || STDEXEC_EDG()
#define STDEXEC_PRAGMA_PUSH() \
_Pragma("diagnostic push") STDEXEC_PRAGMA_IGNORE_EDG(invalid_error_number)
#define STDEXEC_PRAGMA_POP() _Pragma("diagnostic pop")
#define STDEXEC_PRAGMA_IGNORE_EDG(...) \
_Pragma(STDEXEC_STRINGIZE(diag_suppress __VA_ARGS__))
#elif STDEXEC_CLANG() || STDEXEC_GCC()
#define STDEXEC_PRAGMA_PUSH() \
_Pragma("GCC diagnostic push") STDEXEC_PRAGMA_IGNORE_GNU("-Wpragmas") \
STDEXEC_PRAGMA_IGNORE_GNU("-Wunknown-pragmas") \
STDEXEC_PRAGMA_IGNORE_GNU("-Wunknown-warning-option") \
STDEXEC_PRAGMA_IGNORE_GNU("-Wunknown-attributes") \
STDEXEC_PRAGMA_IGNORE_GNU("-Wattributes")
#define STDEXEC_PRAGMA_POP() _Pragma("GCC diagnostic pop")
#define STDEXEC_PRAGMA_IGNORE_GNU(...) \
_Pragma(STDEXEC_STRINGIZE(GCC diagnostic ignored __VA_ARGS__))
#else
#define STDEXEC_PRAGMA_PUSH()
#define STDEXEC_PRAGMA_POP()
#endif
#ifndef STDEXEC_PRAGMA_IGNORE_GNU
#define STDEXEC_PRAGMA_IGNORE_GNU(...)
#endif
#ifndef STDEXEC_PRAGMA_IGNORE_EDG
#define STDEXEC_PRAGMA_IGNORE_EDG(...)
#endif
#if !STDEXEC_MSVC() && defined(__has_builtin)
#define STDEXEC_HAS_BUILTIN __has_builtin
#else
#define STDEXEC_HAS_BUILTIN(...) 0
#endif
#if !STDEXEC_MSVC() && defined(__has_feature)
#define STDEXEC_HAS_FEATURE __has_feature
#else
#define STDEXEC_HAS_FEATURE(...) 0
#endif
#if STDEXEC_HAS_BUILTIN(__is_trivially_copyable) || STDEXEC_MSVC()
#define STDEXEC_IS_TRIVIALLY_COPYABLE(...) __is_trivially_copyable(__VA_ARGS__)
#else
#define STDEXEC_IS_TRIVIALLY_COPYABLE(...) \
std::is_trivially_copyable_v<__VA_ARGS__>
#endif
#if STDEXEC_HAS_BUILTIN(__is_base_of) || (_MSC_VER >= 1914)
#define STDEXEC_IS_BASE_OF(...) __is_base_of(__VA_ARGS__)
#else
#define STDEXEC_IS_BASE_OF(...) std::is_base_of_v<__VA_ARGS__>
#endif
#if STDEXEC_HAS_BUILTIN(__is_convertible_to) || STDEXEC_MSVC()
#define STDEXEC_IS_CONVERTIBLE_TO(...) __is_convertible_to(__VA_ARGS__)
#elif STDEXEC_HAS_BUILTIN(__is_convertible)
#define STDEXEC_IS_CONVERTIBLE_TO(...) __is_convertible(__VA_ARGS__)
#else
#define STDEXEC_IS_CONVERTIBLE_TO(...) std::is_convertible_v<__VA_ARGS__>
#endif
#if STDEXEC_HAS_BUILTIN(__is_const)
#define STDEXEC_IS_CONST(...) __is_const(__VA_ARGS__)
#else
#define STDEXEC_IS_CONST(...) stdexec::__is_const<__VA_ARGS__>
#endif
#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
#define STDEXEC_UNREACHABLE() std::unreachable()
#elif STDEXEC_HAS_BUILTIN(__builtin_unreachable)
#define STDEXEC_UNREACHABLE() __builtin_unreachable()
#elif STDEXEC_MSVC()
#define STDEXEC_UNREACHABLE(...) __assume(false)
#else
#define STDEXEC_UNREACHABLE(...) std::terminate()
#endif
// Before gcc-12, gcc really didn't like tuples or variants of immovable types
#if STDEXEC_GCC() && (__GNUC__ < 12)
#define STDEXEC_IMMOVABLE(_XP) _XP(_XP&&)
#else
#define STDEXEC_IMMOVABLE(_XP) _XP(_XP&&) = delete
#endif
// BUG (gcc PR93711): copy elision fails when initializing a
// [[no_unique_address]] field from a function returning an object
// of class type by value
#if STDEXEC_GCC()
#define STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
#else
#define STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS \
STDEXEC_ATTRIBUTE((no_unique_address))
#endif
#if STDEXEC_NVHPC()
#include <nv/target>
#define STDEXEC_TERMINATE() \
NV_IF_TARGET(NV_IS_HOST, (std::terminate();), (__trap();)) void()
#elif STDEXEC_CLANG() && STDEXEC_CUDA() && defined(__CUDA_ARCH__)
#define STDEXEC_TERMINATE() \
__trap(); \
__builtin_unreachable()
#else
#define STDEXEC_TERMINATE() std::terminate()
#endif
#if STDEXEC_HAS_FEATURE(thread_sanitizer) || defined(__SANITIZE_THREAD__)
#define STDEXEC_TSAN(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#else
#define STDEXEC_TSAN(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
// Before clang-16, clang did not like libstdc++'s ranges implementation
#if __has_include(<ranges>) && \
(defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L) && \
(!STDEXEC_CLANG() || __clang_major__ >= 16 || defined(_LIBCPP_VERSION))
#define STDEXEC_HAS_STD_RANGES() 1
#else
#define STDEXEC_HAS_STD_RANGES() 0
#endif
#if __has_include(<memory_resource>) && \
(defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L)
#define STDEXEC_HAS_STD_MEMORY_RESOURCE() 1
#else
#define STDEXEC_HAS_STD_MEMORY_RESOURCE() 0
#endif
#ifdef STDEXEC_ASSERT
#error \
"Redefinition of STDEXEC_ASSERT is not permitted. Define STDEXEC_ASSERT_FN instead."
#endif
#define STDEXEC_ASSERT(_XP) \
do \
{ \
static_assert(noexcept(_XP)); \
STDEXEC_ASSERT_FN(_XP); \
} while (false)
#ifndef STDEXEC_ASSERT_FN
#define STDEXEC_ASSERT_FN assert
#endif
#define STDEXEC_AUTO_RETURN(...) \
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) \
{ \
return __VA_ARGS__; \
}
// GCC 13 implements lexical friendship, but it is incomplete. See
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111018
#if STDEXEC_CLANG() // || (STDEXEC_GCC() && __GNUC__ >= 13)
#define STDEXEC_FRIENDSHIP_IS_LEXICAL() 1
#else
#define STDEXEC_FRIENDSHIP_IS_LEXICAL() 0
#endif
#if defined(__cpp_explicit_this_parameter) && \
(__cpp_explicit_this_parameter >= 202110)
#define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#else
#define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#if STDEXEC_EXPLICIT_THIS()
#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) __VA_ARGS__
#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME( STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_ARGS__ )
#else
#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) static __VA_ARGS__(STDEXEC_FUN_ARGS
#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME((_OBJ) STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_OPT__(, ) __VA_ARGS__)
#define STDEXEC_EAT_THIS_DETAIL_this
#define STDEXEC_FUN_ARGS(...) STDEXEC_CAT(STDEXEC_EAT_THIS_DETAIL_, __VA_ARGS__))
#endif
// Configure extra type checking
#define STDEXEC_TYPE_CHECKING_ZERO() 0
#define STDEXEC_TYPE_CHECKING_ONE() 1
#define STDEXEC_TYPE_CHECKING_TWO() 2
#define STDEXEC_PROBE_TYPE_CHECKING_ STDEXEC_TYPE_CHECKING_ONE
#define STDEXEC_PROBE_TYPE_CHECKING_0 STDEXEC_TYPE_CHECKING_ZERO
#define STDEXEC_PROBE_TYPE_CHECKING_1 STDEXEC_TYPE_CHECKING_ONE
#define STDEXEC_PROBE_TYPE_CHECKING_STDEXEC_ENABLE_EXTRA_TYPE_CHECKING \
STDEXEC_TYPE_CHECKING_TWO
#define STDEXEC_TYPE_CHECKING_WHICH3(...) \
STDEXEC_PROBE_TYPE_CHECKING_##__VA_ARGS__
#define STDEXEC_TYPE_CHECKING_WHICH2(...) \
STDEXEC_TYPE_CHECKING_WHICH3(__VA_ARGS__)
#define STDEXEC_TYPE_CHECKING_WHICH \
STDEXEC_TYPE_CHECKING_WHICH2(STDEXEC_ENABLE_EXTRA_TYPE_CHECKING)
#ifndef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
#elif STDEXEC_TYPE_CHECKING_WHICH() == 2
// do nothing
#elif STDEXEC_TYPE_CHECKING_WHICH() == 0
#undef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
#else
#undef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 1
#endif
namespace stdexec
{}