1 /* 2 * Copyright (c) 2021-2024 NVIDIA Corporation 3 * 4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * https://llvm.org/LICENSE.txt 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "__execution_fwd.hpp" 19 20 // include these after __execution_fwd.hpp 21 #include "__awaitable.hpp" 22 #include "__completion_signatures.hpp" 23 #include "__concepts.hpp" 24 #include "__connect_awaitable.hpp" 25 #include "__debug.hpp" 26 #include "__env.hpp" 27 #include "__operation_states.hpp" 28 #include "__receivers.hpp" 29 #include "__senders_core.hpp" 30 #include "__tag_invoke.hpp" 31 #include "__transform_completion_signatures.hpp" 32 #include "__transform_sender.hpp" 33 #include "__type_traits.hpp" 34 35 namespace stdexec { 36 ///////////////////////////////////////////////////////////////////////////// 37 // [execution.get_completion_signatures] 38 namespace __detail { 39 struct __dependent_completions { }; 40 41 using dependent_completions = _ERROR_<__detail::__dependent_completions>; 42 43 template <class _Completions> 44 concept __well_formed_completions_helper = __valid_completion_signatures<_Completions> 45 || __same_as<_Completions, dependent_completions>; 46 47 template <class _Completions> 48 inline constexpr bool __well_formed_completions_v = 49 __well_formed_completions_helper<_Completions>; 50 51 template <class _Completions> 52 concept __well_formed_completions = __well_formed_completions_v<_Completions>; 53 } // namespace __detail 54 55 using __detail::dependent_completions; 56 57 namespace __sigs { 58 template <class _Sender, class _Env> 59 using __tfx_sender = 60 transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>; 61 62 template <class _Sender, class... _Env> 63 using __member_result_t = decltype(__declval<_Sender>() 64 .get_completion_signatures(__declval<_Env>()...)); 65 66 template <class _Sender, class... _Env> 67 using __static_member_result_t = decltype(STDEXEC_REMOVE_REFERENCE( 68 _Sender)::get_completion_signatures(__declval<_Sender>(), __declval<_Env>()...)); 69 70 template <class _Sender, class... _Env> 71 concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>; 72 73 template <class _Sender, class... _Env> 74 concept __with_static_member = __mvalid<__static_member_result_t, _Sender, _Env...>; 75 76 template <class _Sender, class... _Env> 77 concept __with_tag_invoke = tag_invocable<get_completion_signatures_t, _Sender, _Env...>; 78 79 template <class _Sender, class... _Env> 80 concept __with_legacy_tag_invoke = (sizeof...(_Env) == 0) 81 && tag_invocable<get_completion_signatures_t, _Sender, env<>>; 82 83 template <class _Sender> 84 using __member_alias_t = __decay_t<_Sender>::completion_signatures; 85 86 template <class _Sender> 87 concept __with_member_alias = __mvalid<__member_alias_t, _Sender>; 88 89 struct get_completion_signatures_t { 90 template <class _Sender, class... _Env> 91 requires(sizeof...(_Env) <= 1) __implstdexec::__sigs::get_completion_signatures_t92 static auto __impl() { 93 // Compute the type of the transformed sender: 94 using __tfx_fn = __if_c<sizeof...(_Env) == 0, __mconst<_Sender>, __q<__tfx_sender>>; 95 using _TfxSender = __minvoke<__tfx_fn, _Sender, _Env...>; 96 97 if constexpr (__merror<_TfxSender>) { 98 // Computing the type of the transformed sender returned an error type. Propagate it. 99 return static_cast<_TfxSender (*)()>(nullptr); 100 } else if constexpr (__with_member_alias<_TfxSender>) { 101 using _Result = __member_alias_t<_TfxSender>; 102 return static_cast<_Result (*)()>(nullptr); 103 } else if constexpr (__with_static_member<_TfxSender, _Env...>) { 104 using _Result = __static_member_result_t<_TfxSender, _Env...>; 105 return static_cast<_Result (*)()>(nullptr); 106 } else if constexpr (__with_member<_TfxSender, _Env...>) { 107 using _Result = __member_result_t<_TfxSender, _Env...>; 108 return static_cast<_Result (*)()>(nullptr); 109 } else if constexpr (__with_tag_invoke<_TfxSender, _Env...>) { 110 using _Result = tag_invoke_result_t<get_completion_signatures_t, _TfxSender, _Env...>; 111 return static_cast<_Result (*)()>(nullptr); 112 } else if constexpr (__with_legacy_tag_invoke<_TfxSender, _Env...>) { 113 // This branch is strictly for backwards compatibility 114 using _Result = tag_invoke_result_t<get_completion_signatures_t, _Sender, env<>>; 115 return static_cast<_Result (*)()>(nullptr); 116 // [WAR] The explicit cast to bool below is to work around a bug in nvc++ (nvbug#4707793) 117 } else if constexpr (bool(__awaitable<_TfxSender, __env::__promise<_Env>...>)) { 118 using _AwaitResult = __await_result_t<_TfxSender, __env::__promise<_Env>...>; 119 using _Result = completion_signatures< 120 // set_value_t() or set_value_t(T) 121 __minvoke<__mremove<void, __qf<set_value_t>>, _AwaitResult>, 122 set_error_t(std::exception_ptr), 123 set_stopped_t() 124 >; 125 return static_cast<_Result (*)()>(nullptr); 126 } else if constexpr (sizeof...(_Env) == 0) { 127 // It's possible this is a dependent sender. 128 return static_cast<dependent_completions (*)()>(nullptr); 129 } else if constexpr ((__is_debug_env<_Env> || ...)) { 130 // This ought to cause a hard error that indicates where the problem is. 131 using _Completions [[maybe_unused]] = 132 decltype(std::remove_reference_t<_TfxSender>::get_completion_signatures( 133 __declval<_TfxSender>(), __declval<_Env>()...)); 134 return static_cast<__debug::__completion_signatures (*)()>(nullptr); 135 } else { 136 using _Result = __mexception< 137 _UNRECOGNIZED_SENDER_TYPE_<>, 138 _WITH_SENDER_<_Sender>, 139 _WITH_ENVIRONMENT_<_Env>... 140 >; 141 return static_cast<_Result (*)()>(nullptr); 142 } 143 } 144 145 // NOT TO SPEC: if we're unable to compute the completion signatures, 146 // return an error type instead of SFINAE. 147 template <class _Sender, class... _Env> 148 requires(sizeof...(_Env) <= 1) 149 constexpr auto operator ()stdexec::__sigs::get_completion_signatures_t150 operator()(_Sender&&, _Env&&...) const noexcept -> decltype(__impl<_Sender, _Env...>()()) { 151 return {}; 152 } 153 }; 154 } // namespace __sigs 155 156 using __sigs::get_completion_signatures_t; 157 inline constexpr get_completion_signatures_t get_completion_signatures{}; 158 159 ///////////////////////////////////////////////////////////////////////////// 160 // [execution.senders.connect] 161 namespace __connect { 162 template <class _Sender, class _Receiver> 163 using __tfx_sender = __mmemoize_q< 164 transform_sender_result_t, 165 __late_domain_of_t<_Sender, env_of_t<_Receiver>>, 166 _Sender, 167 env_of_t<_Receiver> 168 >; 169 170 template <class _Sender, class _Receiver> 171 using __member_result_t = decltype(__declval<_Sender>().connect(__declval<_Receiver>())); 172 173 template <class _Sender, class _Receiver> 174 using __static_member_result_t = decltype(STDEXEC_REMOVE_REFERENCE( 175 _Sender)::connect(__declval<_Sender>(), __declval<_Receiver>())); 176 177 template <class _Sender, class _Receiver> 178 concept __with_member = __mvalid<__member_result_t, _Sender, _Receiver>; 179 180 template <class _Sender, class _Receiver> 181 concept __with_static_member = __mvalid<__static_member_result_t, _Sender, _Receiver>; 182 183 template <class _Sender, class _Receiver> 184 concept __with_tag_invoke = tag_invocable<connect_t, _Sender, _Receiver>; 185 186 template <class _Sender, class _Receiver> 187 concept __with_co_await = __callable<__connect_awaitable_t, _Sender, _Receiver>; 188 189 struct _NO_USABLE_CONNECT_CUSTOMIZATION_FOUND_ { 190 void operator()() const noexcept = delete; 191 }; 192 193 struct connect_t { 194 template <class _Sender, class _Receiver> 195 requires sender_in<_Sender, env_of_t<_Receiver>> && __receiver_from<_Receiver, _Sender> STDEXEC_ATTRIBUTEstdexec::__connect::connect_t196 STDEXEC_ATTRIBUTE(always_inline) 197 static constexpr auto __type_check_arguments() -> bool { 198 if constexpr (sender_in<_Sender, env_of_t<_Receiver>>) { 199 // Instantiate __debug_sender via completion_signatures_of_t to check that the actual 200 // completions match the expected completions. 201 using __checked_signatures 202 [[maybe_unused]] = completion_signatures_of_t<_Sender, env_of_t<_Receiver>>; 203 } else { 204 __diagnose_sender_concept_failure<_Sender, env_of_t<_Receiver>>(); 205 } 206 return true; 207 } 208 209 template <class _OpState> __check_operation_statestdexec::__connect::connect_t210 static constexpr void __check_operation_state() noexcept { 211 static_assert(operation_state<_OpState>, STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER); 212 } 213 214 template <class _Sender, class _Receiver> __select_implstdexec::__connect::connect_t215 static constexpr auto __select_impl() noexcept { 216 using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>; 217 using _TfxSender = __tfx_sender<_Sender, _Receiver>; 218 constexpr bool _NothrowTfxSender = 219 __nothrow_callable<transform_sender_t, _Domain, _Sender, env_of_t<_Receiver>>; 220 221 static_assert(sender<_Sender>, "The first argument to stdexec::connect must be a sender"); 222 static_assert( 223 receiver<_Receiver>, "The second argument to stdexec::connect must be a receiver"); 224 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 225 static_assert(__type_check_arguments<_TfxSender, _Receiver>()); 226 #endif 227 228 if constexpr (__with_static_member<_TfxSender, _Receiver>) { 229 using _Result = __static_member_result_t<_TfxSender, _Receiver>; 230 __check_operation_state<_Result>(); 231 constexpr bool _Nothrow = _NothrowTfxSender 232 && noexcept( 233 __declval<_TfxSender>() 234 .connect(__declval<_TfxSender>(), __declval<_Receiver>())); 235 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 236 } else if constexpr (__with_member<_TfxSender, _Receiver>) { 237 using _Result = __member_result_t<_TfxSender, _Receiver>; 238 __check_operation_state<_Result>(); 239 constexpr bool _Nothrow = _NothrowTfxSender 240 && noexcept(__declval<_TfxSender>() 241 .connect(__declval<_Receiver>())); 242 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 243 } else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) { 244 using _Result = tag_invoke_result_t<connect_t, _TfxSender, _Receiver>; 245 __check_operation_state<_Result>(); 246 constexpr bool _Nothrow = _NothrowTfxSender 247 && nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>; 248 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 249 } else if constexpr (__with_co_await<_TfxSender, _Receiver>) { 250 using _Result = __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>; 251 return static_cast<_Result (*)()>(nullptr); 252 } else if constexpr (__is_debug_env<env_of_t<_Receiver>>) { 253 using _Result = __debug::__debug_operation; 254 return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(nullptr); 255 } else { 256 return _NO_USABLE_CONNECT_CUSTOMIZATION_FOUND_(); 257 } 258 } 259 260 template <class _Sender, class _Receiver> 261 using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>()); 262 263 template <class _Sender, class _Receiver> operator ()stdexec::__connect::connect_t264 auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const 265 noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>) 266 -> __call_result_t<__select_impl_t<_Sender, _Receiver>> { 267 268 static_assert(sender_in<_Sender, env_of_t<_Receiver>>); 269 static_assert(__receiver_from<_Receiver, _Sender>); 270 271 using _TfxSender = __tfx_sender<_Sender, _Receiver>; 272 auto&& __env = get_env(__rcvr); 273 auto __domain = __get_late_domain(__sndr, __env); 274 275 if constexpr (__with_static_member<_TfxSender, _Receiver>) { 276 auto&& __tfx_sndr = transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env); 277 return __tfx_sndr 278 .connect(static_cast<_TfxSender&&>(__tfx_sndr), static_cast<_Receiver&&>(__rcvr)); 279 } else if constexpr (__with_member<_TfxSender, _Receiver>) { // NOLINT(bugprone-branch-clone) 280 return transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env) 281 .connect(static_cast<_Receiver&&>(__rcvr)); 282 } else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) { 283 return tag_invoke( 284 connect_t(), 285 transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env), 286 static_cast<_Receiver&&>(__rcvr)); 287 } else if constexpr (__with_co_await<_TfxSender, _Receiver>) { 288 return __connect_awaitable( 289 transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env), 290 static_cast<_Receiver&&>(__rcvr)); 291 } else { 292 // This should generate an instantiation backtrace that contains useful 293 // debugging information. 294 return transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env) 295 .connect(static_cast<_Receiver&&>(__rcvr)); 296 } 297 } 298 querystdexec::__connect::connect_t299 static constexpr auto query(forwarding_query_t) noexcept -> bool { 300 return false; 301 } 302 }; 303 } // namespace __connect 304 305 using __connect::connect_t; 306 inline constexpr __connect::connect_t connect{}; 307 308 ///////////////////////////////////////////////////////////////////////////// 309 // [exec.snd] 310 template <class _Tag, class... _Args> 311 auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag; 312 template <class _Sig> 313 using __tag_of_sig_t = decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr))); 314 315 template <class _Sender, class _SetSig, class _Env = env<>> 316 concept sender_of = sender_in<_Sender, _Env> 317 && same_as< 318 __types<_SetSig>, 319 __gather_completions_of< 320 __tag_of_sig_t<_SetSig>, 321 _Sender, 322 _Env, 323 __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>, 324 __mconcat<__qq<__types>> 325 > 326 >; 327 328 template <class _Error> 329 requires false 330 using __nofail_t = _Error; 331 332 template <class _Sender, class _Env = env<>> 333 concept __nofail_sender = sender_in<_Sender, _Env> && requires { 334 typename __gather_completion_signatures< 335 __completion_signatures_of_t<_Sender, _Env>, 336 set_error_t, 337 __nofail_t, 338 __sigs::__default_completion, 339 __types 340 >; 341 }; 342 343 ///////////////////////////////////////////////////////////////////////////// 344 // early sender type-checking 345 template <class _Sender> 346 concept __well_formed_sender = __detail::__well_formed_completions< 347 __minvoke<__with_default_q<__completion_signatures_of_t, dependent_completions>, _Sender> 348 >; 349 } // namespace stdexec 350