Merge branch 'development' into cleanup9

This commit is contained in:
Roland Reichwein 2026-07-19 22:57:06 +02:00 committed by GitHub
commit 7e04c663e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 682 additions and 92 deletions

View File

@ -86,6 +86,8 @@ namespace etl
static void destroy(char*, size_t) {}
static void copy(char*, const char*, size_t) {}
static void move(char*, const char*, size_t) {}
static void copy_assign(char*, const char*, size_t) {}
static void move_assign(char*, const char*, size_t) {}
};
// Recursive case.
@ -128,6 +130,30 @@ namespace etl
}
}
static void copy_assign(char* dst, const char* src, size_t type_id)
{
if (type_id == Index)
{
copy_assign_impl(dst, src, etl::integral_constant<bool, etl::is_copy_assignable<THead>::value>{});
}
else
{
variant_operations<Index + 1, TRest...>::copy_assign(dst, src, type_id);
}
}
static void move_assign(char* dst, const char* src, size_t type_id)
{
if (type_id == Index)
{
move_assign_impl(dst, src, etl::integral_constant<bool, etl::is_move_assignable<THead>::value>{});
}
else
{
variant_operations<Index + 1, TRest...>::move_assign(dst, src, type_id);
}
}
private:
static void copy_impl(char* dst, const char* src, etl::true_type)
@ -143,6 +169,20 @@ namespace etl
}
static void move_impl(char*, const char*, etl::false_type) {}
static void copy_assign_impl(char* dst, const char* src, etl::true_type)
{
*reinterpret_cast<THead*>(dst) = *reinterpret_cast<const THead*>(src);
}
static void copy_assign_impl(char*, const char*, etl::false_type) {}
static void move_assign_impl(char* dst, const char* src, etl::true_type)
{
*reinterpret_cast<THead*>(dst) = etl::move(*reinterpret_cast<THead*>(const_cast<char*>(src)));
}
static void move_assign_impl(char*, const char*, etl::false_type) {}
};
//*******************************************
@ -431,6 +471,80 @@ namespace etl
{
}
variant_base(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
: type_id(other.type_id)
{
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
}
}
variant_base(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
: type_id(other.type_id)
{
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
}
}
variant_base& operator=(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value
&& etl::conjunction<etl::is_nothrow_copy_assignable<TTypes>...>::value)
{
if (this != &other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::copy_assign(data, other.data, type_id);
}
else
{
if (type_id != variant_npos)
{
variant_operations<0, TTypes...>::destroy(data, type_id);
type_id = variant_npos;
}
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
type_id = other.type_id;
}
}
}
return *this;
}
variant_base& operator=(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value
&& etl::conjunction<etl::is_nothrow_move_assignable<TTypes>...>::value)
{
if (this != &other)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::move_assign(data, other.data, type_id);
}
else
{
if (type_id != variant_npos)
{
variant_operations<0, TTypes...>::destroy(data, type_id);
type_id = variant_npos;
}
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
type_id = other.type_id;
}
}
}
return *this;
}
~variant_base()
{
if (type_id != variant_npos)
@ -474,7 +588,259 @@ namespace etl
{
}
variant_base(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
: data()
, type_id(other.type_id)
{
copy_construct_from(other, is_all_trivially_copyable{});
}
variant_base(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
: data()
, type_id(other.type_id)
{
move_construct_from(other, is_all_trivially_copyable{});
}
variant_base& operator=(const variant_base& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value
&& etl::conjunction<etl::is_nothrow_copy_assignable<TTypes>...>::value)
{
if (this != &other)
{
copy_assign_from(other, is_all_trivially_copyable{});
}
return *this;
}
variant_base& operator=(variant_base&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value
&& etl::conjunction<etl::is_nothrow_move_assignable<TTypes>...>::value)
{
if (this != &other)
{
move_assign_from(other, is_all_trivially_copyable{});
}
return *this;
}
~variant_base() = default;
private:
// All alternatives trivially copyable => a raw-byte copy correctly
// reproduces both copy and move construction.
typedef etl::integral_constant<bool, etl::conjunction<etl::is_trivially_copyable<TTypes>...>::value> is_all_trivially_copyable;
//*******************************************
// Trivially copyable fast path: raw-byte copy.
//*******************************************
void copy_construct_from(const variant_base& other, etl::true_type) noexcept
{
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
void move_construct_from(const variant_base& other, etl::true_type) noexcept
{
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
void copy_assign_from(const variant_base& other, etl::true_type) noexcept
{
type_id = other.type_id;
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
void move_assign_from(const variant_base& other, etl::true_type) noexcept
{
type_id = other.type_id;
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
//*******************************************
// Non-trivial path: dispatch to the active alternative's own
// copy/move constructor. The active union member shares its address
// with the union, so the raw-pointer dispatch places the new object
// in the correct storage.
//*******************************************
void copy_construct_from(const variant_base& other, etl::false_type)
{
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
}
}
void move_construct_from(variant_base& other, etl::false_type)
{
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
}
}
void copy_assign_from(const variant_base& other, etl::false_type)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::copy_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
return;
}
if (type_id != variant_npos)
{
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
type_id = variant_npos;
}
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::copy(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
type_id = other.type_id;
}
}
void move_assign_from(variant_base& other, etl::false_type)
{
if ((type_id != variant_npos) && (type_id == other.type_id))
{
variant_operations<0, TTypes...>::move_assign(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), type_id);
return;
}
if (type_id != variant_npos)
{
variant_operations<0, TTypes...>::destroy(reinterpret_cast<char*>(&data), type_id);
type_id = variant_npos;
}
if (other.type_id != variant_npos)
{
variant_operations<0, TTypes...>::move(reinterpret_cast<char*>(&data), reinterpret_cast<const char*>(&other.data), other.type_id);
type_id = other.type_id;
}
}
};
//*******************************************
/// Trait: are all types copy constructible?
//*******************************************
template <typename... TTypes>
struct are_all_copy_constructible : etl::conjunction<etl::is_copy_constructible<TTypes>...>
{
};
//*******************************************
/// Trait: are all types move constructible?
//*******************************************
template <typename... TTypes>
struct are_all_move_constructible : etl::conjunction<etl::is_move_constructible<TTypes>...>
{
};
//*******************************************
/// Trait: are all types copy assignable?
//*******************************************
template <typename... TTypes>
struct are_all_copy_assignable : etl::conjunction<etl::is_copy_assignable<TTypes>...>
{
};
//*******************************************
/// Trait: are all types move assignable?
//*******************************************
template <typename... TTypes>
struct are_all_move_assignable : etl::conjunction<etl::is_move_assignable<TTypes>...>
{
};
//*******************************************
/// Flat base that conditionally deletes the copy and move CONSTRUCTORS.
/// Every special member is declared directly (no intermediate bases) so
/// that MSVC does not spuriously delete the defaulted move members when a
/// sibling/base subobject has a deleted special member.
//*******************************************
template <bool IsCopyConstructible, bool IsMoveConstructible>
struct variant_constructor_control
{
variant_constructor_control() = default;
variant_constructor_control(const variant_constructor_control&) = default;
variant_constructor_control(variant_constructor_control&&) = default;
variant_constructor_control& operator=(const variant_constructor_control&) = default;
variant_constructor_control& operator=(variant_constructor_control&&) = default;
};
template <>
struct variant_constructor_control<false, true>
{
variant_constructor_control() = default;
variant_constructor_control(const variant_constructor_control&) = delete;
variant_constructor_control(variant_constructor_control&&) = default;
variant_constructor_control& operator=(const variant_constructor_control&) = default;
variant_constructor_control& operator=(variant_constructor_control&&) = default;
};
template <>
struct variant_constructor_control<true, false>
{
variant_constructor_control() = default;
variant_constructor_control(const variant_constructor_control&) = default;
variant_constructor_control(variant_constructor_control&&) = delete;
variant_constructor_control& operator=(const variant_constructor_control&) = default;
variant_constructor_control& operator=(variant_constructor_control&&) = default;
};
template <>
struct variant_constructor_control<false, false>
{
variant_constructor_control() = default;
variant_constructor_control(const variant_constructor_control&) = delete;
variant_constructor_control(variant_constructor_control&&) = delete;
variant_constructor_control& operator=(const variant_constructor_control&) = default;
variant_constructor_control& operator=(variant_constructor_control&&) = default;
};
//*******************************************
/// Flat base that conditionally deletes the copy and move ASSIGNMENT
/// operators. Every special member is declared directly (see the note on
/// variant_constructor_control above).
//*******************************************
template <bool IsCopyAssignable, bool IsMoveAssignable>
struct variant_assignment_control
{
variant_assignment_control() = default;
variant_assignment_control(const variant_assignment_control&) = default;
variant_assignment_control(variant_assignment_control&&) = default;
variant_assignment_control& operator=(const variant_assignment_control&) = default;
variant_assignment_control& operator=(variant_assignment_control&&) = default;
};
template <>
struct variant_assignment_control<false, true>
{
variant_assignment_control() = default;
variant_assignment_control(const variant_assignment_control&) = default;
variant_assignment_control(variant_assignment_control&&) = default;
variant_assignment_control& operator=(const variant_assignment_control&) = delete;
variant_assignment_control& operator=(variant_assignment_control&&) = default;
};
template <>
struct variant_assignment_control<true, false>
{
variant_assignment_control() = default;
variant_assignment_control(const variant_assignment_control&) = default;
variant_assignment_control(variant_assignment_control&&) = default;
variant_assignment_control& operator=(const variant_assignment_control&) = default;
variant_assignment_control& operator=(variant_assignment_control&&) = delete;
};
template <>
struct variant_assignment_control<false, false>
{
variant_assignment_control() = default;
variant_assignment_control(const variant_assignment_control&) = default;
variant_assignment_control(variant_assignment_control&&) = default;
variant_assignment_control& operator=(const variant_assignment_control&) = delete;
variant_assignment_control& operator=(variant_assignment_control&&) = delete;
};
} // namespace private_variant
@ -483,7 +849,13 @@ namespace etl
///\ingroup variant
//***************************************************************************
template <typename... TTypes>
class variant : private private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>
class variant
: private private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>
, private private_variant::variant_constructor_control< private_variant::are_all_copy_constructible<TTypes...>::value,
private_variant::are_all_move_constructible<TTypes...>::value>
, private private_variant::variant_assignment_control<
private_variant::are_all_copy_constructible<TTypes...>::value && private_variant::are_all_copy_assignable<TTypes...>::value,
private_variant::are_all_move_constructible<TTypes...>::value && private_variant::are_all_move_assignable<TTypes...>::value>
{
using base_type = private_variant::variant_base<private_variant::are_all_trivially_destructible<TTypes...>::value, TTypes...>;
@ -671,31 +1043,13 @@ namespace etl
/// Copy constructor.
///\param other The other variant object to copy.
//***************************************************************************
#include "diagnostic_uninitialized_push.h"
ETL_CONSTEXPR14 variant(const variant& other) noexcept(etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value)
: base_type(other.type_id)
{
if (other.index() != variant_npos)
{
do_copy_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
}
}
#include "diagnostic_pop.h"
variant(const variant& other) = default;
//***************************************************************************
/// Move constructor.
///\param other The other variant object to copy.
//***************************************************************************
#include "diagnostic_uninitialized_push.h"
ETL_CONSTEXPR14 variant(variant&& other) noexcept(etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value)
: base_type(other.type_id)
{
if (other.index() != variant_npos)
{
do_move_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
}
}
#include "diagnostic_pop.h"
variant(variant&& other) = default;
//***************************************************************************
/// Destructor.
@ -806,51 +1160,13 @@ namespace etl
/// Assignment operator for variant type.
///\param other The variant to assign.
//***************************************************************************
variant& operator=(const variant& other) ETL_NOEXCEPT_IF((etl::conjunction<etl::is_nothrow_copy_constructible<TTypes>...>::value))
{
if (this != &other)
{
if (other.index() == variant_npos)
{
type_id = variant_npos;
}
else
{
do_destroy();
do_copy_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
type_id = other.type_id;
}
}
return *this;
}
variant& operator=(const variant& other) = default;
//***************************************************************************
/// Assignment operator for variant type.
/// Move assignment operator for variant type.
///\param other The variant to assign.
//***************************************************************************
variant& operator=(variant&& other) ETL_NOEXCEPT_IF((etl::conjunction<etl::is_nothrow_move_constructible<TTypes>...>::value))
{
if (this != &other)
{
if (other.index() == variant_npos)
{
type_id = variant_npos;
}
else
{
do_destroy();
do_move_from(other, etl::integral_constant<bool, Is_Trivially_Destructible_Suite>{});
type_id = other.type_id;
}
}
return *this;
}
variant& operator=(variant&& other) = default;
//***************************************************************************
/// Checks whether the variant doesn't contain a valid value.
@ -1149,36 +1465,6 @@ namespace etl
::new (static_cast<char*>(data)) T(etl::forward<U>(value));
}
//***************************************************************************
/// Copy from another variant.
//***************************************************************************
void do_copy_from(const variant& other, etl::integral_constant<bool, true>)
{
// Trivially destructible: copy via memcpy to avoid issues with non-trivial copy assignment in union.
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
void do_copy_from(const variant& other, etl::integral_constant<bool, false>)
{
// Non-trivially destructible: use switch-based dispatch.
private_variant::variant_operations<0, TTypes...>::copy(data, other.data, other.type_id);
}
//***************************************************************************
/// Move from another variant.
//***************************************************************************
void do_move_from(variant& other, etl::integral_constant<bool, true>)
{
// Trivially destructible: copy via memcpy (trivial move == copy).
memcpy(static_cast<void*>(&data), static_cast<const void*>(&other.data), sizeof(data));
}
void do_move_from(variant& other, etl::integral_constant<bool, false>)
{
// Non-trivially destructible: use switch-based dispatch.
private_variant::variant_operations<0, TTypes...>::move(data, other.data, other.type_id);
}
#if ETL_USING_CPP17 && !defined(ETL_VARIANT_FORCE_CPP11)
//***************************************************************************
/// Call the relevant visitor by attempting each one.

View File

@ -3323,9 +3323,30 @@ namespace etl
//*********************************************
// is_assignable
#if !ETL_USING_BUILTIN_IS_ASSIGNABLE && ETL_USING_CPP11
namespace private_type_traits
{
template <typename, typename T1, typename T2>
struct is_assignable_ : etl::false_type
{
};
template <typename T1, typename T2>
struct is_assignable_<etl::void_t<decltype(etl::declval<T1>() = etl::declval<T2>())>, T1, T2> : etl::true_type
{
};
} // namespace private_type_traits
#endif
template <typename T1, typename T2>
#if ETL_USING_BUILTIN_IS_ASSIGNABLE
struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)>
#elif ETL_USING_CPP11
// Builtin-free assignability detection. Used when the __is_assignable builtin
// is unavailable (e.g. MSVC, where determine_builtin_support.h disables the
// __has_builtin block). Mirrors the void_t-based is_constructible above so
// that class types are correctly reported as (copy/move) assignable.
struct is_assignable : public private_type_traits::is_assignable_<etl::void_t<>, T1, T2>
#else
struct is_assignable
: public etl::bool_constant< (etl::is_arithmetic<T1>::value || etl::is_pointer<T1>::value)

View File

@ -321,6 +321,26 @@ namespace
bool copied_to;
};
//*********************************************
// Copy constructible, but copy assignment is deleted.
struct CopyConstructibleNonCopyAssignable
{
CopyConstructibleNonCopyAssignable() = default;
CopyConstructibleNonCopyAssignable(const CopyConstructibleNonCopyAssignable&) = default;
CopyConstructibleNonCopyAssignable& operator=(const CopyConstructibleNonCopyAssignable&) = delete;
};
//*********************************************
// Move constructible, but move assignment is deleted.
struct MoveConstructibleNonMoveAssignable
{
MoveConstructibleNonMoveAssignable() = default;
MoveConstructibleNonMoveAssignable(MoveConstructibleNonMoveAssignable&&) = default;
MoveConstructibleNonMoveAssignable& operator=(MoveConstructibleNonMoveAssignable&&) = delete;
MoveConstructibleNonMoveAssignable(const MoveConstructibleNonMoveAssignable&) = delete;
MoveConstructibleNonMoveAssignable& operator=(const MoveConstructibleNonMoveAssignable&) = delete;
};
//*********************************************
// Trivially destructible, but non-copyable and non-movable.
// Exercises the trivially-destructible (variadic_union) emplace path.
@ -420,6 +440,109 @@ namespace
int value;
};
//*********************************************
// Non-trivially destructible type whose copy/move ASSIGNMENT has a
// distinguishable effect from copy/move CONSTRUCTION. Used to verify that
// same-type variant assignment dispatches through the alternative's own
// assignment operator (no destroy + reconstruct). Exercises the
// uninitialized_buffer (non-trivial) path.
struct AssignTracker
{
AssignTracker(int value_)
: value(value_)
, constructed(true)
, copy_assigned(false)
, move_assigned(false)
{
}
AssignTracker(const AssignTracker& other)
: value(other.value)
, constructed(true)
, copy_assigned(false)
, move_assigned(false)
{
}
AssignTracker(AssignTracker&& other)
: value(other.value)
, constructed(true)
, copy_assigned(false)
, move_assigned(false)
{
}
~AssignTracker() {}
AssignTracker& operator=(const AssignTracker& rhs)
{
value = rhs.value;
copy_assigned = true;
move_assigned = false;
return *this;
}
AssignTracker& operator=(AssignTracker&& rhs)
{
value = rhs.value;
copy_assigned = false;
move_assigned = true;
return *this;
}
int value;
bool constructed;
bool copy_assigned;
bool move_assigned;
};
//*********************************************
// Trivially destructible variant of AssignTracker (no user destructor) that
// exercises the trivially-destructible (variadic_union) path.
struct TrivialAssignTracker
{
TrivialAssignTracker(int value_)
: value(value_)
, copy_assigned(false)
, move_assigned(false)
{
}
TrivialAssignTracker(const TrivialAssignTracker& other)
: value(other.value)
, copy_assigned(false)
, move_assigned(false)
{
}
TrivialAssignTracker(TrivialAssignTracker&& other)
: value(other.value)
, copy_assigned(false)
, move_assigned(false)
{
}
TrivialAssignTracker& operator=(const TrivialAssignTracker& rhs)
{
value = rhs.value;
copy_assigned = true;
move_assigned = false;
return *this;
}
TrivialAssignTracker& operator=(TrivialAssignTracker&& rhs)
{
value = rhs.value;
copy_assigned = false;
move_assigned = true;
return *this;
}
int value;
bool copy_assigned;
bool move_assigned;
};
} // namespace
// Moved from the top of the file otherwise clang has issues with
@ -560,6 +683,26 @@ struct etl::is_move_constructible<NonTrivialNonMovable> : public etl::false_type
template <>
struct etl::is_move_constructible<NonAddressable> : public etl::true_type
{
};
template <>
struct etl::is_copy_constructible<AssignTracker> : public etl::true_type
{
};
template <>
struct etl::is_move_constructible<AssignTracker> : public etl::true_type
{
};
template <>
struct etl::is_copy_constructible<TrivialAssignTracker> : public etl::true_type
{
};
template <>
struct etl::is_move_constructible<TrivialAssignTracker> : public etl::true_type
{
};
#endif
@ -999,6 +1142,146 @@ namespace
CHECK_EQUAL(variant_1_etl.index(), variant_2_etl.index());
}
//*************************************************************************
// Issue #1512: a variant must only be copy constructible / copy assignable
// when every alternative is copy constructible. Moveable is move-only
// (its copy operations are deleted), so a variant containing it must not
// be copyable, while remaining move constructible / move assignable.
TEST(test_copy_disabled_with_non_copyable_alternative)
{
typedef etl::variant<int, Moveable> non_copyable_variant;
CHECK(!etl::is_copy_constructible<non_copyable_variant>::value);
CHECK(!etl::is_copy_assignable<non_copyable_variant>::value);
CHECK(etl::is_move_constructible<non_copyable_variant>::value);
CHECK(etl::is_move_assignable<non_copyable_variant>::value);
// A variant of purely copyable alternatives must remain copyable.
typedef etl::variant<int, Copyable> copyable_variant;
CHECK(etl::is_copy_constructible<copyable_variant>::value);
CHECK(etl::is_copy_assignable<copyable_variant>::value);
}
//*************************************************************************
// A non-copy-assignable alternative must disable the variant's copy
// assignment operator, and a non-move-assignable alternative must disable
// the variant's move assignment operator, while leaving the corresponding
// constructors available.
TEST(test_assignment_disabled_with_non_assignable_alternative)
{
typedef etl::variant<int, CopyConstructibleNonCopyAssignable> copy_non_assignable_variant;
CHECK(etl::is_copy_constructible<copy_non_assignable_variant>::value);
CHECK(!etl::is_copy_assignable<copy_non_assignable_variant>::value);
typedef etl::variant<int, MoveConstructibleNonMoveAssignable> move_non_assignable_variant;
CHECK(etl::is_move_constructible<move_non_assignable_variant>::value);
CHECK(!etl::is_move_assignable<move_non_assignable_variant>::value);
}
//*************************************************************************
// When source and destination hold the SAME alternative, assignment must
// dispatch through that alternative's own assignment operator rather than
// destroying and reconstructing it. Non-trivially-destructible path.
TEST(test_copy_assign_same_type_uses_alternative_assignment)
{
typedef etl::variant<int, AssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<AssignTracker>{}, 1);
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
variant_1 = variant_2;
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
CHECK(etl::get<AssignTracker>(variant_1).copy_assigned);
CHECK(!etl::get<AssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
TEST(test_move_assign_same_type_uses_alternative_assignment)
{
typedef etl::variant<int, AssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<AssignTracker>{}, 1);
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
variant_1 = etl::move(variant_2);
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
CHECK(!etl::get<AssignTracker>(variant_1).copy_assigned);
CHECK(etl::get<AssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
// When source and destination hold DIFFERENT alternatives, assignment must
// still destroy and reconstruct.
TEST(test_copy_assign_different_type_reconstructs)
{
typedef etl::variant<int, AssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<int>{}, 5);
tracker_variant variant_2(etl::in_place_type_t<AssignTracker>{}, 2);
variant_1 = variant_2;
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<AssignTracker>(variant_1).value);
CHECK(etl::get<AssignTracker>(variant_1).constructed);
CHECK(!etl::get<AssignTracker>(variant_1).copy_assigned);
CHECK(!etl::get<AssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
// Same as above but for the trivially-destructible (variadic_union) path.
TEST(test_assign_same_type_uses_alternative_assignment_trivial)
{
typedef etl::variant<int, TrivialAssignTracker> tracker_variant;
tracker_variant variant_1(etl::in_place_type_t<TrivialAssignTracker>{}, 1);
tracker_variant variant_2(etl::in_place_type_t<TrivialAssignTracker>{}, 2);
variant_1 = variant_2;
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(2, etl::get<TrivialAssignTracker>(variant_1).value);
CHECK(etl::get<TrivialAssignTracker>(variant_1).copy_assigned);
CHECK(!etl::get<TrivialAssignTracker>(variant_1).move_assigned);
tracker_variant variant_3(etl::in_place_type_t<TrivialAssignTracker>{}, 3);
variant_1 = etl::move(variant_3);
CHECK_EQUAL(1U, variant_1.index());
CHECK_EQUAL(3, etl::get<TrivialAssignTracker>(variant_1).value);
CHECK(etl::get<TrivialAssignTracker>(variant_1).move_assigned);
}
//*************************************************************************
// Issue #1512: even though the copy operations are disabled at compile
// time, a move-only variant must still move construct / move assign
// correctly, preserving the active alternative.
TEST(test_move_only_variant_moves_correctly)
{
typedef etl::variant<int, Moveable> non_copyable_variant;
non_copyable_variant variant_1(etl::in_place_type_t<Moveable>{});
CHECK_EQUAL(1U, variant_1.index());
// Move construction preserves the active alternative.
non_copyable_variant variant_2(etl::move(variant_1));
CHECK_EQUAL(1U, variant_2.index());
CHECK(etl::get<Moveable>(variant_2).moved_to);
// Move assignment preserves the active alternative.
non_copyable_variant variant_3;
variant_3 = etl::move(variant_2);
CHECK_EQUAL(1U, variant_3.index());
CHECK(etl::get<Moveable>(variant_3).moved_to);
}
//*************************************************************************
TEST(test_assign_from_value)
{