From 6613b750fa9d6c99df93df9f7c0360782c3c6e29 Mon Sep 17 00:00:00 2001 From: Robert Leahy Date: Sat, 27 Sep 2025 17:18:40 -0400 Subject: [PATCH] write_env: No Evaluated Context for Environment Type The get_env implementation for stdexec::write_env is supplied via a lambda. Prior to this commit that lambda relied on automatic return type deduction. When a function relies on automatic return type deduction the compiler must evaluate the body thereof to determine the type returned thereby. A consequence of the aforegoing formulation is that if any statement in the body of the lambda directly or transitively relied on the completeness of an incomplete type compilation would fail. The above might seem normal and non-problematic until one considers that it is fairly normal in C++ code to compute (and rely upon) types and other properties of functions long before any bona fide evaluation of that function occurs. This motivates the distinction between evaluated and unevaluated contexts. However as a consequence of the preceding paragraph the previous implementation of get_env meant that when code relied upon the return type thereof in an unevaluated context the context became an evaluated context in the body of get_env (and transitively through all statements therein). The newly-added unit test highlights this. With the previous implementation of get_env (which uses automatic return type deduction) compilation fails. With the new implementation of get_env (which provides an explicit return type) compilation succeeds. --- include/stdexec/__detail/__write_env.hpp | 8 +-- test/CMakeLists.txt | 1 + .../stdexec/algos/adaptors/test_write_env.cpp | 66 +++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 test/stdexec/algos/adaptors/test_write_env.cpp diff --git a/include/stdexec/__detail/__write_env.hpp b/include/stdexec/__detail/__write_env.hpp index 011656c20..8e7614cbc 100644 --- a/include/stdexec/__detail/__write_env.hpp +++ b/include/stdexec/__detail/__write_env.hpp @@ -61,10 +61,10 @@ namespace stdexec { stdexec::get_env(__child)); }; - static constexpr auto get_env = - [](__ignore, const auto& __state, const auto& __rcvr) noexcept { - return __env::__join(__state, stdexec::get_env(__rcvr)); - }; + static constexpr auto get_env = [](__ignore, const auto& __state, const auto& __rcvr) noexcept + -> decltype(__env::__join(__state, stdexec::get_env(__rcvr))) { + return __env::__join(__state, stdexec::get_env(__rcvr)); + }; static constexpr auto get_completion_signatures = [](_Self &&, _Env &&...) noexcept diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b3f8113cf..a761a079e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,6 +55,7 @@ set(stdexec_test_sources stdexec/algos/adaptors/test_stopped_as_optional.cpp stdexec/algos/adaptors/test_stopped_as_error.cpp stdexec/algos/adaptors/test_ensure_started.cpp + stdexec/algos/adaptors/test_write_env.cpp stdexec/algos/consumers/test_start_detached.cpp stdexec/algos/consumers/test_sync_wait.cpp stdexec/algos/other/test_execute.cpp diff --git a/test/stdexec/algos/adaptors/test_write_env.cpp b/test/stdexec/algos/adaptors/test_write_env.cpp new file mode 100644 index 000000000..c31c1c10b --- /dev/null +++ b/test/stdexec/algos/adaptors/test_write_env.cpp @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2025 Robert Leahy. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + * Licensed under the Apache License, Version 2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include + +namespace { + + template + struct receiver : expect_void_receiver<> { + constexpr ::stdexec::env<> get_env() const noexcept { + return state_->get_env(); + } + T* state_; + }; + + struct state; + + static_assert(!std::is_same_v< + void, + decltype(::stdexec::connect( + ::stdexec::just() + | ::stdexec::write_env(::stdexec::prop{ + ::stdexec::get_stop_token, + std::declval<::stdexec::inplace_stop_source&>().get_token()}), + receiver{{}, nullptr}))>); + + struct state { + constexpr ::stdexec::env<> get_env() const noexcept { + return {}; + } + }; + + TEST_CASE( + "write_env works when the actual environment is sourced from a type which was initially " + "incomplete but has since been completed", + "[adaptors][write_env]") { + ::stdexec::inplace_stop_source source; + state s; + auto op = ::stdexec::connect( + ::stdexec::just() + | ::stdexec::write_env(::stdexec::prop{::stdexec::get_stop_token, source.get_token()}), + receiver{{}, &s}); + ::stdexec::start(op); + } +} // namespace