blob: d9f34b883f42928b159baa6e535383cf7bca4d02 [file] [log] [blame]
/*
* Copyright (c) 2021-2023 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
#include "../functional.hpp"
#include "../stop_token.hpp"
#include "__concepts.hpp"
#include "__config.hpp"
#include "__execution_fwd.hpp"
#include "__meta.hpp"
#include <concepts>
#include <exception>
#include <type_traits>
STDEXEC_PRAGMA_PUSH()
STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
namespace stdexec
{
// [exec.queries.queryable]
template <class T>
concept queryable = destructible<T>;
template <class Tag>
struct __query
{
template <class Sig>
static inline constexpr Tag (*signature)(Sig) = nullptr;
};
//////////////////////////////////////////////////////////////////////////////////////////////////
// [exec.queries]
namespace __queries
{
struct forwarding_query_t
{
template <class _Query>
constexpr bool operator()(_Query __query) const noexcept
{
if constexpr (tag_invocable<forwarding_query_t, _Query>)
{
return tag_invoke(*this, (_Query&&)__query);
}
else if constexpr (std::derived_from<_Query, forwarding_query_t>)
{
return true;
}
else
{
return false;
}
}
};
struct query_or_t
{
template <class _Query, class _Queryable, class _Default>
constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const
noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default
{
return (_Default&&)__default;
}
template <class _Query, class _Queryable, class _Default>
requires __callable<_Query, _Queryable>
constexpr auto operator()(_Query __query, _Queryable&& __queryable,
_Default&&) const
noexcept(__nothrow_callable<_Query, _Queryable>)
-> __call_result_t<_Query, _Queryable>
{
return ((_Query&&)__query)((_Queryable&&)__queryable);
}
};
struct execute_may_block_caller_t : __query<execute_may_block_caller_t>
{
template <class _Tp>
requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>
constexpr bool operator()(_Tp&& __t) const noexcept
{
static_assert(
same_as<bool, tag_invoke_result_t<execute_may_block_caller_t,
__cref_t<_Tp>>>);
static_assert(
nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>);
return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t));
}
constexpr bool operator()(auto&&) const noexcept
{
return true;
}
};
struct get_forward_progress_guarantee_t :
__query<get_forward_progress_guarantee_t>
{
template <class _Tp>
requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>
constexpr auto operator()(_Tp&& __t) const noexcept(
nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>)
-> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>>
{
return tag_invoke(get_forward_progress_guarantee_t{},
std::as_const(__t));
}
constexpr stdexec::forward_progress_guarantee
operator()(auto&&) const noexcept
{
return stdexec::forward_progress_guarantee::weakly_parallel;
}
};
struct __has_algorithm_customizations_t :
__query<__has_algorithm_customizations_t>
{
template <class _Tp>
using __result_t =
tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>;
template <class _Tp>
requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>>
constexpr __result_t<_Tp> operator()(_Tp&&) const
noexcept(noexcept(__result_t<_Tp>{}))
{
using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t,
__cref_t<_Tp>>;
static_assert(_Boolean{}
? true
: true); // must be contextually convertible to bool
return _Boolean{};
}
constexpr std::false_type operator()(auto&&) const noexcept
{
return {};
}
};
// TODO: implement allocator concept
template <class _T0>
concept __allocator_c = true;
struct get_scheduler_t : __query<get_scheduler_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_scheduler_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_scheduler_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_scheduler_t, const _Env&>;
auto operator()() const noexcept;
};
struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_delegatee_scheduler_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
auto operator()(const _Env& __t) const noexcept
-> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>;
auto operator()() const noexcept;
};
struct get_allocator_t : __query<get_allocator_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_allocator_t&) noexcept
{
return true;
}
template <class _Env>
requires tag_invocable<get_allocator_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_allocator_t, const _Env&>
{
static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
static_assert(
__allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>);
return tag_invoke(get_allocator_t{}, __env);
}
auto operator()() const noexcept;
};
struct get_stop_token_t : __query<get_stop_token_t>
{
friend constexpr bool tag_invoke(forwarding_query_t,
const get_stop_token_t&) noexcept
{
return true;
}
template <class _Env>
never_stop_token operator()(const _Env&) const noexcept
{
return {};
}
template <class _Env>
requires tag_invocable<get_stop_token_t, const _Env&>
auto operator()(const _Env& __env) const noexcept
-> tag_invoke_result_t<get_stop_token_t, const _Env&>
{
static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
static_assert(stoppable_token<
tag_invoke_result_t<get_stop_token_t, const _Env&>>);
return tag_invoke(get_stop_token_t{}, __env);
}
auto operator()() const noexcept;
};
template <class _Queryable, class _CPO>
concept __has_completion_scheduler_for =
queryable<_Queryable> && //
tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>;
template <__completion_tag _CPO>
struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>>
{
friend constexpr bool
tag_invoke(forwarding_query_t,
const get_completion_scheduler_t<_CPO>&) noexcept
{
return true;
}
template <__has_completion_scheduler_for<_CPO> _Queryable>
auto operator()(const _Queryable& __queryable) const noexcept
-> tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
const _Queryable&>;
};
struct get_domain_t
{
template <class _Ty>
requires tag_invocable<get_domain_t, const _Ty&>
constexpr auto operator()(const _Ty& __ty) const noexcept
-> tag_invoke_result_t<get_domain_t, const _Ty&>
{
static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
"Customizations of get_domain must be noexcept.");
static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>,
"Customizations of get_domain must return a class type.");
return tag_invoke(get_domain_t{}, __ty);
}
friend constexpr bool tag_invoke(forwarding_query_t, get_domain_t) noexcept
{
return true;
}
};
} // namespace __queries
using __queries::__has_algorithm_customizations_t;
using __queries::execute_may_block_caller_t;
using __queries::forwarding_query_t;
using __queries::get_allocator_t;
using __queries::get_completion_scheduler_t;
using __queries::get_delegatee_scheduler_t;
using __queries::get_domain_t;
using __queries::get_forward_progress_guarantee_t;
using __queries::get_scheduler_t;
using __queries::get_stop_token_t;
using __queries::query_or_t;
inline constexpr forwarding_query_t forwarding_query{};
inline constexpr query_or_t query_or{}; // NOT TO SPEC
inline constexpr execute_may_block_caller_t execute_may_block_caller{};
inline constexpr __has_algorithm_customizations_t
__has_algorithm_customizations{};
inline constexpr get_forward_progress_guarantee_t
get_forward_progress_guarantee{};
inline constexpr get_scheduler_t get_scheduler{};
inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
inline constexpr get_allocator_t get_allocator{};
inline constexpr get_stop_token_t get_stop_token{};
#if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
template <__completion_tag _CPO>
inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{};
#else
template <>
inline constexpr get_completion_scheduler_t<set_value_t>
get_completion_scheduler<set_value_t>{};
template <>
inline constexpr get_completion_scheduler_t<set_error_t>
get_completion_scheduler<set_error_t>{};
template <>
inline constexpr get_completion_scheduler_t<set_stopped_t>
get_completion_scheduler<set_stopped_t>{};
#endif
template <class _Tag>
concept __forwarding_query = forwarding_query(_Tag{});
inline constexpr get_domain_t get_domain{};
template <class _Tag, class _Queryable, class _Default>
using __query_result_or_t =
__call_result_t<query_or_t, _Tag, _Queryable, _Default>;
/////////////////////////////////////////////////////////////////////////////
namespace __env
{
// For getting an execution environment from a receiver,
// or the attributes from a sender.
struct get_env_t
{
template <class _EnvProvider>
requires tag_invocable<get_env_t, const _EnvProvider&>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto
operator()(const _EnvProvider& __env_provider) const noexcept
-> tag_invoke_result_t<get_env_t, const _EnvProvider&>
{
static_assert(
queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
return tag_invoke(*this, __env_provider);
}
template <class _EnvProvider>
constexpr empty_env operator()(const _EnvProvider&) const noexcept
{
return {};
}
};
// To be kept in sync with the promise type used in __connect_awaitable
template <class _Env>
struct __promise
{
template <class _Ty>
_Ty&& await_transform(_Ty&& __value) noexcept
{
return (_Ty&&)__value;
}
template <class _Ty>
requires tag_invocable<as_awaitable_t, _Ty, __promise&>
auto await_transform(_Ty&& __value) //
noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>)
-> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&>
{
return tag_invoke(as_awaitable, (_Ty&&)__value, *this);
}
template <same_as<get_env_t> _Tag>
friend auto tag_invoke(_Tag, const __promise&) noexcept -> const _Env&
{
std::terminate();
}
};
template <class _Value, class _Tag, class... _Tags>
struct __with
{
using __t = __with;
using __id = __with;
STDEXEC_ATTRIBUTE((no_unique_address)) _Value __value_;
__with() = default;
constexpr explicit __with(_Value __value) noexcept(
__nothrow_decay_copyable<_Value>) :
__value_((_Value&&)__value)
{}
constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept(
__nothrow_decay_copyable<_Value>) :
__value_((_Value&&)__value)
{}
template <__one_of<_Tag, _Tags...> _Key>
friend auto tag_invoke(_Key, const __with& __self) //
noexcept(__nothrow_decay_copyable<_Value>) -> _Value
{
return __self.__value_;
}
};
template <class _Value, class _Tag, class... _Tags>
__with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>;
template <class _Env>
struct __fwd
{
static_assert(__nothrow_move_constructible<_Env>);
using __t = __fwd;
using __id = __fwd;
STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
template <__forwarding_query _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __fwd& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <class _Env>
__fwd(_Env&&) -> __fwd<_Env>;
template <class _Env>
struct __ref
{
using __t = __ref;
using __id = __ref;
const _Env& __env_;
template <class _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __ref& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <class _Env>
__ref(_Env&) -> __ref<_Env>;
struct __ref_fn
{
template <class _Env>
constexpr auto operator()(_Env&& __env) const
{
if constexpr (same_as<_Env, _Env&>)
{
return __ref{(_Env&&)__env};
}
else
{
return (_Env&&)__env;
}
}
};
template <class _Env, class _Tag, class... _Tags>
struct __without_ : _Env
{
static_assert(__nothrow_move_constructible<_Env>);
using __t = __without_;
using __id = __without_;
constexpr explicit __without_(_Env&& __env, _Tag, _Tags...) noexcept :
_Env((_Env&&)__env)
{}
template <__one_of<_Tag, _Tags...> _Key, class _Self>
requires(std::is_base_of_v<__without_, __decay_t<_Self>>)
friend auto tag_invoke(_Key, _Self&&) noexcept = delete;
};
struct __without_fn
{
template <class _Env, class _Tag, class... _Tags>
constexpr decltype(auto) operator()(_Env&& __env, _Tag,
_Tags...) const noexcept
{
if constexpr (tag_invocable<_Tag, _Env> ||
(tag_invocable<_Tags, _Env> || ...))
{
return __without_{__ref_fn()((_Env&&)__env), _Tag(), _Tags()...};
}
else
{
return static_cast<_Env>((_Env&&)__env);
}
}
};
inline constexpr __without_fn __without{};
template <class _Env, class _Tag, class... _Tags>
using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
template <class _Second, class _First>
struct __joined : _Second
{
static_assert(__nothrow_move_constructible<_First>);
static_assert(__nothrow_move_constructible<_Second>);
using __t = __joined;
using __id = __joined;
STDEXEC_ATTRIBUTE((no_unique_address)) _First __env_;
template <class _Tag>
requires tag_invocable<_Tag, const _First&>
friend auto tag_invoke(_Tag, const __joined& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _First&>)
-> tag_invoke_result_t<_Tag, const _First&>
{
return _Tag()(__self.__env_);
}
};
template <class _Second, class _First>
__joined(_Second&&, _First&&) -> __joined<_Second, _First>;
template <__nothrow_move_constructible _Fun>
struct __from
{
using __t = __from;
using __id = __from;
STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
template <class _Tag>
requires __callable<const _Fun&, _Tag>
friend auto tag_invoke(_Tag, const __from& __self) //
noexcept(__nothrow_callable<const _Fun&, _Tag>)
-> __call_result_t<const _Fun&, _Tag>
{
return __self.__fun_(_Tag());
}
};
template <class _Fun>
__from(_Fun) -> __from<_Fun>;
struct __fwd_fn
{
template <class Env>
auto operator()(Env&& env) const
{
return __fwd{(Env&&)env};
}
empty_env operator()(empty_env) const
{
return {};
}
};
struct __join_fn
{
empty_env operator()() const
{
return {};
}
template <class _Env>
_Env operator()(_Env&& __env) const
{
return (_Env&&)__env;
}
empty_env operator()(empty_env) const
{
return {};
}
template <class _Env>
_Env operator()(_Env&& __env, empty_env) const
{
return (_Env&&)__env;
}
empty_env operator()(empty_env, empty_env) const
{
return {};
}
template <class... Rest>
decltype(auto) operator()(empty_env, Rest&&... rest) const
{
return __fwd_fn()(__join_fn()((Rest&&)rest...));
}
template <class First, class... Rest>
decltype(auto) operator()(First&& first, Rest&&... rest) const
{
return __joined{__fwd_fn()(__join_fn()((Rest&&)rest...)),
(First&&)first};
}
};
inline constexpr __join_fn __join{};
template <class... _Envs>
using __join_t = __result_of<__join, _Envs...>;
} // namespace __env
using __env::empty_env;
using __env::get_env_t;
inline constexpr get_env_t get_env{};
template <class _EnvProvider>
concept environment_provider = //
requires(_EnvProvider& __ep) {
{
get_env(std::as_const(__ep))
} -> queryable;
};
} // namespace stdexec
STDEXEC_PRAGMA_POP()