Uh oh!
There was an error while loading. Please reload this page.
fix(read_env): only advertise inline_completion for set_value_t - #2112
Conversation
Cra3z
commented
Jun 9, 2026
I think the more fundamental cause lies in a flaw of the current |
Right, I did think that was unusual! I presume this is still the correct completion behavior for a sender which only completes with |
ericniebler
commented
Jun 17, 2026
Uh oh!
There was an error while loading. Please reload this page.
a3791fd to
1e412f7CompareUh oh!
There was an error while loading. Please reload this page.
1e412f7 to
4aa7567Compare`read_env`'s `__attrs::query` was a member template, so it returned
`inline_completion` for every completion tag - even `set_stopped_t`,
which read_env never sends. That made `let_value` mistakenly classify
chains like
read_env(q) | let_value([](auto v) { return some_async_sender; })
as inline-completing. The check `__never_sends || behavior ==
inline_completion` passed for every tag because read_env claimed inline
across the board.
`__as_awaitable` then picked the inline `__sender_awaiter`, which holds
the operation state as a local in `await_suspend` and destroys it at the
closing brace. For any inner sender that hadn't actually completed by
then, this is a use-after-free.
Bind the query to the tags read_env actually completes with: always
`set_value_t`, and `set_error_t(exception_ptr)` only when the query is
potentially throwing - both complete inline within `start()`, matching
the completion signatures.4aa7567 to
9a30194Compareericniebler
commented
Jul 4, 2026
/ok to test 9a30194 |
Uh oh!
There was an error while loading. Please reload this page.
read_env's__attrs::querywas a member template, so it returnedinline_completionfor every completion tag - evenset_error_tandset_stopped_t, which read_env never sends. That madelet_valuemistakenly classify chains likeas inline-completing. The check
__never_sends || behavior == inline_completionpassed for every tag because read_env claimed inline across the board.__as_awaitablethen picked the inline__sender_awaiter, which holds the operation state as a local inawait_suspendand destroys it at the closing brace. For any inner sender that hadn't actually completed by then, this is a use-after-free.Example: Godbolt
Drop the
_SetTagtemplate parameter and bind the query directly toset_value_t- the only tag read_env sends - matching what__just::__attrsalready does. The other tags now fall through to__unknown, the inline check correctly fails, and the non-inline awaiter gets selected.