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 "__concepts.hpp" 21 #include "__completion_signatures.hpp" 22 #include "__diagnostics.hpp" 23 #include "__env.hpp" 24 #include "__meta.hpp" 25 #include "__senders_core.hpp" 26 27 #include <exception> // IWYU pragma: keep for std::terminate 28 29 namespace stdexec { 30 ///////////////////////////////////////////////////////////////////////////// 31 // Some utilities for debugging senders 32 namespace __debug { 33 struct __is_debug_env_t : __query<__is_debug_env_t> { querystdexec::__debug::__is_debug_env_t34 static constexpr auto query(forwarding_query_t) noexcept -> bool { 35 return true; 36 } 37 }; 38 39 template <class _Env> 40 using __debug_env_t = env<prop<__is_debug_env_t, bool>, _Env>; 41 42 template <class _Env> 43 concept __is_debug_env = __callable<__is_debug_env_t, _Env>; 44 45 struct __completion_signatures { }; 46 47 #if STDEXEC_MSVC() 48 // MSVCBUG https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032 49 // MSVCBUG https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831 50 51 template <class _Sig> 52 struct __normalize_sig; 53 54 template <class _Tag, class... _Args> 55 struct __normalize_sig<_Tag(_Args...)> { 56 using __type = _Tag (*)(_Args&&...); 57 }; 58 59 template <class _Sig> 60 using __normalize_sig_t = __normalize_sig<_Sig>::__type; 61 #else 62 template <class _Sig> 63 extern int __normalize_sig; 64 65 template <class _Tag, class... _Args> 66 extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...); 67 68 template <class _Sig> 69 using __normalize_sig_t = decltype(__normalize_sig<_Sig>); 70 #endif 71 72 template <class... _Sigs> 73 struct __valid_completions { 74 template <class... _Args> 75 requires __one_of<set_value_t (*)(_Args&&...), _Sigs...> STDEXEC_ATTRIBUTEstdexec::__debug::__valid_completions76 STDEXEC_ATTRIBUTE(host, device) 77 void set_value(_Args&&...) noexcept { 78 STDEXEC_TERMINATE(); 79 } 80 81 template <class _Error> 82 requires __one_of<set_error_t (*)(_Error&&), _Sigs...> STDEXEC_ATTRIBUTEstdexec::__debug::__valid_completions83 STDEXEC_ATTRIBUTE(host, device) 84 void set_error(_Error&&) noexcept { 85 STDEXEC_TERMINATE(); 86 } 87 STDEXEC_ATTRIBUTEstdexec::__debug::__valid_completions88 STDEXEC_ATTRIBUTE(host, device) 89 void set_stopped() noexcept 90 requires __one_of<set_stopped_t (*)(), _Sigs...> 91 { 92 STDEXEC_TERMINATE(); 93 } 94 }; 95 96 template <class _CvrefSenderId, class _Env, class _Completions> 97 struct __debug_receiver { 98 using __t = __debug_receiver; 99 using __id = __debug_receiver; 100 using receiver_concept = receiver_t; 101 }; 102 103 template <class _CvrefSenderId, class _Env, class... _Sigs> 104 struct __debug_receiver<_CvrefSenderId, _Env, completion_signatures<_Sigs...>> 105 : __valid_completions<__normalize_sig_t<_Sigs>...> { 106 using __t = __debug_receiver; 107 using __id = __debug_receiver; 108 using receiver_concept = receiver_t; 109 STDEXEC_ATTRIBUTEstdexec::__debug::__debug_receiver110 STDEXEC_ATTRIBUTE(host, device) auto get_env() const noexcept -> __debug_env_t<_Env> { 111 STDEXEC_TERMINATE(); 112 } 113 }; 114 115 struct _COMPLETION_SIGNATURES_MISMATCH_ { }; 116 117 template <class _Sig> 118 struct _COMPLETION_SIGNATURE_ { }; 119 120 template <class... _Sigs> 121 struct _IS_NOT_ONE_OF_ { }; 122 123 template <class _Sender> 124 struct _SIGNAL_SENT_BY_SENDER_ { }; 125 126 template <class _Warning> 127 [[deprecated( 128 "The sender claims to send a particular set of completions," 129 " but in actual fact it completes with a result that is not" STDEXEC_ATTRIBUTE(host,device)130 " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE(host, device) void _ATTENTION_() noexcept { 131 } 132 133 template <class _Sig> 134 struct __invalid_completion { 135 struct __t { 136 template <class _CvrefSenderId, class _Env, class... _Sigs> 137 // BUGBUG this works around a recently (aug 2023) introduced regression in nvc++ 138 requires(!__one_of<_Sig, _Sigs...>) __tstdexec::__debug::__invalid_completion::__t139 __t(__debug_receiver<_CvrefSenderId, _Env, completion_signatures<_Sigs...>>&&) noexcept { 140 using _SenderId = __decay_t<_CvrefSenderId>; 141 using _Sender = stdexec::__t<_SenderId>; 142 using _What = _WARNING_< 143 _COMPLETION_SIGNATURES_MISMATCH_, 144 _COMPLETION_SIGNATURE_<_Sig>, 145 _IS_NOT_ONE_OF_<_Sigs...>, 146 _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>> 147 >; 148 __debug::_ATTENTION_<_What>(); 149 } 150 }; 151 }; 152 153 template <__completion_tag _Tag, class... _Args> STDEXEC_ATTRIBUTE(host,device)154 STDEXEC_ATTRIBUTE(host, device) 155 void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>, _Args&&...) noexcept { 156 } 157 158 struct __debug_operation { startstdexec::__debug::__debug_operation159 void start() & noexcept { 160 } 161 }; 162 163 //////////////////////////////////////////////////////////////////////////// 164 // `__debug_sender` 165 // =============== 166 167 // Understanding why a particular sender doesn't connect to a particular 168 // receiver is nigh impossible in the current design due to limitations in 169 // how the compiler reports overload resolution failure in the presence of 170 // constraints. `__debug_sender` is a utility to assist with the process. It 171 // gives you the deep template instantiation backtrace that you need to 172 // understand where in a chain of senders the problem is occurring. 173 174 // ```c++ 175 // template <class _Sigs, class _Env = env<>, class _Sender> 176 // void __debug_sender(_Sender&& __sndr, _Env = {}); 177 178 // template <class _Env = env<>, class _Sender> 179 // void __debug_sender(_Sender&& __sndr, _Env = {}); 180 // ``` 181 182 // **Usage:** 183 184 // To find out where in a chain of senders a sender is failing to connect 185 // to a receiver, pass it to `__debug_sender`, optionally with an 186 // environment argument; e.g. `__debug_sender(sndr [, env])` 187 188 // To find out why a sender will not connect to a receiver of a particular 189 // signature, specify the set of completion signatures as an explicit template 190 // argument that names an instantiation of `completion_signatures`; e.g.: 191 // `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`. 192 193 // **How it works:** 194 195 // The `__debug_sender` function `connect`'s the sender to a 196 // `__debug_receiver`, whose environment is augmented with a special 197 // `__is_debug_env_t` query. An additional fall-back overload is added to 198 // the `connect` CPO that recognizes receivers whose environments respond to 199 // that query and lets them through. Then in a non-immediate context, it 200 // looks for a `tag_invoke(connect_t...)` overload for the input sender and 201 // receiver. This will recurse until it hits the `tag_invoke` call that is 202 // causing the failure. 203 204 // At least with clang, this gives me a nice backtrace, at the bottom of 205 // which is the faulty `tag_invoke` overload with a mention of the 206 // constraint that failed. 207 template <class _Sigs, class _Env = env<>, class _Sender> __debug_sender(_Sender && __sndr,const _Env &={})208 void __debug_sender(_Sender&& __sndr, const _Env& = {}) { 209 if constexpr (!__is_debug_env<_Env>) { 210 if constexpr (sender_in<_Sender, _Env>) { 211 using _Receiver = __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>; 212 using _Operation = connect_result_t<_Sender, _Receiver>; 213 //static_assert(receiver_of<_Receiver, _Sigs>); 214 if constexpr (!same_as<_Operation, __debug_operation>) { 215 if (sizeof(_Sender) == ~0u) { // never true 216 auto __op = connect(static_cast<_Sender&&>(__sndr), _Receiver{}); 217 stdexec::start(__op); 218 } 219 } 220 } else { 221 stdexec::__diagnose_sender_concept_failure<_Sender, _Env>(); 222 } 223 } 224 } 225 226 template <class _Env = env<>, class _Sender> __debug_sender(_Sender && __sndr,const _Env &={})227 void __debug_sender(_Sender&& __sndr, const _Env& = {}) { 228 if constexpr (!__is_debug_env<_Env>) { 229 if constexpr (sender_in<_Sender, _Env>) { 230 using _Sigs = __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>; 231 using _Receiver = __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>; 232 if constexpr (!same_as<_Sigs, __debug::__completion_signatures>) { 233 using _Operation = connect_result_t<_Sender, _Receiver>; 234 //static_assert(receiver_of<_Receiver, _Sigs>); 235 if constexpr (!same_as<_Operation, __debug_operation>) { 236 if (sizeof(_Sender) == ~0ul) { // never true 237 auto __op = connect(static_cast<_Sender&&>(__sndr), _Receiver{}); 238 stdexec::start(__op); 239 } 240 } 241 } 242 } else { 243 __diagnose_sender_concept_failure<_Sender, _Env>(); 244 } 245 } 246 } 247 } // namespace __debug 248 249 using __debug::__is_debug_env; 250 using __debug::__debug_sender; 251 } // namespace stdexec 252