Skip to content

Task env_type requiring a query fails to compile under starts_on + let_value, even when the consumer provides the query #2239

Description

@kyusic

I have a stdexec::task whose environment type requires a query from the enclosing
environment, so that starting the task without that query is a compile error rather than
something that silently runs with a default value.

The consumer provides the query. This compiles and runs in every arrangement I tried
except one: when there is a let_value between starts_on and the task.

#include <stdexec/execution.hpp>

namespace ex = stdexec;

struct my_query_t {
  static constexpr bool query(ex::forwarding_query_t) noexcept { return true; }

  template <class Env>
  auto operator()(const Env& env) const noexcept -> decltype(env.query(*this)) {
    return env.query(*this);
  }
};
inline constexpr my_query_t my_query{};

struct my_own_env {
  template <class ParentEnv>
    requires requires(const ParentEnv& parent) { my_query(parent); }
  explicit my_own_env(const ParentEnv& parent) : value(my_query(parent)) {}

  int value = -1;
};

struct my_task_env {
  template <class ParentEnv>
  using env_type = my_own_env;

  explicit my_task_env(const my_own_env& own) noexcept : value(own.value) {}

  auto query(my_query_t) const noexcept -> int { return value; }

  int value;
};

auto child() -> ex::task<void, my_task_env> { co_return; }

int main() {
  ex::run_loop loop;
  auto sched = loop.get_scheduler();
  auto q = ex::prop{my_query, 7};

  ex::sync_wait(ex::starts_on(sched, ex::just() | ex::let_value(child)) |
                ex::write_env(q));
}
g++ -std=c++26 -Iinclude -fsyntax-only repro.cpp

fails with:

include/stdexec/__detail/__task.hpp:440:34: error: no matching function for call to ‘my_own_env::my_own_env(<brace-enclosed initializer list>)’
  440 |         return __own_env_t<_Env>{};
      |                                  ^

clang 21.1.8 reports the same thing (no matching constructor for initialization of '__own_env_t<...>' at the same line).

Replacing only the last statement, keeping everything else, these all compile:

variant result
starts_on(sched, just() | let_value(child)) | write_env(q) fails
on(sched, just() | let_value(child)) | write_env(q) fails
starts_on(sched, child()) | write_env(q) compiles
just() | let_value(child) | write_env(q) compiles
continues_on(just() | let_value(child), sched) | write_env(q) compiles
starts_on(sched, just() | let_value(child) | write_env(q)) compiles

So the task sees the query when it is the direct child of starts_on, and when write_env
is applied inside starts_on, but not when a let_value sits between starts_on and the
task. Is the failing arrangement expected to work, or am I misusing env_type here?

Tested at f91f6363 with g++ 15.2.0 and clang 21.1.8, both -std=c++26.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions