From 5ffbf4141e6cce5a49f67fe6cf7695235d554007 Mon Sep 17 00:00:00 2001 From: alwaysprince05 Date: Sun, 13 Sep 2026 18:00:42 +0530 Subject: [PATCH] test: cover task custom env query forwarding through let_value (#2239) Issue #2239 reported that a task whose env_type is constructed from the enclosing environment failed to compile under starts_on + let_value when the consumer provides the queried value. The bug was incidentally fixed by the task reorganization in e88b1313, but without a regression test. Add two regression tests pinning the fixed behavior: the task's custom env must observe queries written by write_env, both when a let_value sits between starts_on and the task (the reported failing arrangement) and when the task is a direct child of starts_on. On pre-fix code the reported arrangement fails to compile, so these tests guard against regressions at compile time. Generated with Codebuff Co-Authored-By: Codebuff --- test/stdexec/types/test_task.cpp | 75 ++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/test/stdexec/types/test_task.cpp b/test/stdexec/types/test_task.cpp index 769ae66b4..d8974feec 100644 --- a/test/stdexec/types/test_task.cpp +++ b/test/stdexec/types/test_task.cpp @@ -725,6 +725,81 @@ namespace })); } + // Regression test for https://github.com/NVIDIA/stdexec/issues/2239: a task + // whose custom env type is built from the enclosing environment must observe + // queries written by write_env, even when a let_value sits between + // starts_on and the task. + struct query_2239_t + { + static constexpr bool query(ex::forwarding_query_t) noexcept + { + return true; + } + + template + auto operator()(Env const &env) const noexcept -> decltype(env.query(*this)) + { + return env.query(*this); + } + }; + + inline constexpr query_2239_t query_2239{}; + + struct own_env_2239 + { + template + requires std::invocable + explicit own_env_2239(ParentEnv const &parent) + : value(query_2239(parent)) + {} + + int value = -1; + }; + + struct task_env_2239 + { + template + using env_type = own_env_2239; + + explicit task_env_2239(own_env_2239 const &own) noexcept + : value(own.value) + {} + + [[nodiscard]] + auto query(query_2239_t) const noexcept -> int + { + return value; + } + + int value; + }; + + auto task_with_env_2239() -> ex::task + { + int value = co_await ex::read_env(query_2239); + CHECK(value == 7); + co_return; + } + + TEST_CASE("task env built from the enclosing environment observes write_env across let_value", + "[types][task]") + { + exec::single_thread_context ctx; + auto q = ex::prop{query_2239, 7}; + // The arrangement from issue #2239: write_env outside starts_on, with a + // let_value between starts_on and the task. + ex::sync_wait(ex::starts_on(ctx.get_scheduler(), ex::just() | ex::let_value(task_with_env_2239)) + | ex::write_env(q)); + } + + TEST_CASE("task env built from the enclosing environment observes write_env on a direct child", + "[types][task]") + { + exec::single_thread_context ctx; + auto q = ex::prop{query_2239, 7}; + ex::sync_wait(ex::starts_on(ctx.get_scheduler(), task_with_env_2239()) | ex::write_env(q)); + } + constinit int global_int = 0; constexpr auto wrap_ref = ex::then([](auto &i) noexcept { return std::ref(i); });