Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions include/exec/env.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,19 @@ namespace experimental::execution
struct __without_t
{
template <class _Env, class _Query>
constexpr auto operator()(_Env&& __env, _Query) const noexcept
requires STDEXEC::__queryable_with<_Env, _Query>
constexpr auto operator()(_Env&& __env, _Query) const
noexcept(STDEXEC::__nothrow_constructible_from<__without<_Env, _Query>, _Env>)
-> __without<_Env, _Query>
{
if constexpr (STDEXEC::__queryable_with<_Env, _Query>)
{
return __without<_Env, _Query>{static_cast<_Env&&>(__env)};
}
else
{
return static_cast<_Env&&>(__env);
}
return __without<_Env, _Query>{static_cast<_Env&&>(__env)};
}

template <class _Env, class _Query>
constexpr auto operator()(_Env&& __env, _Query) const
noexcept(STDEXEC::__nothrow_move_constructible<_Env>) -> _Env
{
return static_cast<_Env&&>(__env);
}
};

Expand Down
96 changes: 96 additions & 0 deletions test/exec/test_env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include <stdexec/execution.hpp>
#include <test_common/catch2.hpp>

#include <type_traits>
#include <utility>

namespace
{
// Two dummy properties:
Expand Down Expand Up @@ -53,4 +56,97 @@ namespace
STATIC_REQUIRE(!std::invocable<Foo, decltype(e4)>);
CHECK(bar(e4) == 43);
}

TEST_CASE("without propagates environment move exceptions", "[env]")
{
struct missing_query : STDEXEC::__query<missing_query>
{
using STDEXEC::__query<missing_query>::operator();
};

struct without_move_error
{};

struct throwing_env
{
explicit throwing_env(bool* throw_on_move) noexcept
: throw_on_move_{throw_on_move}
{}

throwing_env(throwing_env const & other) noexcept
: throw_on_move_{other.throw_on_move_}
{}

throwing_env(throwing_env&& other)
: throw_on_move_{other.throw_on_move_}
{
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
if (*throw_on_move_)
{
throw without_move_error{};
}
#endif
}

int query(Foo) const noexcept
{
return 42;
}

bool* throw_on_move_;
};

bool throw_on_move = false;
throwing_env missing_env{&throw_on_move};
throwing_env queried_env{&throw_on_move};

STATIC_REQUIRE_FALSE(noexcept(exec::without(std::move(missing_env), missing_query{})));
STATIC_REQUIRE_FALSE(noexcept(exec::without(std::move(queried_env), foo)));
STATIC_REQUIRE(noexcept(exec::without(missing_env, missing_query{})));
STATIC_REQUIRE(noexcept(exec::without(queried_env, foo)));

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
throw_on_move = true;
CHECK_THROWS_AS(exec::without(std::move(missing_env), missing_query{}), without_move_error);
CHECK_THROWS_AS(exec::without(std::move(queried_env), foo), without_move_error);
#endif

struct throwing_copy_error
{};

struct throwing_copy_env
{
explicit throwing_copy_env(bool* throw_on_copy) noexcept
: throw_on_copy_{throw_on_copy}
{}

throwing_copy_env(throwing_copy_env const & other)
: throw_on_copy_{other.throw_on_copy_}
{
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
if (*throw_on_copy_)
{
throw throwing_copy_error{};
}
#endif
}

throwing_copy_env(throwing_copy_env&&) noexcept = default;

bool* throw_on_copy_;
};

bool throw_on_copy = false;
throwing_copy_env copy_env{&throw_on_copy};

STATIC_REQUIRE(
std::is_same_v<decltype(exec::without(copy_env, missing_query{})), throwing_copy_env&>);
STATIC_REQUIRE(noexcept(exec::without(copy_env, missing_query{})));

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
throw_on_copy = true;
auto&& result = exec::without(copy_env, missing_query{});
CHECK(&result == &copy_env);
#endif
}
} // namespace
Loading