From da768cb0a87edfa5585f9f32b32cb503d948f28a Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sun, 5 Apr 2026 13:41:04 -0700 Subject: [PATCH 1/3] add the `get_start_scheduler` query per P3941R4 --- .clang-format | 4 +- include/exec/async_scope.hpp | 2 +- include/exec/at_coroutine_exit.hpp | 14 +-- include/exec/on_coro_disposition.hpp | 23 ++-- include/exec/reschedule.hpp | 4 +- include/exec/static_thread_pool.hpp | 6 +- include/exec/task.hpp | 108 +++++++++++------- include/exec/trampoline_scheduler.hpp | 5 +- include/nvexec/stream/sync_wait.cuh | 12 +- include/stdexec/__detail/__affine_on.hpp | 5 +- include/stdexec/__detail/__any.hpp | 102 +++++++++-------- include/stdexec/__detail/__config.hpp | 2 +- .../stdexec/__detail/__connect_awaitable.hpp | 12 +- .../stdexec/__detail/__counting_scopes.hpp | 6 +- include/stdexec/__detail/__domain.hpp | 10 +- include/stdexec/__detail/__execution_fwd.hpp | 2 + include/stdexec/__detail/__on.hpp | 10 +- .../stdexec/__detail/__parallel_scheduler.hpp | 2 +- .../__detail/__parallel_scheduler_backend.hpp | 10 +- include/stdexec/__detail/__read_env.hpp | 6 + include/stdexec/__detail/__run_loop.hpp | 4 +- include/stdexec/__detail/__schedulers.hpp | 55 +++++++-- include/stdexec/__detail/__sync_wait.hpp | 13 +-- include/stdexec/__detail/__task.hpp | 35 +++--- include/stdexec/__detail/__task_scheduler.hpp | 8 +- include/stdexec/__detail/__utility.hpp | 10 ++ test/exec/test_start_detached.cpp | 8 +- test/exec/test_task.cpp | 2 +- test/stdexec/algos/adaptors/test_on.cpp | 9 +- test/stdexec/algos/adaptors/test_on2.cpp | 2 +- test/stdexec/algos/adaptors/test_on3.cpp | 8 +- .../stdexec/algos/factories/test_schedule.cpp | 7 +- test/stdexec/types/test_counting_scopes.cpp | 2 +- 33 files changed, 288 insertions(+), 220 deletions(-) diff --git a/.clang-format b/.clang-format index 9028646bf..03b1ef092 100644 --- a/.clang-format +++ b/.clang-format @@ -71,7 +71,9 @@ EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Leave FixNamespaceComments: true IfMacros: [ - 'STDEXEC_CATCH' + 'STDEXEC_CATCH', + 'STDEXEC_IF_CONSTEVAL', + 'STDEXEC_IF_NOT_CONSTEVAL' ] IncludeBlocks: Preserve IndentAccessModifiers: false diff --git a/include/exec/async_scope.hpp b/include/exec/async_scope.hpp index b7c752286..69366455a 100644 --- a/include/exec/async_scope.hpp +++ b/include/exec/async_scope.hpp @@ -726,7 +726,7 @@ namespace experimental::execution } [[nodiscard]] - constexpr auto query(get_scheduler_t) const noexcept -> STDEXEC::inline_scheduler + constexpr auto query(get_start_scheduler_t) const noexcept -> STDEXEC::inline_scheduler { return {}; } diff --git a/include/exec/at_coroutine_exit.hpp b/include/exec/at_coroutine_exit.hpp index 7d77fe6ff..861ed509e 100644 --- a/include/exec/at_coroutine_exit.hpp +++ b/include/exec/at_coroutine_exit.hpp @@ -30,10 +30,8 @@ namespace experimental::execution { using namespace STDEXEC; - using __any_scheduler_t = - any_receiver_ref>::any_sender<>::any_scheduler<>; + using __any_scheduler_t = any_scheduler>>>; struct __die_on_stop_t { @@ -153,7 +151,7 @@ namespace experimental::execution auto await_suspend(__std::coroutine_handle<_Promise> __parent) noexcept -> bool { // Set the cleanup task's scheduler to the parent coroutine's scheduler. - __coro_.promise().__scheduler_ = get_scheduler(get_env(__parent.promise())); + __coro_.promise().__scheduler_ = get_start_scheduler(get_env(__parent.promise())); // This causes the parent to be resumed after the cleanup action is performed. __coro_.promise().set_continuation(__parent.promise().continuation()); // This causes the parent to invoke the cleanup action when it performs the final @@ -190,13 +188,13 @@ namespace experimental::execution struct __env { - __promise const & __promise_; - [[nodiscard]] - auto query(get_scheduler_t) const noexcept -> __any_scheduler_t + auto query(get_start_scheduler_t) const noexcept -> __any_scheduler_t { return __promise_.__scheduler_; } + + __promise const & __promise_; }; struct __promise : with_awaitable_senders<__promise> diff --git a/include/exec/on_coro_disposition.hpp b/include/exec/on_coro_disposition.hpp index 454467d4e..bfd8385cd 100644 --- a/include/exec/on_coro_disposition.hpp +++ b/include/exec/on_coro_disposition.hpp @@ -32,10 +32,8 @@ namespace experimental::execution { using namespace STDEXEC; - using __any_scheduler = - any_receiver_ref>::any_sender<>::any_scheduler<>; + using __any_scheduler = any_scheduler>>>; template concept __promise_with_disposition = __at_coro_exit::__has_continuation<_Promise> @@ -99,7 +97,7 @@ namespace experimental::execution _Promise& __promise = __std::coroutine_handle<_Promise>::from_address(__parent).promise(); return __promise.disposition(); }; - __coro_.promise().__scheduler_ = get_scheduler(get_env(__parent.promise())); + __coro_.promise().__scheduler_ = get_start_scheduler(get_env(__parent.promise())); __coro_.promise().set_continuation(__parent.promise().continuation()); __parent.promise().set_continuation(__coro_); return false; @@ -131,28 +129,21 @@ namespace experimental::execution struct __env { - __promise const & __promise_; - [[nodiscard]] - auto query(get_scheduler_t) const noexcept -> __any_scheduler + auto query(get_start_scheduler_t) const noexcept -> __any_scheduler { return __promise_.__scheduler_; } + + __promise const & __promise_; }; struct __promise : with_awaitable_senders<__promise> { -#if STDEXEC_EDG() template - __promise(_Action&&, _Ts&&... __ts) noexcept + explicit(STDEXEC_EDG() != 0) __promise(_Action&&, _Ts&... __ts) noexcept : __args_{__ts...} {} -#else - template - explicit __promise(_Action&&, _Ts&... __ts) noexcept - : __args_{__ts...} - {} -#endif auto initial_suspend() noexcept -> __std::suspend_always { diff --git a/include/exec/reschedule.hpp b/include/exec/reschedule.hpp index 97ed4214f..401c38b78 100644 --- a/include/exec/reschedule.hpp +++ b/include/exec/reschedule.hpp @@ -36,7 +36,7 @@ namespace experimental::execution _WITH_ENVIRONMENT_(_Env)>; template - using __schedule_sender_t = schedule_result_t<__call_result_t>; + using __schedule_sender_t = schedule_result_t<__call_result_t>; template using __try_schedule_sender_t = @@ -63,7 +63,7 @@ namespace experimental::execution auto connect(_Receiver __rcvr) const -> connect_result_t<__schedule_sender_t>, _Receiver> { - auto __sched = get_scheduler(STDEXEC::get_env(__rcvr)); + auto __sched = get_start_scheduler(STDEXEC::get_env(__rcvr)); return STDEXEC::connect(STDEXEC::schedule(__sched), static_cast<_Receiver&&>(__rcvr)); } diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index ab288afb8..7f6dbd341 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -283,7 +283,8 @@ namespace experimental::execution { if constexpr (__completes_on) { - auto sched = STDEXEC::get_completion_scheduler(get_env(sndr), env); + auto sched = STDEXEC::get_completion_scheduler(get_env(sndr), + env); static_assert(std::is_same_v); return __apply(_transform_bulk{*sched.pool_}, static_cast(sndr)); } @@ -308,7 +309,8 @@ namespace experimental::execution { if constexpr (__completes_on) { - auto sched = STDEXEC::get_scheduler(env); + auto sched = STDEXEC::get_completion_scheduler(get_env(sndr), + env); return __apply(_transform_iterate{*sched.pool_}, static_cast(sndr)); } else diff --git a/include/exec/task.hpp b/include/exec/task.hpp index d917380d8..a16e4cbed 100644 --- a/include/exec/task.hpp +++ b/include/exec/task.hpp @@ -45,8 +45,7 @@ namespace experimental::execution using __any_scheduler_completions = completion_signatures; - using __any_scheduler = - any_receiver_ref<__any_scheduler_completions>::any_sender<>::any_scheduler<>; + using __any_scheduler = any_scheduler>>; static_assert(scheduler<__any_scheduler>); @@ -59,17 +58,17 @@ namespace experimental::execution }; template - concept __indirect_scheduler_provider = requires(_Ty const & t) { - { get_env(t) } -> __scheduler_provider; + concept __indirect_start_scheduler_provider = requires(_Ty const & t) { + { get_start_scheduler(get_env(t)) } -> scheduler; }; template - constexpr auto __check_parent_promise_has_scheduler() noexcept -> bool + constexpr auto __parent_promise_has_start_scheduler() noexcept -> bool { - static_assert(__indirect_scheduler_provider<_ParentPromise>, + static_assert(__indirect_start_scheduler_provider<_ParentPromise>, "exec::task cannot be co_await-ed in a coroutine that " - "does not have an associated scheduler."); - return __indirect_scheduler_provider<_ParentPromise>; + "does not have an associated start scheduler."); + return __indirect_start_scheduler_provider<_ParentPromise>; } template @@ -84,41 +83,59 @@ namespace experimental::execution __sticky }; + template <__scheduler_affinity _SchedulerAffinity> + struct __optional_scheduler_storage + { + __optional_scheduler_storage() = default; + constexpr explicit __optional_scheduler_storage(__ignore) noexcept {} + }; + + template <> + struct __optional_scheduler_storage<__scheduler_affinity::__sticky> + { + __optional_scheduler_storage() = default; + + template + constexpr explicit __optional_scheduler_storage(_Scheduler __sched) noexcept + : __scheduler_(__sched) + {} + + __any_scheduler __scheduler_{STDEXEC::inline_scheduler{}}; + }; + template <__scheduler_affinity _SchedulerAffinity = __scheduler_affinity::__sticky> - class __default_task_context_impl + class __default_task_context_impl : private __optional_scheduler_storage<_SchedulerAffinity> { template friend struct __default_awaiter_context; - static constexpr bool __with_scheduler = _SchedulerAffinity == __scheduler_affinity::__sticky; + static constexpr bool __with_affinity = _SchedulerAffinity == __scheduler_affinity::__sticky; - STDEXEC_ATTRIBUTE(no_unique_address) - __if_c<__with_scheduler, __any_scheduler, __ignore> __scheduler_{STDEXEC::inline_scheduler{}}; - inplace_stop_token __stop_token_; + inplace_stop_token __stop_token_; public: template constexpr explicit __default_task_context_impl(_ParentPromise& __parent) noexcept { - if constexpr (_SchedulerAffinity == __scheduler_affinity::__sticky) + if constexpr (__with_affinity && __parent_promise_has_start_scheduler<_ParentPromise>()) { - if constexpr (__check_parent_promise_has_scheduler<_ParentPromise>()) - { - __scheduler_ = get_scheduler(get_env(__parent)); - } + // get_start_scheduler is used here to get the parent's "current" scheduler, + // which is the one on which this task has been started (i.e., co_await-ed). + auto __parent_sched = get_start_scheduler(get_env(__parent)); + this->__scheduler_ = __parent_sched; } } template - constexpr explicit __default_task_context_impl(_Scheduler&& __scheduler) - : __scheduler_{static_cast<_Scheduler&&>(__scheduler)} + constexpr explicit __default_task_context_impl(_Scheduler&& __sched) noexcept + : __optional_scheduler_storage<_SchedulerAffinity>{static_cast<_Scheduler&&>(__sched)} {} [[nodiscard]] - constexpr auto query(get_scheduler_t) const noexcept -> __any_scheduler const & - requires(__with_scheduler) + constexpr auto query(get_start_scheduler_t) const noexcept -> __any_scheduler const & + requires(__with_affinity) { - return __scheduler_; + return this->__scheduler_; } [[nodiscard]] @@ -130,7 +147,7 @@ namespace experimental::execution [[nodiscard]] constexpr auto query(get_completion_behavior_t) const noexcept { - if constexpr (__with_scheduler) + if constexpr (__with_affinity) { return completion_behavior::asynchronous_affine | completion_behavior::inline_completion; } @@ -148,16 +165,16 @@ namespace experimental::execution template constexpr void set_scheduler(_Scheduler&& __sched) - requires(__with_scheduler) + requires(__with_affinity) { - __scheduler_ = static_cast<_Scheduler&&>(__sched); + this->__scheduler_ = static_cast<_Scheduler&&>(__sched); } template using promise_context_t = __default_task_context_impl; template - requires(!__with_scheduler) || __indirect_scheduler_provider<_ParentPromise> + requires(!__with_affinity) || __indirect_start_scheduler_provider<_ParentPromise> using awaiter_context_t = __default_awaiter_context<_ParentPromise>; }; @@ -165,7 +182,7 @@ namespace experimental::execution using default_task_context = __default_task_context_impl<__scheduler_affinity::__sticky>; template - using __raw_task_context = __default_task_context_impl<__scheduler_affinity::__none>; + using inline_task_context = __default_task_context_impl<__scheduler_affinity::__none>; // This is the context associated with basic_task's awaiter. By default // it does nothing. @@ -387,7 +404,7 @@ namespace experimental::execution if (!std::exchange(__p.__rescheduled_, true)) { // Create a cleanup action that transitions back onto the current scheduler: - auto __sched = get_scheduler(*__p.__context_); + auto __sched = get_start_scheduler(*__p.__context_); auto __guard = at_coroutine_exit(__compose(unstoppable, STDEXEC::schedule), std::move(__sched)); // Insert the cleanup action into the head of the continuation chain by @@ -483,7 +500,7 @@ namespace experimental::execution private: using __scheduler_t = - __call_result_or_t; + __call_result_or_t; struct __final_awaitable { @@ -523,15 +540,13 @@ namespace experimental::execution [[nodiscard]] constexpr auto disposition() const noexcept -> __task::disposition { - switch (this->__data_.index()) - { - case 0: - return __task::disposition::succeeded; - case 1: - return __task::disposition::failed; - default: - return __task::disposition::stopped; - } + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + STDEXEC_CONSTEXPR_LOCAL __task::disposition __map[] = {__task::disposition::stopped, + __task::disposition::succeeded, + __task::disposition::failed}; + // index() returns 0 for success, 1 for error, and -1 for stopped, so add 1 to + // get the correct disposition. + return __map[this->__data_.index() + 1ul]; } constexpr void unhandled_exception() noexcept @@ -541,7 +556,7 @@ namespace experimental::execution #ifndef __clang_analyzer__ template - requires __scheduler_provider<_Context> + requires __start_scheduler_provider<_Context> auto await_transform(_CvSender&& __sndr) noexcept -> decltype(auto) { if constexpr (__completes_where_it_starts(__sndr), - get_scheduler(*__context_)), + get_start_scheduler(*__context_)), *this); } } template - requires __scheduler_provider<_Context> + requires __start_scheduler_provider<_Context> auto await_transform(__reschedule_coroutine_on_t::__wrapper<_Scheduler> __box) noexcept -> decltype(auto) { @@ -592,6 +607,11 @@ namespace experimental::execution template struct __task_awaiter { + constexpr __task_awaiter(__std::coroutine_handle<__promise> __coro) noexcept + : __coro_(__coro) + {} + STDEXEC_IMMOVABLE(__task_awaiter); + constexpr ~__task_awaiter() { if (__coro_) @@ -631,7 +651,7 @@ namespace experimental::execution return std::move(__var::__get<0>(__coro_.promise().__data_)); } - __std::coroutine_handle<__promise> __coro_; + __std::coroutine_handle<__promise> __coro_{}; __optional> __context_{}; }; @@ -640,7 +660,7 @@ namespace experimental::execution : __coro_(__coro) {} - __std::coroutine_handle __coro_; + __std::coroutine_handle __coro_{}; }; } // namespace __task diff --git a/include/exec/trampoline_scheduler.hpp b/include/exec/trampoline_scheduler.hpp index 00a3c7d23..614ad0c50 100644 --- a/include/exec/trampoline_scheduler.hpp +++ b/include/exec/trampoline_scheduler.hpp @@ -63,11 +63,12 @@ namespace experimental::execution struct __attrs { - template <__one_of _Tag, __queryable_with _Env> + template <__one_of _Tag, + __queryable_with _Env> [[nodiscard]] constexpr auto query(get_completion_scheduler_t<_Tag>, _Env const & __env) const noexcept { - return get_scheduler(__env); + return get_start_scheduler(__env); } template <__one_of _Tag, __queryable_with _Env> diff --git a/include/nvexec/stream/sync_wait.cuh b/include/nvexec/stream/sync_wait.cuh index 8184ac8fa..eae319ed6 100644 --- a/include/nvexec/stream/sync_wait.cuh +++ b/include/nvexec/stream/sync_wait.cuh @@ -35,14 +35,10 @@ namespace nv::execution::_strm { struct env { + template < + __one_of _Query> [[nodiscard]] - constexpr auto query(get_scheduler_t) const noexcept -> run_loop::scheduler - { - return __sched_; - } - - [[nodiscard]] - constexpr auto query(get_delegation_scheduler_t) const noexcept -> run_loop::scheduler + constexpr auto query(_Query) const noexcept -> run_loop::scheduler { return __sched_; } @@ -166,7 +162,7 @@ namespace nv::execution::_strm [[nodiscard]] auto get_env() const noexcept -> env { - return {loop_->get_scheduler()}; + return {loop_->get_start_scheduler()}; } state* state_; diff --git a/include/stdexec/__detail/__affine_on.hpp b/include/stdexec/__detail/__affine_on.hpp index 97e10dff8..9ebd1140d 100644 --- a/include/stdexec/__detail/__affine_on.hpp +++ b/include/stdexec/__detail/__affine_on.hpp @@ -66,7 +66,8 @@ namespace STDEXEC auto &[__tag, __ign, __child] = __sndr; using __child_t = decltype(__child); using __cv_child_t = __copy_cvref_t<_Sender, __child_t>; - using __sched_t = __call_result_or_t, _Env const &>; + using __sched_t = + __call_result_or_t, _Env const &>; if constexpr (!sender_in<__cv_child_t, _Env>) { // NOLINT(bugprone-branch-clone) @@ -109,7 +110,7 @@ namespace STDEXEC // the environment has an infallible scheduler, so we can adapt the sender to run on // that scheduler, which will make it affine. return STDEXEC::__finally_(STDEXEC::__forward_like<_Sender>(__child), - unstoppable(schedule(get_scheduler(__env)))); + unstoppable(schedule(get_start_scheduler(__env)))); } } }; diff --git a/include/stdexec/__detail/__any.hpp b/include/stdexec/__detail/__any.hpp index 8b4402878..5fc3aa6b0 100644 --- a/include/stdexec/__detail/__any.hpp +++ b/include/stdexec/__detail/__any.hpp @@ -668,7 +668,7 @@ namespace STDEXEC::__any {}; template - concept __has_root_kind = std::remove_reference_t<_Interface>::__root_kind == _RootKind; + concept __has_root_kind = _Interface::__root_kind == _RootKind; ////////////////////////////////////////////////////////////////////////////////////////// //! __interface_base @@ -707,32 +707,29 @@ namespace STDEXEC::__any constexpr virtual void __slice_to_(__value_proxy_root<_Interface> &__result) // noexcept(__nothrow_slice) { - STDEXEC_ASSERT(!__empty(*this)); STDEXEC_ASSERT(__can_slice); - if constexpr (_Base::__box_kind != __box_kind::__abstract && __can_slice) + STDEXEC_ASSERT(!__empty(*this)); + + if constexpr (_Base::__box_kind == __box_kind::__abstract || !__can_slice) { - using __root_interface_t = _Base::__interface_type; - constexpr bool __is_root_interface = - std::same_as<__root_interface_t, __this_interface_type>; - STDEXEC_ASSERT(!__is_root_interface); - if constexpr (!__is_root_interface) - { - if constexpr (_Base::__box_kind == __box_kind::__proxy) - { - __value(*this).__slice_to_(__result); - __reset(*this); - } - else if constexpr (_Base::__root_kind == __root_kind::__value) - { - // Move from type-erased values, but not from type-erased references - // potentially throwing: - __result.emplace(std::move(__value(*this))); - } - else - { - __result.emplace(__value(*this)); - } - } + STDEXEC_ASSERT(!"should never be here: __slice_to_ called on a type that cannot be sliced"); + __std::unreachable(); + } + else if constexpr (__same_as) + { + STDEXEC_ASSERT(!"we should be calling __move_to or __copy_to, not __slice_to_"); + __std::unreachable(); + } + else if constexpr (_Base::__box_kind == __box_kind::__proxy) + { + __value(*this).__slice_to_(__result); + __reset(*this); + } + else + { + // Move from type-erased values, but not from type-erased references. + STDEXEC_CONSTEXPR_LOCAL bool __is_value = _Base::__root_kind == __root_kind::__value; + __result.emplace(__value(STDEXEC::__maybe_move<__is_value>(*this))); // could throw } } @@ -1303,7 +1300,7 @@ namespace STDEXEC::__any __reference_proxy_root(__reference_proxy_root &&) = delete; __reference_proxy_root &operator=(__reference_proxy_root &&) = delete; - constexpr void __copy(__reference_proxy_root const &__other) noexcept + constexpr void __copy_from(__reference_proxy_root const &__other) noexcept { STDEXEC_IF_CONSTEVAL { @@ -1761,7 +1758,7 @@ namespace STDEXEC::__any && (_Other::__root_kind == __root_kind::__value) constexpr __any(_Interface<_Other> __other) noexcept(__as_large_as<_Other>) { - (*this).__assign(std::move(__other)); + (*this).__assign_from(std::move(__other)); } template @@ -1770,8 +1767,8 @@ namespace STDEXEC::__any constexpr __any(_Interface<_Other> const &__other) { _Interface<_Other> __tmp; - __tmp.__copy(__other); - (*this).__assign(std::move(__tmp)); + __tmp.__copy_from(__other); + (*this).__assign_from(std::move(__tmp)); } template <__model_of<_Interface> _Value> @@ -1789,7 +1786,7 @@ namespace STDEXEC::__any constexpr __any &operator=(_Interface<_Other> __other) noexcept(__as_large_as<_Other>) { __reset(*this); - (*this).__assign(std::move(__other)); + (*this).__assign_from(std::move(__other)); return *this; } @@ -1803,10 +1800,10 @@ namespace STDEXEC::__any return *this; _Interface<_Other> __tmp; - __tmp.__copy(__other); + __tmp.__copy_from(__other); __reset(*this); - (*this).__assign(std::move(__tmp)); + (*this).__assign_from(std::move(__tmp)); return *this; } @@ -1817,32 +1814,38 @@ namespace STDEXEC::__any } private: - // Assigning from a type that __extends _Interface. _Its buffer may be larger than + // Assigning from a type that __extends _Interface. Its buffer may be larger than // ours, or it may be a reference type, so we can be only conditionally // noexcept. template requires __extension_of<_Interface<_Other>, __imovable> - constexpr void __assign(_Interface<_Other> &&__other) noexcept(__as_large_as<_Other>) + constexpr void __assign_from(_Interface<_Other> &&__other) noexcept(__as_large_as<_Other>) { - constexpr bool __ptr_convertible = std::derived_from<_Other, __iabstract<_Interface>>; + using __root_t = __value_proxy_root<_Interface>; + STDEXEC_CONSTEXPR_LOCAL + bool __is_value = _Other::__root_kind == __root_kind::__value; + STDEXEC_CONSTEXPR_LOCAL + bool __ptr_convertible = std::derived_from<_Other, __iabstract<_Interface>>; if (__empty(__other)) - { return; - } - else if constexpr (_Other::__root_kind == __root_kind::__reference || !__ptr_convertible) - { // NOLINT(bugprone-branch-clone) + + if constexpr (!__is_value || !__ptr_convertible) // compile-time condition + { // NOLINT(bugprone-branch-clone) return __other.__slice_to_(*this); } - else if (__other.__in_situ_()) + else if (__other.__in_situ_()) // runtime condition { - return __other.__slice_to_(*this); + using __other_interface_t = _Other::__interface_type; + if constexpr (__same_as<__other_interface_t, __iabstract<_Interface>>) + static_cast<__root_t &>(*this) = static_cast<__root_t &&>(__other); + else + return __other.__slice_to_(*this); + } + else STDEXEC_IF_CONSTEVAL + { + (*this).__root_ptr_ = std::exchange(__other.__root_ptr_, nullptr); } - else - STDEXEC_IF_CONSTEVAL - { - (*this).__root_ptr_ = std::exchange(__other.__root_ptr_, nullptr); - } else { auto &__this_ptr = *__std::start_lifetime_as<__tagged_ptr>((*this).__buff_); @@ -1868,7 +1871,7 @@ namespace STDEXEC::__any constexpr __any_ptr_base(__any_ptr_base const &__other) noexcept : __reference_() { - __reference_.__copy(__other.__reference_); + __reference_.__copy_from(__other.__reference_); } template