blob: 5ee256e8468e25b8aacfe5e9f7eb29d723bd2f80 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/stdcompat/variant.h>
#include <string>
#include <type_traits>
#include <gtest/gtest.h>
#include "test_helper.h"
namespace {
struct no_copy {
no_copy(const no_copy&) = delete;
no_copy(no_copy&&) = default;
no_copy& operator=(const no_copy&) = delete;
no_copy& operator=(no_copy&&) = default;
};
struct no_move {
no_move(const no_move&) = default;
no_move(no_move&&) = delete;
no_move& operator=(const no_move&) = default;
no_move& operator=(no_move&&) = delete;
};
struct no_copy_no_move {
no_copy_no_move(const no_copy_no_move&) = delete;
no_copy_no_move(no_copy_no_move&&) = delete;
no_copy_no_move& operator=(const no_copy_no_move&) = delete;
no_copy_no_move& operator=(no_copy_no_move&&) = delete;
};
struct non_trivial_destructor {
~non_trivial_destructor() {}
};
struct non_trivial_copy {
non_trivial_copy(const non_trivial_copy&) {}
non_trivial_copy(non_trivial_copy&&) = default;
non_trivial_copy& operator=(const non_trivial_copy&) { return *this; }
non_trivial_copy& operator=(non_trivial_copy&&) = default;
};
struct non_trivial_move {
non_trivial_move(const non_trivial_move&) = default;
non_trivial_move(non_trivial_move&&) {}
non_trivial_move& operator=(const non_trivial_move&) = default;
non_trivial_move& operator=(non_trivial_move&&) { return *this; }
};
struct literal_traits {
using a_type = cpp17::monostate;
using b_type = int;
using c_type = double;
using variant = cpp17::variant<a_type, b_type, c_type>;
static constexpr a_type a_value{};
static constexpr b_type b_value = 10;
static constexpr c_type c_value = 2.5;
static constexpr c_type c2_value = 4.2;
static variant a, b, c;
static constexpr variant const_a{};
static constexpr variant const_b{cpp17::in_place_index<1>, b_value};
static constexpr variant const_c{cpp17::in_place_index<2>, c_value};
};
literal_traits::variant literal_traits::a;
literal_traits::variant literal_traits::b{cpp17::in_place_index<1>, literal_traits::b_value};
literal_traits::variant literal_traits::c{cpp17::in_place_index<2>, literal_traits::c_value};
struct complex_traits {
using a_type = cpp17::monostate;
using b_type = int;
using c_type = std::string;
using variant = cpp17::variant<a_type, b_type, c_type>;
static const a_type a_value;
static const b_type b_value;
static const c_type c_value;
static const c_type c2_value;
static variant a, b, c;
static const variant const_a;
static const variant const_b;
static const variant const_c;
};
const cpp17::monostate complex_traits::a_value{};
const int complex_traits::b_value = 10;
const std::string complex_traits::c_value = "test";
const std::string complex_traits::c2_value = "another";
complex_traits::variant complex_traits::a;
complex_traits::variant complex_traits::b{cpp17::in_place_index<1>, complex_traits::b_value};
complex_traits::variant complex_traits::c{cpp17::in_place_index<2>, complex_traits::c_value};
const complex_traits::variant complex_traits::const_a;
const complex_traits::variant complex_traits::const_b{cpp17::in_place_index<1>,
complex_traits::b_value};
const complex_traits::variant complex_traits::const_c{cpp17::in_place_index<2>,
complex_traits::c_value};
template <typename T>
void accessors() {
EXPECT_EQ(T::a.index(), 0u);
EXPECT_EQ(T::a_value, cpp17::get<0>(T::a));
EXPECT_EQ(T::a_value, cpp17::get<0>(T::const_a));
EXPECT_EQ(T::b.index(), 1u);
EXPECT_EQ(T::b_value, cpp17::get<1>(T::b));
EXPECT_EQ(T::b_value, cpp17::get<1>(T::const_b));
EXPECT_EQ(T::c.index(), 2u);
EXPECT_EQ(T::c_value, cpp17::get<2>(T::c));
EXPECT_EQ(T::c_value, cpp17::get<2>(T::const_c));
}
template <typename T>
void copy_move_assign() {
using b_type = typename T::b_type;
using c_type = typename T::c_type;
typename T::variant x;
EXPECT_EQ(0u, x.index());
EXPECT_EQ(T::a_value, cpp17::get<0>(x));
x = T::b;
EXPECT_EQ(1u, x.index());
EXPECT_TRUE(cpp17::holds_alternative<b_type>(x));
EXPECT_FALSE(cpp17::holds_alternative<c_type>(x));
EXPECT_EQ(T::b_value, cpp17::get<1>(x));
x.template emplace<2>(T::c_value);
EXPECT_EQ(2u, x.index());
EXPECT_FALSE(cpp17::holds_alternative<b_type>(x));
EXPECT_TRUE(cpp17::holds_alternative<c_type>(x));
EXPECT_EQ(T::c_value, cpp17::get<2>(x));
typename T::variant y(T::b);
EXPECT_EQ(1u, y.index());
EXPECT_EQ(T::b_value, cpp17::get<1>(y));
x = std::move(y);
EXPECT_EQ(1u, x.index());
EXPECT_EQ(T::b_value, cpp17::get<1>(x));
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
#endif
x = x;
EXPECT_EQ(1u, x.index());
EXPECT_EQ(T::b_value, cpp17::get<1>(x));
#ifdef __clang__
#pragma clang diagnostic pop
#endif
x = std::move(x);
EXPECT_EQ(1u, x.index());
EXPECT_TRUE(cpp17::holds_alternative<b_type>(x));
EXPECT_FALSE(cpp17::holds_alternative<c_type>(x));
EXPECT_EQ(T::b_value, cpp17::get<1>(x));
x = T::a;
EXPECT_EQ(0u, x.index());
EXPECT_EQ(T::a_value, cpp17::get<0>(x));
x = T::c;
typename T::variant z(std::move(x));
EXPECT_EQ(2u, z.index());
EXPECT_FALSE(cpp17::holds_alternative<b_type>(z));
EXPECT_TRUE(cpp17::holds_alternative<c_type>(z));
EXPECT_EQ(T::c_value, cpp17::get<2>(z));
}
template <typename T>
void swapping() {
typename T::variant x;
EXPECT_EQ(0u, x.index());
EXPECT_EQ(T::a_value, cpp17::get<0>(x));
typename T::variant y(T::c);
y.swap(y);
EXPECT_EQ(2u, y.index());
EXPECT_EQ(T::c_value, cpp17::get<2>(y));
x.swap(y);
EXPECT_EQ(2u, x.index());
EXPECT_EQ(T::c_value, cpp17::get<2>(x));
EXPECT_EQ(0u, y.index());
EXPECT_EQ(T::a_value, cpp17::get<0>(y));
y.template emplace<2>(T::c2_value);
x.swap(y);
EXPECT_EQ(2u, x.index());
EXPECT_EQ(T::c2_value, cpp17::get<2>(x));
EXPECT_EQ(2u, y.index());
EXPECT_EQ(T::c_value, cpp17::get<2>(y));
x = T::b;
y.swap(x);
EXPECT_EQ(2u, x.index());
EXPECT_EQ(T::c_value, cpp17::get<2>(x));
EXPECT_EQ(1u, y.index());
EXPECT_EQ(T::b_value, cpp17::get<1>(y));
x = T::a;
y.swap(x);
EXPECT_EQ(1u, x.index());
EXPECT_EQ(T::b_value, cpp17::get<1>(x));
EXPECT_EQ(0u, y.index());
EXPECT_EQ(T::a_value, cpp17::get<0>(y));
}
template <typename T>
void get_wrong_type() {
ASSERT_THROW_OR_ABORT(
{
using b_type = typename T::b_type;
typename T::variant x;
EXPECT_EQ(0u, x.index());
cpp17::get<b_type>(x);
},
cpp17::bad_variant_access);
}
template <typename T>
void get_wrong_index() {
ASSERT_THROW_OR_ABORT(
{
typename T::variant x;
EXPECT_EQ(0u, x.index());
cpp17::get<1>(x);
},
cpp17::bad_variant_access);
}
// Test constexpr behavior.
namespace constexpr_test {
static_assert(literal_traits::variant().index() == 0, "");
static_assert(literal_traits::const_a.index() == 0, "");
static_assert(cpp17::get<0>(literal_traits::const_a) == literal_traits::a_value, "");
static_assert(literal_traits::const_b.index() == 1, "");
static_assert(cpp17::get<1>(literal_traits::const_b) == literal_traits::b_value, "");
static_assert(literal_traits::const_c.index() == 2, "");
static_assert(cpp17::get<2>(literal_traits::const_c) == literal_traits::c_value, "");
} // namespace constexpr_test
// Test comparisons.
namespace comparison_tests {
struct greater {};
struct less {};
constexpr bool operator==(greater, greater) { return true; }
constexpr bool operator<=(greater, greater) { return true; }
constexpr bool operator>=(greater, greater) { return true; }
constexpr bool operator!=(greater, greater) { return false; }
constexpr bool operator<(greater, greater) { return false; }
constexpr bool operator>(greater, greater) { return false; }
constexpr bool operator==(less, less) { return true; }
constexpr bool operator<=(less, less) { return true; }
constexpr bool operator>=(less, less) { return true; }
constexpr bool operator!=(less, less) { return false; }
constexpr bool operator<(less, less) { return false; }
constexpr bool operator>(less, less) { return false; }
constexpr bool operator==(greater, less) { return false; }
constexpr bool operator<=(greater, less) { return false; }
constexpr bool operator>=(greater, less) { return true; }
constexpr bool operator!=(greater, less) { return true; }
constexpr bool operator<(greater, less) { return false; }
constexpr bool operator>(greater, less) { return true; }
constexpr bool operator==(less, greater) { return false; }
constexpr bool operator<=(less, greater) { return true; }
constexpr bool operator>=(less, greater) { return false; }
constexpr bool operator!=(less, greater) { return true; }
constexpr bool operator<(less, greater) { return true; }
constexpr bool operator>(less, greater) { return false; }
// These definitions make cpp17::monostate always compare less than
// |less| and |greater|.
constexpr bool operator==(cpp17::monostate, greater) { return false; }
constexpr bool operator<=(cpp17::monostate, greater) { return true; }
constexpr bool operator>=(cpp17::monostate, greater) { return false; }
constexpr bool operator!=(cpp17::monostate, greater) { return true; }
constexpr bool operator<(cpp17::monostate, greater) { return true; }
constexpr bool operator>(cpp17::monostate, greater) { return false; }
constexpr bool operator==(greater, cpp17::monostate) { return false; }
constexpr bool operator<=(greater, cpp17::monostate) { return false; }
constexpr bool operator>=(greater, cpp17::monostate) { return true; }
constexpr bool operator!=(greater, cpp17::monostate) { return true; }
constexpr bool operator<(greater, cpp17::monostate) { return false; }
constexpr bool operator>(greater, cpp17::monostate) { return true; }
constexpr bool operator==(cpp17::monostate, less) { return false; }
constexpr bool operator<=(cpp17::monostate, less) { return true; }
constexpr bool operator>=(cpp17::monostate, less) { return false; }
constexpr bool operator!=(cpp17::monostate, less) { return true; }
constexpr bool operator<(cpp17::monostate, less) { return true; }
constexpr bool operator>(cpp17::monostate, less) { return false; }
constexpr bool operator==(less, cpp17::monostate) { return false; }
constexpr bool operator<=(less, cpp17::monostate) { return false; }
constexpr bool operator>=(less, cpp17::monostate) { return true; }
constexpr bool operator!=(less, cpp17::monostate) { return true; }
constexpr bool operator<(less, cpp17::monostate) { return false; }
constexpr bool operator>(less, cpp17::monostate) { return true; }
template <typename T, typename U>
constexpr bool match_comparisons(T lhs, U rhs) {
// Use the following variant for all of the tests below. Note that the types
// are ordered such that unlike variant comparisons yield a total order.
// That is: cpp17::monostate < less < greater.
using variant = cpp17::variant<cpp17::monostate, less, greater>;
static_assert((variant{lhs} == variant{rhs}) == (lhs == rhs), "");
static_assert((variant{lhs} != variant{rhs}) == (lhs != rhs), "");
static_assert((variant{lhs} <= variant{rhs}) == (lhs <= rhs), "");
static_assert((variant{lhs} >= variant{rhs}) == (lhs >= rhs), "");
static_assert((variant{lhs} < variant{rhs}) == (lhs < rhs), "");
static_assert((variant{lhs} > variant{rhs}) == (lhs > rhs), "");
return true;
}
static_assert(match_comparisons(cpp17::monostate{}, cpp17::monostate{}), "");
static_assert(match_comparisons(cpp17::monostate{}, less{}), "");
static_assert(match_comparisons(cpp17::monostate{}, greater{}), "");
static_assert(match_comparisons(less{}, cpp17::monostate{}), "");
static_assert(match_comparisons(less{}, less{}), "");
static_assert(match_comparisons(less{}, greater{}), "");
static_assert(match_comparisons(greater{}, cpp17::monostate{}), "");
static_assert(match_comparisons(greater{}, less{}), "");
static_assert(match_comparisons(greater{}, greater{}), "");
} // namespace comparison_tests
// Ensure the variant is copy-constructible only when the types are copyable.
namespace copy_construction_test {
static_assert(std::is_copy_constructible<cpp17::variant<cpp17::monostate>>::value, "");
static_assert(!std::is_copy_constructible<cpp17::variant<cpp17::monostate, no_copy>>::value, "");
static_assert(std::is_copy_constructible<cpp17::variant<cpp17::monostate, no_move>>::value, "");
static_assert(!std::is_copy_constructible<cpp17::variant<cpp17::monostate, no_copy_no_move>>::value,
"");
static_assert(std::is_copy_constructible<literal_traits::variant>::value, "");
static_assert(std::is_copy_constructible<complex_traits::variant>::value, "");
} // namespace copy_construction_test
// Ensure the variant is copy-assignable only when the types are copyable.
namespace copy_assignment_test {
static_assert(std::is_copy_assignable<cpp17::variant<cpp17::monostate>>::value, "");
static_assert(!std::is_copy_assignable<cpp17::variant<cpp17::monostate, no_copy>>::value, "");
static_assert(std::is_copy_assignable<cpp17::variant<cpp17::monostate, no_move>>::value, "");
static_assert(!std::is_copy_assignable<cpp17::variant<cpp17::monostate, no_copy_no_move>>::value,
"");
static_assert(std::is_copy_assignable<literal_traits::variant>::value, "");
static_assert(std::is_copy_assignable<complex_traits::variant>::value, "");
} // namespace copy_assignment_test
// Ensure the variant is move-constructible only when the types are movable.
// Note that copy-constructible types are also considered movable.
namespace move_construction_test {
static_assert(std::is_move_constructible<cpp17::variant<cpp17::monostate>>::value, "");
static_assert(std::is_move_constructible<cpp17::variant<cpp17::monostate, no_copy>>::value, "");
static_assert(std::is_move_constructible<cpp17::variant<cpp17::monostate, no_move>>::value, "");
static_assert(!std::is_move_constructible<cpp17::variant<cpp17::monostate, no_copy_no_move>>::value,
"");
static_assert(std::is_move_constructible<literal_traits::variant>::value, "");
static_assert(std::is_move_constructible<complex_traits::variant>::value, "");
} // namespace move_construction_test
// Ensure the variant is move-assignable only when the types are movable.
// Note that copy-assignable types are also considered movable.
namespace move_assignment_test {
static_assert(std::is_move_assignable<cpp17::variant<cpp17::monostate>>::value, "");
static_assert(std::is_move_assignable<cpp17::variant<cpp17::monostate, no_copy>>::value, "");
static_assert(std::is_move_assignable<cpp17::variant<cpp17::monostate, no_move>>::value, "");
static_assert(!std::is_move_assignable<cpp17::variant<cpp17::monostate, no_copy_no_move>>::value,
"");
static_assert(std::is_move_assignable<literal_traits::variant>::value, "");
static_assert(std::is_move_assignable<complex_traits::variant>::value, "");
} // namespace move_assignment_test
// Ensure that the correct sequence of base types are considered in the
// implementation of variant to ensure that the right methods participate
// in overload resolution.
namespace impl_test {
// Type with a trivial destructor, move, and copy.
namespace trivial_type {
static_assert(std::is_trivially_destructible<cpp17::variant<cpp17::monostate, int>>::value, "");
static_assert(std::is_trivially_move_constructible<cpp17::variant<cpp17::monostate, int>>::value,
"");
static_assert(std::is_trivially_copy_constructible<cpp17::variant<cpp17::monostate, int>>::value,
"");
static_assert(std::is_trivially_move_assignable<cpp17::variant<cpp17::monostate, int>>::value, "");
static_assert(std::is_trivially_copy_assignable<cpp17::variant<cpp17::monostate, int>>::value, "");
} // namespace trivial_type
// Type with a non-trivial destructor implies it has non-trivial move and copy too.
namespace non_trivial_destructor_type {
static_assert(!std::is_trivially_destructible<
cpp17::variant<cpp17::monostate, non_trivial_destructor>>::value,
"");
static_assert(!std::is_trivially_move_constructible<
cpp17::variant<cpp17::monostate, non_trivial_destructor>>::value,
"");
static_assert(!std::is_trivially_copy_constructible<
cpp17::variant<cpp17::monostate, non_trivial_destructor>>::value,
"");
static_assert(!std::is_trivially_move_assignable<
cpp17::variant<cpp17::monostate, non_trivial_destructor>>::value,
"");
static_assert(!std::is_trivially_copy_assignable<
cpp17::variant<cpp17::monostate, non_trivial_destructor>>::value,
"");
} // namespace non_trivial_destructor_type
// Type with a non-trivial move constructor actually ends up being trivially
// movable anyhow if it has a trivial copy constructor and destructor.
namespace non_trivial_move_type {
static_assert(
std::is_trivially_destructible<cpp17::variant<cpp17::monostate, non_trivial_move>>::value, "");
static_assert(!std::is_trivially_move_constructible<
cpp17::variant<cpp17::monostate, non_trivial_move>>::value,
"");
static_assert(
std::is_trivially_copy_constructible<cpp17::variant<cpp17::monostate, non_trivial_move>>::value,
"");
static_assert(
!std::is_trivially_move_assignable<cpp17::variant<cpp17::monostate, non_trivial_move>>::value,
"");
static_assert(
std::is_trivially_copy_assignable<cpp17::variant<cpp17::monostate, non_trivial_move>>::value,
"");
} // namespace non_trivial_move_type
// Type with a non-trivial copy constructor may be trivially movable while not
// trivially copyable.
namespace non_trivial_copy_type {
static_assert(
std::is_trivially_destructible<cpp17::variant<cpp17::monostate, non_trivial_copy>>::value, "");
static_assert(
std::is_trivially_move_constructible<cpp17::variant<cpp17::monostate, non_trivial_copy>>::value,
"");
static_assert(!std::is_trivially_copy_constructible<
cpp17::variant<cpp17::monostate, non_trivial_copy>>::value,
"");
static_assert(
std::is_trivially_move_assignable<cpp17::variant<cpp17::monostate, non_trivial_copy>>::value,
"");
static_assert(
!std::is_trivially_copy_assignable<cpp17::variant<cpp17::monostate, non_trivial_copy>>::value,
"");
} // namespace non_trivial_copy_type
// std::string is not trivally destructible, movable, or copyable.
namespace string_type {
static_assert(!std::is_trivially_destructible<cpp17::variant<cpp17::monostate, std::string>>::value,
"");
static_assert(
!std::is_trivially_move_constructible<cpp17::variant<cpp17::monostate, std::string>>::value,
"");
static_assert(
!std::is_trivially_copy_constructible<cpp17::variant<cpp17::monostate, std::string>>::value,
"");
static_assert(
!std::is_trivially_move_assignable<cpp17::variant<cpp17::monostate, std::string>>::value, "");
static_assert(
!std::is_trivially_copy_assignable<cpp17::variant<cpp17::monostate, std::string>>::value, "");
} // namespace string_type
} // namespace impl_test
TEST(VariantTest, AccessorsOnLiterals) { accessors<literal_traits>(); }
TEST(VariantTest, AccessorsOnComplex) { accessors<complex_traits>(); }
TEST(VariantTest, CopyMoveAssignWithLiteral) { copy_move_assign<literal_traits>(); }
TEST(VariantTest, CopyMoveAssignWithComplex) { copy_move_assign<complex_traits>(); }
TEST(VariantTest, SwappingWithLiteral) { swapping<literal_traits>(); }
TEST(VariantTest, SwappingWithComplex) { swapping<complex_traits>(); }
TEST(VariantTest, GetWrongTypeAbortsLiteral) { get_wrong_type<literal_traits>(); }
TEST(VariantTest, GetWrongTypeAbortsComplex) { get_wrong_type<complex_traits>(); }
TEST(VariantTest, GetWrongIndexAbortsLiteral) { get_wrong_index<literal_traits>(); }
TEST(VariantTest, GetWrongIndexAbortsComplex) { get_wrong_index<complex_traits>(); }
#if __cpp_lib_variant >= 201606L && !defined(LIB_STDCOMPAT_USE_POLYFILLS)
// Sanity check that the template switches correctly.
TEST(VariantTest, PolyfillIsAliasWhenVariantIsAvailable) {
static_assert(std::is_same_v<std::variant<int, float>, cpp17::variant<int, float>>);
}
#endif
} // namespace