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
50 changes: 38 additions & 12 deletions include/iris/rvariant/rvariant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,15 +472,27 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN
(sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_assignable_v<T>) ||
is_recursive_wrapper_v<T>
) {
T tmp{std::forward<Args>(args)...}; // may throw
static_assert(noexcept(t_old_i = std::move(tmp)));
t_old_i = std::move(tmp);
static_assert(noexcept(t_old_i = std::declval<T&&>()));
if constexpr (sizeof...(Args) == 0) {
T tmp = T(); // may throw
t_old_i = std::move(tmp);
} else {
T tmp(std::forward<Args>(args)...); // may throw
t_old_i = std::move(tmp);
}

} else if constexpr (
sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_copy_assignable_v<T>
) { // strange type...
T const tmp{std::forward<Args>(args)...}; // may throw
static_assert(noexcept(t_old_i = tmp));
t_old_i = tmp;
static_assert(noexcept(t_old_i = std::declval<T const&>()));
if constexpr (sizeof...(Args) == 0) {
T const tmp = T(); // may throw
t_old_i = tmp;
} else {
T const tmp(std::forward<Args>(args)...); // may throw
t_old_i = tmp;
}

} else {
static_assert(!never_valueless);
t_old_i.~T_old_i();
Expand All @@ -495,19 +507,33 @@ IRIS_RVARIANT_ALWAYS_THROWING_UNREACHABLE_BEGIN
(sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_move_constructible_v<T>) ||
is_recursive_wrapper_v<T>
) {
T tmp{std::forward<Args>(args)...}; // may throw
t_old_i.~T_old_i();
static_assert(std::is_nothrow_constructible_v<storage_type, std::in_place_index_t<I>, T&&>);
std::construct_at(&this->storage_, std::in_place_index<I>, std::move(tmp)); // never throws
if constexpr (sizeof...(Args) == 0) {
T tmp = T(); // may throw
t_old_i.~T_old_i();
std::construct_at(&this->storage_, std::in_place_index<I>, std::move(tmp)); // never throws
} else {
T tmp(std::forward<Args>(args)...); // may throw
t_old_i.~T_old_i();
std::construct_at(&this->storage_, std::in_place_index<I>, std::move(tmp)); // never throws
}
this->index_ = I;

} else if constexpr (
sizeof(T) <= detail::never_valueless_trivial_size_limit && std::is_trivially_copy_constructible_v<T>
) { // strange type...
T const tmp{std::forward<Args>(args)...}; // may throw
t_old_i.~T_old_i();
static_assert(std::is_nothrow_constructible_v<storage_type, std::in_place_index_t<I>, T const&>);
std::construct_at(&this->storage_, std::in_place_index<I>, tmp); // never throws
if constexpr (sizeof...(Args) == 0) {
T const tmp = T(); // may throw
t_old_i.~T_old_i();
std::construct_at(&this->storage_, std::in_place_index<I>, tmp); // never throws
} else {
T const tmp(std::forward<Args>(args)...); // may throw
t_old_i.~T_old_i();
std::construct_at(&this->storage_, std::in_place_index<I>, tmp); // never throws
}
this->index_ = I;

} else {
static_assert(!never_valueless);
t_old_i.~T_old_i();
Expand Down
84 changes: 80 additions & 4 deletions test/rvariant/rvariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <bit>
#include <concepts>
#include <exception>
#include <initializer_list>
#include <memory>
#include <type_traits>
#include <utility>
Expand Down Expand Up @@ -1195,11 +1196,11 @@ TEST_CASE("emplace")
using V = iris::rvariant<int, S>;
{
V v(std::in_place_type<int>);
v.emplace<S>(); // type-changing & no args; test T{}
v.emplace<S>(); // type-changing & no args; test T()
}
{
V v(std::in_place_type<S>);
v.emplace<S>(); // non-type-changing & no args; test T{}
v.emplace<S>(); // non-type-changing & no args; test T()
}
}
{
Expand All @@ -1214,15 +1215,90 @@ TEST_CASE("emplace")
using V = iris::rvariant<int, StrangeS>;
{
V v(std::in_place_type<int>);
v.emplace<StrangeS>(); // type-changing & no args; test T{}
v.emplace<StrangeS>(); // type-changing & no args; test T()
}
{
V v(std::in_place_type<StrangeS>);
v.emplace<StrangeS>(); // non-type-changing & no args; test T{}
v.emplace<StrangeS>(); // non-type-changing & no args; test T()
}
}
// NOLINTEND(modernize-use-equals-default)
}
{
// NOLINTBEGIN(modernize-use-equals-default)
// The contained value is direct-non-list-initialized ([variant.mod]): an initializer_list
// constructor is not preferred, and a narrowing into the parameter of a constructor is not
// ill-formed. `S(int)` is not `noexcept` so that the never-valueless paths are taken.
{
struct S
{
S(int value) noexcept(false) : value(value) {} // potentially-throwing
S(std::initializer_list<int>) noexcept(false) : from_list(true) {} // potentially-throwing
int value = 0;
bool from_list = false;
};
using V = iris::rvariant<int, S>;
long long wide = 42;

V v(std::in_place_type<int>);
v.emplace<S>(1); // type-changing; test T(args...)
CHECK(!iris::get<S>(v).from_list);
CHECK(iris::get<S>(v).value == 1);
v.emplace<S>(2); // non-type-changing; test T(args...)
CHECK(!iris::get<S>(v).from_list);
CHECK(iris::get<S>(v).value == 2);

V w(std::in_place_type<int>);
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4244)
#endif
w.emplace<S>(wide); // type-changing; narrowing into `S(int)`
#ifdef _MSC_VER
# pragma warning(pop)
#endif
CHECK(iris::get<S>(w).value == 42);
w.emplace<S>(wide); // non-type-changing; narrowing into `S(int)`
CHECK(iris::get<S>(w).value == 42);
}
{
struct StrangeS
{
StrangeS(int value) noexcept(false) : value(value) {} // potentially-throwing
StrangeS(std::initializer_list<int>) noexcept(false) : from_list(true) {} // potentially-throwing
StrangeS(StrangeS&&) noexcept {} // not trivial
StrangeS(StrangeS const&) = default; // trivial
StrangeS& operator=(StrangeS&&) noexcept { return *this; } // not trivial
StrangeS& operator=(StrangeS const&) = default; // trivial
int value = 0;
bool from_list = false;
};
using V = iris::rvariant<int, StrangeS>;
long long wide = 42;

V v(std::in_place_type<int>);
v.emplace<StrangeS>(1); // type-changing; test T(args...)
CHECK(!iris::get<StrangeS>(v).from_list);
CHECK(iris::get<StrangeS>(v).value == 1);
v.emplace<StrangeS>(2); // non-type-changing; test T(args...)
CHECK(!iris::get<StrangeS>(v).from_list);
CHECK(iris::get<StrangeS>(v).value == 2);

V w(std::in_place_type<int>);
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4244)
#endif
w.emplace<StrangeS>(wide); // type-changing; narrowing into `StrangeS(int)`
#ifdef _MSC_VER
# pragma warning(pop)
#endif
CHECK(iris::get<StrangeS>(w).value == 42);
w.emplace<StrangeS>(wide); // non-type-changing; narrowing into `StrangeS(int)`
CHECK(iris::get<StrangeS>(w).value == 42);
}
// NOLINTEND(modernize-use-equals-default)
}

// ReSharper disable CppStaticAssertFailure

Expand Down
Loading