From b338cb0f06a54e9d01a5b3da819d615e561e1448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neboj=C5=A1a=20Cvetkovi=C4=87?= Date: Tue, 9 Jun 2026 02:25:22 +0100 Subject: [PATCH] fix(read_env): only advertise inline_completion for set_value_t `read_env`'s `__attrs::query` was a member template, so it returned `inline_completion` for every completion tag - even `set_error_t` and `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. Drop the `_SetTag` template parameter and bind the query directly to `set_value_t` - the only tag read_env sends - matching what `__just::__attrs` already does. The other tags now fall through to `__unknown`, the inline check correctly fails, and the non-inline awaiter gets selected. --- include/stdexec/__detail/__read_env.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index b0c88e998..af79ae5a6 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -80,9 +80,8 @@ namespace STDEXEC template struct __attrs { - template STDEXEC_ATTRIBUTE(nodiscard) - constexpr auto query(__get_completion_behavior_t<_SetTag>) const noexcept + constexpr auto query(__get_completion_behavior_t) const noexcept { return __completion_behavior::__inline_completion; }