Skip to content

Commit 286e71b

Browse files
committed
flatten the structure of env<> for the sake of msvc
fixes #1979
1 parent 47bb920 commit 286e71b

2 files changed

Lines changed: 87 additions & 50 deletions

File tree

include/stdexec/__detail/__env.hpp

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "__meta.hpp"
2222
#include "__query.hpp"
2323
#include "__tag_invoke.hpp"
24+
#include "__tuple.hpp"
2425

2526
#include <exception> // IWYU pragma: keep for std::terminate
2627
#include <functional> // IWYU pragma: keep for unwrap_reference_t
@@ -29,6 +30,7 @@
2930
STDEXEC_PRAGMA_PUSH()
3031
STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
3132
STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
33+
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
3234

3335
namespace STDEXEC
3436
{
@@ -113,7 +115,7 @@ namespace STDEXEC
113115
}
114116
else
115117
{
116-
return env<_Env1, __fwd_env_t<_Env2>>{{static_cast<_Env1 &&>(__env1)},
118+
return env<_Env1, __fwd_env_t<_Env2>>{static_cast<_Env1 &&>(__env1),
117119
__fwd_fn()(static_cast<_Env2 &&>(__env2))};
118120
}
119121
}
@@ -201,70 +203,48 @@ namespace STDEXEC
201203
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
202204
prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
203205

204-
//////////////////////////////////////////////////////////////////////
205-
// env
206-
template <class... _Envs>
207-
struct env;
208-
209-
template <>
210-
struct env<>
211-
{
212-
STDEXEC_ATTRIBUTE(nodiscard, host, device)
213-
auto query() const = delete;
214-
};
215-
216-
template <class _Env>
217-
struct env<_Env> : _Env
218-
{};
219-
220-
template <class _Env>
221-
struct env<_Env &>
206+
namespace __detail
222207
{
223208
template <class _Query, class... _Args>
224-
requires __queryable_with<_Env, _Query, _Args...>
225-
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
226-
constexpr auto query(_Query, _Args &&...__args) const
227-
noexcept(__nothrow_queryable_with<_Env, _Query, _Args...>)
228-
-> __query_result_t<_Env, _Query, _Args...>
209+
struct __get_1st_env
229210
{
230-
return __query<_Query>()(__env_, static_cast<_Args &&>(__args)...);
231-
}
211+
template <class _Env>
212+
using __has_query_t = __mbool<__queryable_with<_Env, _Query, _Args...>>;
232213

233-
_Env &__env_;
234-
};
214+
template <class... _Envs>
215+
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
216+
constexpr auto operator()(env<_Envs...> const &__env) const noexcept -> decltype(auto)
217+
{
218+
// count of elements that includes the first env that supports the query and all
219+
// subsequent envs
220+
STDEXEC_CONSTEXPR_LOCAL auto __index =
221+
sizeof...(_Envs) - __mcall<__mfind_if<__q1<__has_query_t>, __msize>, _Envs...>::value;
222+
if constexpr (__index < sizeof...(_Envs))
223+
return STDEXEC::__get<__index>(__env);
224+
}
225+
};
226+
} // namespace __detail
235227

236-
template <class _Env1, class _Env2>
237-
struct env<_Env1, _Env2>
228+
//////////////////////////////////////////////////////////////////////
229+
// env
230+
template <class... _Envs>
231+
struct env : __tuple<_Envs...>
238232
{
239233
template <class _Query, class... _Args>
240-
requires __queryable_with<_Env1, _Query, _Args...>
241-
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
242-
constexpr auto query(_Query, _Args &&...__args) const
243-
noexcept(__nothrow_queryable_with<_Env1, _Query, _Args...>)
244-
-> __query_result_t<_Env1, _Query, _Args...>
245-
{
246-
return __query<_Query>()(__env1_, static_cast<_Args &&>(__args)...);
247-
}
234+
using __1st_env_t = __call_result_t<__detail::__get_1st_env<_Query, _Args...>, env const &>;
248235

249236
template <class _Query, class... _Args>
250-
requires __queryable_with<_Env1, _Query, _Args...>
251-
|| __queryable_with<_Env2, _Query, _Args...>
237+
requires __not_same_as<__1st_env_t<_Query, _Args...>, void>
252238
STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
253239
constexpr auto query(_Query, _Args &&...__args) const
254-
noexcept(__nothrow_queryable_with<_Env2, _Query, _Args...>)
255-
-> __query_result_t<_Env2, _Query, _Args...>
240+
noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>)
241+
-> __query_result_t<__1st_env_t<_Query, _Args...>, _Query, _Args...>
256242
{
257-
return __query<_Query>()(__env2_, static_cast<_Args &&>(__args)...);
243+
auto const &__env = __detail::__get_1st_env<_Query, _Args...>()(*this);
244+
return __query<_Query>()(__env, static_cast<_Args &&>(__args)...);
258245
}
259-
260-
STDEXEC_ATTRIBUTE(no_unique_address) _Env1 __env1_;
261-
STDEXEC_ATTRIBUTE(no_unique_address) _Env2 __env2_;
262246
};
263247

264-
template <class _Env1, class _Env2, class... _Envs>
265-
struct env<_Env1, _Env2, _Envs...> : env<env<_Env1, _Env2>, _Envs...>
266-
{};
267-
268248
template <class... _Envs>
269249
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;
270250

test/stdexec/queries/test_env.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
namespace ex = STDEXEC;
2323

24+
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
25+
2426
namespace
2527
{
2628
template <typename T>
@@ -106,4 +108,59 @@ namespace
106108
auto sch = ex::get_completion_scheduler<ex::set_value_t>(attrs, ex::env{});
107109
CHECK(std::same_as<decltype(sch), ex::inline_scheduler>);
108110
}
111+
112+
// Before v19, clang could not compile this test because of the large number of nested
113+
// envs.
114+
#if !STDEXEC_CLANG() || STDEXEC_CLANG_VERSION >= 19'00
115+
116+
# define DEFINE_QUERY(name) constexpr struct name ## _t : ex::__query<name ## _t> {} name{}
117+
118+
DEFINE_QUERY(query_0);
119+
DEFINE_QUERY(query_1);
120+
DEFINE_QUERY(query_2);
121+
DEFINE_QUERY(query_3);
122+
DEFINE_QUERY(query_4);
123+
DEFINE_QUERY(query_5);
124+
DEFINE_QUERY(query_6);
125+
DEFINE_QUERY(query_7);
126+
DEFINE_QUERY(query_8);
127+
DEFINE_QUERY(query_9);
128+
DEFINE_QUERY(query_10);
129+
DEFINE_QUERY(query_11);
130+
DEFINE_QUERY(query_12);
131+
132+
TEST_CASE("env supports lots of child envs without exceeding compiler limits", "[queries][env]")
133+
{
134+
auto env = ex::env{
135+
ex::prop{ query_0, 0},
136+
ex::prop{ query_1, 1},
137+
ex::prop{ query_2, 2},
138+
ex::prop{ query_3, 3},
139+
ex::prop{ query_4, 4},
140+
ex::prop{ query_5, 5},
141+
ex::prop{ query_6, 6},
142+
ex::prop{ query_7, 7},
143+
ex::prop{ query_8, 8},
144+
ex::prop{ query_9, 9},
145+
ex::prop{query_10, 10},
146+
ex::prop{query_11, 11},
147+
ex::prop{query_12, 12}
148+
};
149+
150+
CHECK(env.query(query_0) == 0);
151+
CHECK(env.query(query_1) == 1);
152+
CHECK(env.query(query_2) == 2);
153+
CHECK(env.query(query_3) == 3);
154+
CHECK(env.query(query_4) == 4);
155+
CHECK(env.query(query_5) == 5);
156+
CHECK(env.query(query_6) == 6);
157+
CHECK(env.query(query_7) == 7);
158+
CHECK(env.query(query_8) == 8);
159+
CHECK(env.query(query_9) == 9);
160+
CHECK(env.query(query_10) == 10);
161+
CHECK(env.query(query_11) == 11);
162+
CHECK(env.query(query_12) == 12);
163+
}
164+
165+
#endif
109166
} // namespace

0 commit comments

Comments
 (0)