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