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 "__transform_completion_signatures.hpp" 31 #include "__transform_sender.hpp" 32 #include "__type_traits.hpp" 33 34 namespace stdexec 35 { 36 ///////////////////////////////////////////////////////////////////////////// 37 // [execution.get_completion_signatures] 38 namespace __detail 39 { 40 struct __dependent_completions 41 {}; 42 43 template <class _Completions> 44 concept __well_formed_sender = 45 __valid_completion_signatures<_Completions> || 46 __same_as<_Completions, _ERROR_<__dependent_completions>>; 47 } // namespace __detail 48 49 using dependent_completions = _ERROR_<__detail::__dependent_completions>; 50 51 namespace __sigs 52 { 53 template <class _Sender, class _Env> 54 using __tfx_sender = 55 transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>; 56 57 template <class _Sender, class... _Env> 58 using __member_result_t = 59 decltype(__declval<_Sender>().get_completion_signatures( 60 __declval<_Env>()...)); 61 62 template <class _Sender, class... _Env> 63 using __static_member_result_t = // 64 decltype(STDEXEC_REMOVE_REFERENCE(_Sender) // 65 ::get_completion_signatures(__declval<_Sender>(), 66 __declval<_Env>()...)); 67 68 template <class _Sender, class... _Env> 69 concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>; 70 71 template <class _Sender, class... _Env> 72 concept __with_static_member = 73 __mvalid<__static_member_result_t, _Sender, _Env...>; 74 75 template <class _Sender, class... _Env> 76 concept __with_tag_invoke = // 77 tag_invocable<get_completion_signatures_t, _Sender, _Env...>; 78 79 template <class _Sender, class... _Env> 80 concept __with_legacy_tag_invoke = // 81 (sizeof...(_Env) == 0) && 82 tag_invocable<get_completion_signatures_t, _Sender, empty_env>; 83 84 template <class _Sender> 85 using __member_alias_t = // 86 typename __decay_t<_Sender>::completion_signatures; 87 88 template <class _Sender> 89 concept __with_member_alias = __mvalid<__member_alias_t, _Sender>; 90 91 struct get_completion_signatures_t 92 { 93 template <class _Sender, class... _Env> __implstdexec::__sigs::get_completion_signatures_t94 static auto __impl() 95 { 96 // Compute the type of the transformed sender: 97 using __tfx_fn = 98 __if_c<sizeof...(_Env) == 0, __mconst<_Sender>, __q<__tfx_sender>>; 99 using _TfxSender = __minvoke<__tfx_fn, _Sender, _Env...>; 100 101 if constexpr (__merror<_TfxSender>) 102 { 103 // Computing the type of the transformed sender returned an error 104 // type. Propagate it. 105 return static_cast<_TfxSender (*)()>(nullptr); 106 } 107 else if constexpr (__with_member_alias<_TfxSender>) 108 { 109 using _Result = __member_alias_t<_TfxSender>; 110 return static_cast<_Result (*)()>(nullptr); 111 } 112 else if constexpr (__with_static_member<_TfxSender, _Env...>) 113 { 114 using _Result = __static_member_result_t<_TfxSender, _Env...>; 115 return static_cast<_Result (*)()>(nullptr); 116 } 117 else if constexpr (__with_member<_TfxSender, _Env...>) 118 { 119 using _Result = __member_result_t<_TfxSender, _Env...>; 120 return static_cast<_Result (*)()>(nullptr); 121 } 122 else if constexpr (__with_tag_invoke<_TfxSender, _Env...>) 123 { 124 using _Result = tag_invoke_result_t<get_completion_signatures_t, 125 _TfxSender, _Env...>; 126 return static_cast<_Result (*)()>(nullptr); 127 } 128 else if constexpr (__with_legacy_tag_invoke<_TfxSender, _Env...>) 129 { 130 // This branch is strictly for backwards compatibility 131 using _Result = tag_invoke_result_t<get_completion_signatures_t, 132 _Sender, empty_env>; 133 return static_cast<_Result (*)()>(nullptr); 134 // [WAR] The explicit cast to bool below is to work around a bug in 135 // nvc++ (nvbug#4707793) 136 } 137 else if constexpr (bool(__awaitable<_TfxSender, 138 __env::__promise<_Env>...>)) 139 { 140 using _AwaitResult = 141 __await_result_t<_TfxSender, __env::__promise<_Env>...>; 142 using _Result = completion_signatures< 143 // set_value_t() or set_value_t(T) 144 __minvoke<__mremove<void, __qf<set_value_t>>, _AwaitResult>, 145 set_error_t(std::exception_ptr), set_stopped_t()>; 146 return static_cast<_Result (*)()>(nullptr); 147 } 148 else if constexpr (sizeof...(_Env) == 0) 149 { 150 // It's possible this is a dependent sender. 151 return static_cast<dependent_completions (*)()>(nullptr); 152 } 153 else if constexpr ((__is_debug_env<_Env> || ...)) 154 { 155 using __tag_invoke::tag_invoke; 156 // This ought to cause a hard error that indicates where the problem 157 // is. 158 using _Completions [[maybe_unused]] = 159 tag_invoke_result_t<get_completion_signatures_t, _Sender, 160 _Env...>; 161 return static_cast<__debug::__completion_signatures (*)()>(nullptr); 162 } 163 else 164 { 165 using _Result = __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, 166 _WITH_SENDER_<_Sender>, 167 _WITH_ENVIRONMENT_<_Env>...>; 168 return static_cast<_Result (*)()>(nullptr); 169 } 170 } 171 172 // NOT TO SPEC: if we're unable to compute the completion signatures, 173 // return an error type instead of SFINAE. 174 template <class _Sender, class... _Env> 175 requires(sizeof...(_Env) <= 1) operator ()stdexec::__sigs::get_completion_signatures_t176 constexpr auto operator()(_Sender&&, _Env&&...) const noexcept // 177 -> decltype(__impl<_Sender, _Env...>()()) 178 { 179 return {}; 180 } 181 }; 182 } // namespace __sigs 183 184 using __sigs::get_completion_signatures_t; 185 inline constexpr get_completion_signatures_t get_completion_signatures{}; 186 187 ///////////////////////////////////////////////////////////////////////////// 188 // [execution.senders.connect] 189 namespace __connect 190 { 191 template <class _Sender, class _Receiver> 192 using __tfx_sender = // 193 transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver>>, 194 _Sender, env_of_t<_Receiver>>; 195 196 template <class _Sender, class _Receiver> 197 using __member_result_t = 198 decltype(__declval<_Sender>().connect(__declval<_Receiver>())); 199 200 template <class _Sender, class _Receiver> 201 using __static_member_result_t = 202 decltype(STDEXEC_REMOVE_REFERENCE(_Sender) // 203 ::connect(__declval<_Sender>(), __declval<_Receiver>())); 204 205 template <class _Sender, class _Receiver> 206 concept __with_member = __mvalid<__member_result_t, _Sender, _Receiver>; 207 208 template <class _Sender, class _Receiver> 209 concept __with_static_member = 210 __mvalid<__static_member_result_t, _Sender, _Receiver>; 211 212 template <class _Sender, class _Receiver> 213 concept __with_tag_invoke = tag_invocable<connect_t, _Sender, _Receiver>; 214 215 template <class _Sender, class _Receiver> 216 concept __with_co_await = __callable<__connect_awaitable_t, _Sender, _Receiver>; 217 218 struct connect_t 219 { 220 template <class _Sender, class _Env> __check_signaturesstdexec::__connect::connect_t221 static constexpr auto __check_signatures() -> bool 222 { 223 if constexpr (sender_in<_Sender, _Env>) 224 { 225 // Instantiate __debug_sender via completion_signatures_of_t to 226 // check that the actual completions match the expected completions. 227 // 228 // Instantiate completion_signatures_of_t only if sender_in is true 229 // to workaround Clang not implementing CWG#2369 yet (connect() does 230 // not have a constraint for _Sender satisfying sender_in). 231 using __checked_signatures 232 [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>; 233 } 234 return true; 235 } 236 237 template <class _Sender, class _Receiver> __select_implstdexec::__connect::connect_t238 static constexpr auto __select_impl() noexcept 239 { 240 using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>; 241 using _TfxSender = __tfx_sender<_Sender, _Receiver>; 242 constexpr bool _NothrowTfxSender = 243 __nothrow_callable<transform_sender_t, _Domain, _Sender, 244 env_of_t<_Receiver>>; 245 246 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 247 static_assert(__check_signatures<_TfxSender, env_of_t<_Receiver>>()); 248 #endif 249 250 if constexpr (__with_static_member<_TfxSender, _Receiver>) 251 { 252 using _Result = __static_member_result_t<_TfxSender, _Receiver>; 253 constexpr bool _Nothrow = // 254 _NothrowTfxSender && 255 noexcept(__declval<_TfxSender>().connect( 256 __declval<_TfxSender>(), __declval<_Receiver>())); 257 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 258 } 259 else if constexpr (__with_member<_TfxSender, _Receiver>) 260 { 261 using _Result = __member_result_t<_TfxSender, _Receiver>; 262 constexpr bool _Nothrow = // 263 _NothrowTfxSender && noexcept(__declval<_TfxSender>().connect( 264 __declval<_Receiver>())); 265 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 266 } 267 else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) 268 { 269 using _Result = 270 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>; 271 constexpr bool _Nothrow = // 272 _NothrowTfxSender && 273 nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>; 274 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr); 275 } 276 else if constexpr (__with_co_await<_TfxSender, _Receiver>) 277 { 278 using _Result = 279 __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>; 280 return static_cast<_Result (*)()>(nullptr); 281 } 282 else 283 { 284 using _Result = __debug::__debug_operation; 285 return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>( 286 nullptr); 287 } 288 } 289 290 template <class _Sender, class _Receiver> 291 using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>()); 292 293 template <sender _Sender, receiver _Receiver> 294 requires __with_static_member<__tfx_sender<_Sender, _Receiver>, 295 _Receiver> || 296 __with_member<__tfx_sender<_Sender, _Receiver>, 297 _Receiver> || 298 __with_tag_invoke<__tfx_sender<_Sender, _Receiver>, 299 _Receiver> || 300 __with_co_await<__tfx_sender<_Sender, _Receiver>, 301 _Receiver> || 302 __is_debug_env<env_of_t<_Receiver>> 303 auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const 304 noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>) 305 -> __call_result_t<__select_impl_t<_Sender, _Receiver>> 306 { 307 using _TfxSender = __tfx_sender<_Sender, _Receiver>; 308 auto&& __env = get_env(__rcvr); 309 auto __domain = __get_late_domain(__sndr, __env); 310 311 if constexpr (__with_static_member<_TfxSender, _Receiver>) 312 { 313 static_assert( 314 operation_state< 315 __static_member_result_t<_TfxSender, _Receiver>>, 316 "Sender::connect(sender, receiver) must return a type that " 317 "satisfies the operation_state concept"); 318 auto&& __tfx_sndr = transform_sender( 319 __domain, static_cast<_Sender&&>(__sndr), __env); 320 return __tfx_sndr.connect(static_cast<_TfxSender&&>(__tfx_sndr), 321 static_cast<_Receiver&&>(__rcvr)); 322 } 323 else if constexpr (__with_member<_TfxSender, _Receiver>) 324 { 325 static_assert( 326 operation_state<__member_result_t<_TfxSender, _Receiver>>, 327 "sender.connect(receiver) must return a type that " 328 "satisfies the operation_state concept"); 329 return transform_sender(__domain, static_cast<_Sender&&>(__sndr), 330 __env) 331 .connect(static_cast<_Receiver&&>(__rcvr)); 332 } 333 else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) 334 { 335 static_assert( 336 operation_state< 337 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>, 338 "stdexec::connect(sender, receiver) must return a type that " 339 "satisfies the operation_state concept"); 340 return tag_invoke( 341 connect_t(), 342 transform_sender(__domain, static_cast<_Sender&&>(__sndr), 343 __env), 344 static_cast<_Receiver&&>(__rcvr)); 345 } 346 else if constexpr (__with_co_await<_TfxSender, _Receiver>) 347 { 348 return __connect_awaitable( // 349 transform_sender(__domain, static_cast<_Sender&&>(__sndr), 350 __env), 351 static_cast<_Receiver&&>(__rcvr)); 352 } 353 else 354 { 355 // This should generate an instantiation backtrace that contains 356 // useful debugging information. 357 using __tag_invoke::tag_invoke; 358 tag_invoke(*this, 359 transform_sender(__domain, 360 static_cast<_Sender&&>(__sndr), __env), 361 static_cast<_Receiver&&>(__rcvr)); 362 } 363 } 364 querystdexec::__connect::connect_t365 static constexpr auto query(forwarding_query_t) noexcept -> bool 366 { 367 return false; 368 } 369 }; 370 } // namespace __connect 371 372 using __connect::connect_t; 373 inline constexpr __connect::connect_t connect{}; 374 375 ///////////////////////////////////////////////////////////////////////////// 376 // [exec.snd] 377 template <class _Sender, class _Receiver> 378 concept sender_to = // 379 receiver<_Receiver> // 380 && sender_in<_Sender, env_of_t<_Receiver>> // 381 && __receiver_from<_Receiver, _Sender> // 382 && requires(_Sender&& __sndr, _Receiver&& __rcvr) { 383 connect(static_cast<_Sender&&>(__sndr), 384 static_cast<_Receiver&&>(__rcvr)); 385 }; 386 387 template <class _Tag, class... _Args> 388 auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag; 389 template <class _Sig> 390 using __tag_of_sig_t = 391 decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr))); 392 393 template <class _Sender, class _SetSig, class _Env = empty_env> 394 concept sender_of = // 395 sender_in<_Sender, _Env> // 396 && 397 same_as< 398 __types<_SetSig>, 399 __gather_completions_of< 400 __tag_of_sig_t<_SetSig>, _Sender, _Env, 401 __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>, 402 __mconcat<__qq<__types>>>>; 403 404 template <class _Error> 405 requires false 406 using __nofail_t = _Error; 407 408 template <class _Sender, class _Env = empty_env> 409 concept __nofail_sender = 410 sender_in<_Sender, _Env> && 411 requires { 412 typename __gather_completion_signatures< 413 __completion_signatures_of_t<_Sender, _Env>, set_error_t, 414 __nofail_t, __sigs::__default_completion, __types>; 415 }; 416 417 ///////////////////////////////////////////////////////////////////////////// 418 // early sender type-checking 419 template <class _Sender> 420 concept __well_formed_sender = __detail::__well_formed_sender<__minvoke< 421 __with_default_q<__completion_signatures_of_t, dependent_completions>, 422 _Sender>>; 423 } // namespace stdexec 424