blob: 8d16b3b0cc0e6873fc3ca63b2ef75a5a4e342ae1 [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 "__execution_fwd.hpp"
#include <concepts>
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>;
/////////////////////////////////////////////////////////////////////////////
// env_of
namespace __env
{
template <class _Descriptor>
struct __prop;
template <class _Value, class... _Tags>
struct __prop<_Value(_Tags...)>
{
using __t = __prop;
using __id = __prop;
_Value __value_;
template <__one_of<_Tags...> _Key>
friend auto tag_invoke(_Key, const __prop& __self) //
noexcept(__nothrow_decay_copyable<_Value>) -> _Value
{
return __self.__value_;
}
};
template <class... _Tags>
struct __prop<void(_Tags...)>
{
using __t = __prop;
using __id = __prop;
template <__one_of<_Tags...> _Key, class _Self>
requires(std::is_base_of_v<__prop, __decay_t<_Self>>)
friend auto tag_invoke(_Key, _Self&&) noexcept = delete;
};
struct __mkprop_t
{
template <class _Value, class _Tag, class... _Tags>
auto operator()(_Value&& __value, _Tag, _Tags...) const
noexcept(__nothrow_decay_copyable<_Value>)
-> __prop<__decay_t<_Value>(_Tag, _Tags...)>
{
return {(_Value&&)__value};
}
template <class _Tag>
auto operator()(_Tag) const -> __prop<void(_Tag)>
{
return {};
}
};
template <__nothrow_move_constructible _Fun>
struct __env_fn
{
using __t = __env_fn;
using __id = __env_fn;
STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
template <class _Tag>
requires __callable<const _Fun&, _Tag>
friend auto tag_invoke(_Tag, const __env_fn& __self) //
noexcept(__nothrow_callable<const _Fun&, _Tag>)
-> __call_result_t<const _Fun&, _Tag>
{
return __self.__fun_(_Tag());
}
};
template <class _Fun>
__env_fn(_Fun) -> __env_fn<_Fun>;
template <class _Env>
struct __env_fwd
{
static_assert(__nothrow_move_constructible<_Env>);
using __t = __env_fwd;
using __id = __env_fwd;
STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
template <__forwarding_query _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __env_fwd& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <class _Env>
__env_fwd(_Env&&) -> __env_fwd<_Env>;
template <class _Env, class _Base = empty_env>
struct __joined_env : __env_fwd<_Base>
{
static_assert(__nothrow_move_constructible<_Env>);
using __t = __joined_env;
using __id = __joined_env;
STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
const _Base& base() const noexcept
{
return this->__env_fwd<_Base>::__env_;
}
template <class _Tag>
requires tag_invocable<_Tag, const _Env&>
friend auto tag_invoke(_Tag, const __joined_env& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
return _Tag()(__self.__env_);
}
};
template <class _Tag, class _Base>
struct __joined_env<__prop<void(_Tag)>, _Base> : __env_fwd<_Base>
{
using __t = __joined_env;
using __id = __joined_env;
STDEXEC_ATTRIBUTE((no_unique_address)) __prop<void(_Tag)> __env_;
friend void tag_invoke(_Tag, const __joined_env&) noexcept = delete;
};
struct __join_env_t
{
template <class _Env>
_Env operator()(_Env&& __env) const noexcept
{
return (_Env&&)__env;
}
template <class _Env, class _Base>
decltype(auto) operator()(_Env && __env, _Base && __base) const noexcept
{
using __env_t = __decay_t<_Env>;
using __base_t = __decay_t<_Base>;
if constexpr (__same_as<__env_t, empty_env>)
{
return _Base((_Base&&)__base);
}
else if constexpr (__same_as<__base_t, empty_env>)
{
return _Env((_Env&&)__env);
}
else
{
return __joined_env<_Env, _Base>{{(_Base&&)__base}, (_Env&&)__env};
}
}
template <class _Env0, class _Env1, class _Env2, class... _Envs>
decltype(auto) operator()(_Env0 && __env0, _Env1 && __env1, _Env2 && __env2,
_Envs&&... __envs) const noexcept
{
const auto& __join_env = *this;
return __join_env(
(_Env0&&)__env0,
__join_env((_Env1&&)__env1,
__join_env((_Env2&&)__env2, (_Envs&&)__envs...)));
}
};
template <class... _Envs>
using __env_join_t = __call_result_t<__join_env_t, _Envs...>;
// To be kept in sync with the promise type used in __connect_awaitable
template <class _Env>
struct __env_promise
{
template <class _Ty>
_Ty&& await_transform(_Ty&& __value) noexcept
{
return (_Ty&&)__value;
}
template <class _Ty>
requires tag_invocable<as_awaitable_t, _Ty, __env_promise&>
auto await_transform(_Ty&& __value) //
noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __env_promise&>)
-> tag_invoke_result_t<as_awaitable_t, _Ty, __env_promise&>
{
return tag_invoke(as_awaitable, (_Ty&&)__value, *this);
}
friend auto tag_invoke(get_env_t, const __env_promise&) noexcept
-> const _Env&;
};
// For making an environment from key/value pairs and optionally
// another environment.
struct __make_env_t
{
template <__nothrow_move_constructible _Base,
__nothrow_move_constructible _Env>
auto operator()(_Base&& __base, _Env&& __env) const noexcept
-> __env_join_t<_Env, _Base>
{
return __join_env_t()((_Env&&)__env, (_Base&&)__base);
}
template <__nothrow_move_constructible _Env>
_Env operator()(_Env&& __env) const noexcept
{
return (_Env&&)__env;
}
};
// For getting an evaluation environment from a receiver
struct get_env_t
{
template <class _EnvProvider>
requires tag_invocable<get_env_t, const _EnvProvider&>
STDEXEC_ATTRIBUTE((always_inline)) //
constexpr auto
operator()(const _EnvProvider& __with_env) 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, __with_env);
}
template <class _EnvProvider>
constexpr empty_env operator()(const _EnvProvider&) const noexcept
{
return {};
}
};
} // namespace __env
using __env::empty_env;
using __empty_env
[[deprecated("Please use stdexec::empty_env now.")]] = empty_env;
using __env::__env_promise;
inline constexpr __env::__make_env_t __make_env{};
inline constexpr __env::__join_env_t __join_env{};
inline constexpr __env::get_env_t get_env{};
// for making an environment from a single key/value pair
inline constexpr __env::__mkprop_t __mkprop{};
template <class _Tag, class _Value = void>
using __with = __env::__prop<_Value(_Tag)>;
template <class... _Ts>
using __make_env_t = __call_result_t<__env::__make_env_t, _Ts...>;
using __default_env = empty_env;
template <class _EnvProvider>
concept environment_provider = //
requires(_EnvProvider& __ep) {
{
get_env(std::as_const(__ep))
} -> queryable;
};
} // namespace stdexec
STDEXEC_PRAGMA_POP()