diff --git a/include/unifex/schedule_with_subscheduler.hpp b/include/unifex/schedule_with_subscheduler.hpp index 94fc5c16..0ae40615 100644 --- a/include/unifex/schedule_with_subscheduler.hpp +++ b/include/unifex/schedule_with_subscheduler.hpp @@ -70,7 +70,9 @@ inline const struct _fn { operator()(Scheduler&& sched) const -> _result_t { auto&& scheduleOp = schedule(sched); return _result_t{ - static_cast(scheduleOp), {(Scheduler &&) sched}}; + static_cast(scheduleOp), + {(Scheduler &&) sched}, + instruction_ptr::read_return_address()}; } constexpr auto operator()() const noexcept(std::is_nothrow_invocable_v, _fn>) diff --git a/include/unifex/sequence.hpp b/include/unifex/sequence.hpp index affd58b7..aa3b995c 100644 --- a/include/unifex/sequence.hpp +++ b/include/unifex/sequence.hpp @@ -297,11 +297,12 @@ struct _sndr::type { constructible_from< Successor, Successor2>) // - explicit type(Predecessor2&& predecessor, Successor2&& successor) noexcept( + explicit type(Predecessor2&& predecessor, Successor2&& successor, instruction_ptr returnAddress) noexcept( std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : predecessor_(static_cast(predecessor)) - , successor_(static_cast(successor)) {} + , successor_(static_cast(successor)) + , returnAddress_(returnAddress) {} friend constexpr blocking_kind tag_invoke(tag_t, const type& self) { blocking_kind pred = blocking(self.predecessor_); @@ -311,6 +312,11 @@ struct _sndr::type { return std::max(pred(), std::min(succ(), blocking_kind::maybe())); } + friend instruction_ptr + tag_invoke(tag_t, const type& t) noexcept { + return t.returnAddress_; + } + template(typename Receiver, typename Sender) // (requires same_as, type> AND constructible_from> AND @@ -338,84 +344,123 @@ struct _sndr::type { private: UNIFEX_NO_UNIQUE_ADDRESS Predecessor predecessor_; UNIFEX_NO_UNIQUE_ADDRESS Successor successor_; + instruction_ptr returnAddress_; }; namespace _cpo { struct _fn { - // Sequencing a single sender is just the same as returning the sender - // itself. - template - remove_cvref_t operator()(First&& first) const - noexcept(std::is_nothrow_constructible_v, First>) { - return static_cast(first); - } +private: + struct _impl_fn { + // Sequencing a single sender is just the same as returning the sender + // itself. + template + remove_cvref_t operator()(instruction_ptr, First&& first) const + noexcept( + std::is_nothrow_constructible_v, First>) { + return static_cast(first); + } - template(typename First, typename Second) // - (requires sender AND sender AND - tag_invocable<_fn, First, Second>) // - auto - operator()(First&& first, Second&& second) const - noexcept(is_nothrow_tag_invocable_v<_fn, First, Second>) - -> tag_invoke_result_t<_fn, First, Second> { - return unifex::tag_invoke( - _fn{}, static_cast(first), static_cast(second)); - } + template(typename First, typename Second) // + (requires sender AND sender AND + tag_invocable<_fn, First, Second>) // + auto + operator()(instruction_ptr, First&& first, Second&& second) const + noexcept(is_nothrow_tag_invocable_v<_fn, First, Second>) + -> tag_invoke_result_t<_fn, First, Second> { + return unifex::tag_invoke( + _fn{}, static_cast(first), static_cast(second)); + } - template(typename First, typename Second) // - (requires sender AND - sender AND(!tag_invocable<_fn, First, Second>)) // - auto - operator()(First&& first, Second&& second) const - noexcept(std::is_nothrow_constructible_v< - _seq::_sender, - First, - Second>) -> _seq::_sender { - return _seq::_sender{ - static_cast(first), static_cast(second)}; - } + template(typename First, typename Second) // + (requires sender AND + sender AND(!tag_invocable<_fn, First, Second>)) // + auto + operator()( + instruction_ptr returnAddress, First&& first, Second&& second) const + noexcept(std::is_nothrow_constructible_v< + _seq::_sender, + First, + Second, + instruction_ptr>) -> _seq::_sender { + return _seq::_sender{ + static_cast(first), + static_cast(second), + returnAddress}; + } - template( - typename First, typename Second, typename Third, typename... Rest) // - (requires sender AND sender AND - sender AND(sender&&...) - AND tag_invocable<_fn, First, Second, Third, Rest...>) // - auto - operator()( - First&& first, Second&& second, Third&& third, Rest&&... rest) const - noexcept(is_nothrow_tag_invocable_v<_fn, First, Second, Third, Rest...>) - -> tag_invoke_result_t<_fn, First, Second, Third, Rest...> { - return unifex::tag_invoke( - _fn{}, - static_cast(first), - static_cast(second), - static_cast(third), - static_cast(rest)...); - } + template( + typename First, typename Second, typename Third, typename... Rest) // + (requires sender AND sender AND + sender AND(sender&&...) + AND tag_invocable<_fn, First, Second, Third, Rest...>) // + auto + operator()( + instruction_ptr, + First&& first, + Second&& second, + Third&& third, + Rest&&... rest) const + noexcept(is_nothrow_tag_invocable_v<_fn, First, Second, Third, Rest...>) + -> tag_invoke_result_t<_fn, First, Second, Third, Rest...> { + return unifex::tag_invoke( + _fn{}, + static_cast(first), + static_cast(second), + static_cast(third), + static_cast(rest)...); + } - template( - typename First, typename Second, typename Third, typename... Rest) // - (requires sender AND sender AND - sender AND(sender&&...) - AND(!tag_invocable<_fn, First, Second, Third, Rest...>)) // - auto - operator()(First&& first, Second&& second, Third&& third, Rest&&... rest) - const noexcept( - std::is_nothrow_invocable_v<_fn, First, Second> && - std::is_nothrow_invocable_v< - _fn, - std::invoke_result_t<_fn, First, Second>, - Third, - Rest...>) - -> std::invoke_result_t< - _fn, - std::invoke_result_t<_fn, First, Second>, - Third, - Rest...> { - // Fall-back to pair-wise invocation of the sequence() CPO. - return (*this)( - (*this)(static_cast(first), static_cast(second)), - static_cast(third), - static_cast(rest)...); + template( + typename First, typename Second, typename Third, typename... Rest) // + (requires sender AND sender AND + sender AND(sender&&...) + AND(!tag_invocable<_fn, First, Second, Third, Rest...>)) // + auto + operator()( + instruction_ptr returnAddress, + First&& first, + Second&& second, + Third&& third, + Rest&&... rest) const + noexcept( + std::is_nothrow_invocable_v< + _impl_fn, + instruction_ptr, + First, + Second> && + std::is_nothrow_invocable_v< + _impl_fn, + instruction_ptr, + std::invoke_result_t<_fn, First, Second>, + Third, + Rest...>) + -> std::invoke_result_t< + _impl_fn, + instruction_ptr, + std::invoke_result_t<_fn, First, Second>, + Third, + Rest...> { + // Fall-back to pair-wise invocation of the sequence() CPO. + return (*this)( + returnAddress, + (*this)( + returnAddress, + static_cast(first), + static_cast(second)), + static_cast(third), + static_cast(rest)...); + } + }; + +public: + template + auto operator()(First&& first, Rest&&... rest) const noexcept( + std::is_nothrow_invocable_v<_impl_fn, instruction_ptr, First, Rest...>) + -> std::invoke_result_t<_impl_fn, instruction_ptr, First, Rest...> { + return _impl_fn{}( + instruction_ptr::read_return_address(), + std::forward(first), + std::forward(rest)...); } }; } // namespace _cpo diff --git a/include/unifex/then.hpp b/include/unifex/then.hpp index 1570bc27..85e338f2 100644 --- a/include/unifex/then.hpp +++ b/include/unifex/then.hpp @@ -63,31 +63,31 @@ struct _receiver::type { void set_value(Values&&... values) && noexcept { using result_type = std::invoke_result_t; if constexpr (std::is_void_v) { - if constexpr (noexcept(std::invoke((Func&&)func_, (Values&&)values...))) { - std::invoke((Func&&)func_, (Values&&)values...); - unifex::set_value((Receiver&&)receiver_); + if constexpr (std::is_nothrow_invocable_v) { + std::invoke(std::move(func_), std::forward(values)...); + unifex::set_value(std::move(receiver_)); } else { UNIFEX_TRY { - std::invoke((Func&&)func_, (Values&&)values...); - unifex::set_value((Receiver&&)receiver_); + std::invoke(std::move(func_), std::forward(values)...); + unifex::set_value(std::move(receiver_)); } UNIFEX_CATCH(...) { - unifex::set_error((Receiver&&)receiver_, std::current_exception()); + unifex::set_error(std::move(receiver_), std::current_exception()); } } } else { - if constexpr (noexcept(std::invoke((Func&&)func_, (Values&&)values...))) { + if constexpr (std::is_nothrow_invocable_v) { unifex::set_value( - (Receiver&&)receiver_, - std::invoke((Func&&)func_, (Values&&)values...)); + std::move(receiver_), + std::invoke(std::move(func_), std::forward(values)...)); } else { UNIFEX_TRY { unifex::set_value( - (Receiver&&)receiver_, - std::invoke((Func&&)func_, (Values&&)values...)); + std::move(receiver_), + std::invoke(std::move(func_), std::forward(values)...)); } UNIFEX_CATCH(...) { - unifex::set_error((Receiver&&)receiver_, std::current_exception()); + unifex::set_error(std::move(receiver_), std::current_exception()); } } } @@ -95,10 +95,10 @@ struct _receiver::type { template void set_error(Error&& error) && noexcept { - unifex::set_error((Receiver&&)receiver_, (Error&&)error); + unifex::set_error(std::move(receiver_), std::forward(error)); } - void set_done() && noexcept { unifex::set_done((Receiver&&)receiver_); } + void set_done() && noexcept { unifex::set_done(std::move(receiver_)); } template(typename CPO, typename R) // (requires is_receiver_query_cpo_v AND same_as) // @@ -129,6 +129,7 @@ template struct _sender::type { UNIFEX_NO_UNIQUE_ADDRESS Predecessor pred_; UNIFEX_NO_UNIQUE_ADDRESS Func func_; + instruction_ptr returnAddress_; private: // This helper transforms an argument list into either @@ -179,52 +180,81 @@ struct _sender::type { member_t, receiver_t>> { return unifex::connect( - static_cast(s).pred_, + std::forward(s).pred_, receiver_t>{ - static_cast(s).func_, static_cast(r)}); + std::forward(s).func_, std::forward(r)}); } friend constexpr blocking_kind tag_invoke(tag_t, const type& self) noexcept { return unifex::blocking(self.pred_); } + + friend instruction_ptr + tag_invoke(tag_t, const type& t) noexcept { + return t.returnAddress_; + } }; namespace _cpo { struct _fn { private: - template - using _result_t = typename conditional_t< - tag_invocable<_fn, Sender, Func>, - meta_tag_invoke_result<_fn>, - meta_quote2<_then::sender>>::template apply; + struct _impl_fn { + template(typename Sender, typename Func) // + (requires tag_invocable<_fn, Sender, Func>) // + auto + operator()(Sender&& predecessor, Func&& func, instruction_ptr) const + noexcept(is_nothrow_tag_invocable_v<_fn, Sender, Func>) + -> tag_invoke_result_t<_fn, Sender, Func> { + return unifex::tag_invoke( + _fn{}, std::forward(predecessor), std::forward(func)); + } + + template(typename Sender, typename Func) // + (requires(!tag_invocable<_fn, Sender, Func>)) // + auto + operator()( + Sender&& predecessor, + Func&& func, + instruction_ptr returnAddress) const + noexcept(std::is_nothrow_constructible_v< + _then::sender, + Sender, + Func, + instruction_ptr>) -> _then::sender { + return _then::sender{ + std::forward(predecessor), + std::forward(func), + returnAddress}; + } + }; public: - template(typename Sender, typename Func) // - (requires tag_invocable<_fn, Sender, Func>) // - auto - operator()(Sender&& predecessor, Func&& func) const - noexcept(is_nothrow_tag_invocable_v<_fn, Sender, Func>) - -> _result_t { - return unifex::tag_invoke(_fn{}, (Sender&&)predecessor, (Func&&)func); - } - template(typename Sender, typename Func) // - (requires(!tag_invocable<_fn, Sender, Func>)) // - auto - operator()(Sender&& predecessor, Func&& func) const - noexcept(std::is_nothrow_constructible_v< - _then::sender, - Sender, - Func>) -> _result_t { - return _then::sender{(Sender&&)predecessor, (Func&&)func}; + template + auto operator()(Sender&& predecessor, Func&& func) const noexcept( + std::is_nothrow_invocable_v<_impl_fn, Sender, Func, instruction_ptr>) + -> std::invoke_result_t<_impl_fn, Sender, Func, instruction_ptr> { + return _impl_fn{}( + std::forward(predecessor), + std::forward(func), + instruction_ptr::read_return_address()); } + template constexpr auto operator()(Func&& func) const - noexcept(std::is_nothrow_invocable_v, _fn, Func>) - -> bind_back_result_t<_fn, Func> { - return bind_back(*this, (Func&&)func); + noexcept(std::is_nothrow_invocable_v< + tag_t, + _impl_fn, + Func, + instruction_ptr>) + -> bind_back_result_t<_impl_fn, Func, instruction_ptr> { + return bind_back( + _impl_fn{}, + std::forward(func), + instruction_ptr::read_return_address()); } }; + } // namespace _cpo } // namespace _then diff --git a/include/unifex/with_query_value.hpp b/include/unifex/with_query_value.hpp index da386685..8234e14c 100644 --- a/include/unifex/with_query_value.hpp +++ b/include/unifex/with_query_value.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with @@ -142,9 +142,10 @@ class _sender::type { sender_traits::is_always_scheduler_affine; template - explicit type(Sender2&& sender, Value2&& value) + explicit type(Sender2&& sender, Value2&& value, instruction_ptr returnAddress) : sender_((Sender2 &&) sender) - , value_((Value2 &&) value) {} + , value_((Value2 &&) value) + , returnAddress_(returnAddress) {} template(typename Self, typename Receiver) // (requires same_as, type> AND @@ -181,28 +182,58 @@ class _sender::type { return unifex::blocking(s.sender_); } + friend instruction_ptr + tag_invoke(tag_t, const type& t) noexcept { + return t.returnAddress_; + } + private: Sender sender_; Value value_; + instruction_ptr returnAddress_; }; } // namespace _with_query_value namespace _with_query_value_cpo { inline const struct _fn { + struct _impl_fn { + template + _with_query_value::sender operator()( + Sender&& sender, + CPO, + Value&& value, + instruction_ptr returnAddress) const { + static_assert( + std::is_empty_v, + "with_query_value() does not support stateful CPOs"); + return _with_query_value::sender{ + (Sender&&)sender, (Value&&)value, returnAddress}; + } + }; + template - _with_query_value::sender - operator()(Sender&& sender, CPO, Value&& value) const { - static_assert( - std::is_empty_v, - "with_query_value() does not support stateful CPOs"); - return _with_query_value::sender{ - (Sender &&) sender, (Value &&) value}; + auto operator()(Sender&& sender, CPO cpo, Value&& value) const { + return _impl_fn{}( + std::forward(sender), + std::forward(cpo), + std::forward(value), + instruction_ptr::read_return_address()); } + template constexpr auto operator()(const CPO&, Value&& value) const - noexcept(std::is_nothrow_invocable_v, _fn, CPO, Value>) - -> bind_back_result_t<_fn, CPO, Value> { - return bind_back(*this, CPO{}, (Value &&) value); + noexcept(std::is_nothrow_invocable_v< + tag_t, + _fn, + CPO, + Value, + instruction_ptr>) + -> bind_back_result_t<_impl_fn, CPO, Value, instruction_ptr> { + return bind_back( + _impl_fn{}, + CPO{}, + (Value&&)value, + instruction_ptr::read_return_address()); } } with_query_value{}; } // namespace _with_query_value_cpo