Skip to content

sync_wait / co_await of starts_on(task_scheduler{...}, task) fails a completion-domain static_assert #2238

Description

@kyusic

Starting a stdexec::task with starts_on on a stdexec::task_scheduler produces a sender
that I cannot sync_wait, and cannot co_await from another task. The same code with the
concrete scheduler compiles.

#include <stdexec/execution.hpp>

namespace ex = stdexec;

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

int main() {
  ex::run_loop loop;
  ex::sync_wait(
      ex::starts_on(ex::task_scheduler{loop.get_scheduler()}, child()));
}
g++ -std=c++26 -Iinclude -fsyntax-only repro.cpp

fails with:

include/stdexec/__detail/__domain.hpp:297:21: error: static assertion failed: the sender claims to complete on a domain that is not the domain of its completion scheduler
  297 |       static_assert(__same_as<_Domain, __detail::__scheduler_domain_t<_Sch, _Env const &...>>,

clang 21.1.8 reports the same static_assert at the same line.

The same failure appears when the sender is awaited from another task:

auto parent() -> ex::task<void> {
  auto sched = co_await ex::get_start_scheduler();
  co_await ex::starts_on(sched, child());
}

int main() {
  ex::run_loop loop;
  ex::sync_wait(ex::starts_on(loop.get_scheduler(), parent()));
}

Here sched deduces to stdexec::task_scheduler, even though parent() itself is
started on the concrete run_loop::scheduler.

Variants, all with the same child():

variant result
sync_wait(starts_on(task_scheduler{s}, child())) fails
co_await starts_on(task_scheduler{s}, child()) inside a task fails
sync_wait(starts_on(s, child())) with the concrete scheduler s compiles
sync_wait(starts_on(task_scheduler{s}, just())) compiles
sync_wait(starts_on(task_scheduler{s}, just() | let_value(child))) compiles
sync_wait(child() | write_env(prop{get_start_scheduler, task_scheduler{s}})) compiles
connect(starts_on(task_scheduler{s}, child()), some_receiver{}) compiles
spawn(starts_on(task_scheduler{s}, child()) | upon_error(...), scope.get_token()) compiles
completion_signatures_of_t<decltype(starts_on(task_scheduler{s}, child())), env<>> compiles

Is starts_on with a task_scheduler and a task child expected to be usable with
sync_wait / co_await, or is the type-erased scheduler meant to be used differently 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