I spawn a child into a counting_scope. The child reads the stop token out of the
environment and passes it to a coroutine that takes stdexec::inplace_stop_token. The
scope supplies a real inplace_stop_token, and the program runs as expected when it
compiles.
Wrapping that child in starts_on makes clang reject the program. gcc accepts the same
code.
#include <utility>
#include <stdexec/execution.hpp>
namespace ex = stdexec;
// In real code this is a member function defined in a .cpp, so its parameter
// type is fixed.
auto work(ex::inplace_stop_token token) -> ex::task<void> {
(void)token;
co_return;
}
int main() {
ex::run_loop loop;
ex::counting_scope scope;
auto child = ex::let_value(ex::read_env(ex::get_stop_token),
[](auto token) -> ex::task<void> {
co_await work(token);
});
ex::spawn(ex::starts_on(loop.get_scheduler(), std::move(child)) |
ex::upon_error([](auto&&) noexcept {}),
scope.get_token());
ex::sync_wait(scope.join());
}
clang++ -std=c++26 -Iinclude -fsyntax-only repro.cpp
fails with:
repro.cpp:20:41: error: no matching function for call to 'work'
20 | co_await work(token);
| ^~~~
include/stdexec/__detail/../__detail/../functional.hpp:168:16: note: in instantiation of function template specialization 'main()::(anonymous
class)::operator()<stdexec::never_stop_token>' requested here
168 | return static_cast<_Fun &&>(__fun)(static_cast<_Args &&>(__args)...);
| ^
...
repro.cpp:9:6: note: candidate function not viable: no known conversion from 'stdexec::never_stop_token' to 'ex::inplace_stop_token' for 1st argument
9 | auto work(ex::inplace_stop_token token) -> ex::task<void> {
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
The same command with g++ -std=c++26 compiles.
Variants, each changing one thing in the program above:
| variant |
g++ 15.2.0 |
clang 21.1.8 |
| as written |
compiles |
fails |
work takes the token as auto instead of inplace_stop_token |
compiles |
compiles |
| the child does not read the environment |
compiles |
compiles |
the scheduler is given through write_env(prop{get_start_scheduler, sched}) instead of starts_on |
compiles |
compiles |
sync_wait with the token in the environment, instead of spawning into a scope |
compiles |
compiles |
connect to a receiver whose environment carries the token, instead of spawning into a scope |
compiles |
compiles |
Is the child expected to be instantiated with an environment that has no stop token here,
or am I holding starts_on wrong?
Versions:
- stdexec
main @ f91f6363 (2026-08-22)
- g++ 15.2.0, clang 21.1.8, both
-std=c++26
- clang 22.1.2 rejects the program the same way
I spawn a child into a
counting_scope. The child reads the stop token out of theenvironment and passes it to a coroutine that takes
stdexec::inplace_stop_token. Thescope supplies a real
inplace_stop_token, and the program runs as expected when itcompiles.
Wrapping that child in
starts_onmakes clang reject the program. gcc accepts the samecode.
fails with:
The same command with
g++ -std=c++26compiles.Variants, each changing one thing in the program above:
worktakes the token asautoinstead ofinplace_stop_tokenwrite_env(prop{get_start_scheduler, sched})instead ofstarts_onsync_waitwith the token in the environment, instead of spawning into a scopeconnectto a receiver whose environment carries the token, instead of spawning into a scopeIs the child expected to be instantiated with an environment that has no stop token here,
or am I holding
starts_onwrong?Versions:
main@f91f6363(2026-08-22)-std=c++26