Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<variant>: P0608R3 Improving variant's converting constructor/assignment #1629

Merged
merged 19 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
29 changes: 23 additions & 6 deletions stl/inc/variant
Original file line number Diff line number Diff line change
Expand Up @@ -967,18 +967,34 @@ using _Variant_destroy_layer = conditional_t<conjunction_v<is_trivially_destruct
#pragma warning(disable : 4365) // '%s': conversion from '%s' to '%s', signed/unsigned mismatch
#pragma warning(disable : 5215) // '%s' a function parameter with volatile qualified type is deprecated in C++20
#endif // __clang__
template <size_t _Idx, class _Ty>
struct _Variant_type_test { // build Ti x[] = {std::forward<T>(t)};
template <size_t _Idx, class _Type>
static constexpr auto _Construct_array(_Type(&&)[1]) -> _Meta_list<integral_constant<size_t, _Idx>, _Type>;

template <size_t _Idx, class _Ty, class _Type>
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
using type = decltype(_Construct_array<_Idx, _Type>({_STD declval<_Ty&&>()}));
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
};

MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
template <size_t _Idx, class _Ty, class _Type>
using _Variant_type_resolver = typename _Variant_type_test::template type<_Idx, _Ty, _Type>;

template <size_t _Idx, class _Type>
struct _Variant_init_single_overload {
using _FTy = _Meta_list<integral_constant<size_t, _Idx>, _Ty> (*)(_Ty);
operator _FTy();
template <class _Ty>
using _FTy = _Variant_type_resolver<_Idx, _Ty, _Type>;

template <class _Ty>
auto operator()(_Ty&&, _Type) -> _FTy<_Ty>;
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
};

template <class _Indices, class... _Types>
struct _Variant_init_overload_set_;

template <size_t... _Indices, class... _Types>
struct _Variant_init_overload_set_<index_sequence<_Indices...>, _Types...>
: _Variant_init_single_overload<_Indices, _Types>... {};
: _Variant_init_single_overload<_Indices, _Types>... {
using _Variant_init_single_overload<_Indices, _Types>::operator()...;
};

template <class... _Types>
using _Variant_init_overload_set = _Variant_init_overload_set_<index_sequence_for<_Types...>, _Types...>;
Expand All @@ -987,11 +1003,12 @@ template <class Enable, class _Ty, class... _Types>
struct _Variant_init_helper {}; // failure case (has no member "type")

template <class _Ty, class... _Types>
struct _Variant_init_helper<void_t<decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>()))>, _Ty,
struct _Variant_init_helper<
void_t<decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty&&>(), _STD declval<_Ty>()))>, _Ty,
_Types...> {
// perform overload resolution to determine the unique alternative that should be initialized in
// variant<_Types...> from an argument expression with type and value category _Ty
using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>()));
using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty&&>(), _STD declval<_Ty>()));
};

template <class _Ty, class... _Types> // extract the type from _Variant_init_helper
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ tests\P0556R3_bit_integral_power_of_two_operations
tests\P0586R2_integer_comparison
tests\P0595R2_is_constant_evaluated
tests\P0607R0_inline_variables
tests\P0608R3_sane_variant_converting_constructor
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
tests\P0616R0_using_move_in_numeric
tests\P0631R8_numbers_math_constants
tests\P0660R10_jthread_and_cv_any
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0088R3_variant/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4681,6 +4681,7 @@ void test_T_ctor_basic() {
#endif
}

#ifndef __clang__ // TRANSITION, ...
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
struct BoomOnAnything {
template <class T>
constexpr BoomOnAnything(T) { static_assert(!std::is_same<T, T>::value, ""); }
Expand All @@ -4692,6 +4693,7 @@ void test_no_narrowing_check_for_class_types() {
assert(v.index() == 0);
assert(std::get<0>(v) == 42);
}
#endif // !__clang__

struct Bar {};
struct Baz {};
Expand All @@ -4708,7 +4710,9 @@ int run_test() {
test_T_ctor_basic();
test_T_ctor_noexcept();
test_T_ctor_sfinae();
#ifndef __clang__ // TRANSITION, ...
test_no_narrowing_check_for_class_types();
#endif // !__clang__
test_construction_with_repeated_types();
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_17_matrix.lst
164 changes: 164 additions & 0 deletions tests/std/tests/P0608R3_sane_variant_converting_constructor/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// Also tests for P1957R2: Converting from T* to bool should be considered narrowing

#include <assert.h>
#include <bitset>
#include <functional>
#include <optional>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>

using namespace std;

struct double_double {
double_double(double x) : x_(x) {}

double x_;
};
struct convertible_bool {
convertible_bool(bool x) : x_(x) {}
~convertible_bool() = default;
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved

operator bool() const noexcept {
return x_;
}

bool x_;
};

// P0608R3 examples
static_assert(is_constructible_v<variant<string, bool>, const char*>);
static_assert(is_constructible_v<variant<string, bool>, string>);
static_assert(is_constructible_v<variant<char, optional<char16_t>>, char16_t>);
static_assert(is_constructible_v<variant<int, reference_wrapper<double>>, double&>);
static_assert(is_constructible_v<variant<float, int>, char>);
static_assert(is_constructible_v<variant<float, long int>, int>);
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
static_assert(is_constructible_v<variant<float, long long int>, int>);
static_assert(is_constructible_v<variant<float, long, double>, int>);
static_assert(is_constructible_v<variant<float, vector<int>, long long int>, int>);
static_assert(is_constructible_v<variant<float, int, long long int>, char>);

static_assert(!is_constructible_v<variant<float>, int>);
static_assert(!is_constructible_v<variant<float, vector<int>>, int>);
static_assert(!is_constructible_v<variant<float, char>, int>);

// P1957R2 examples
static_assert(is_constructible_v<variant<bool, int>, bool>);
static_assert(is_constructible_v<variant<bool, int>, std::bitset<4>::reference>);
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
static_assert(is_constructible_v<variant<bool>, std::bitset<4>::reference>);

// More examples
static_assert(is_constructible_v<variant<double_double>, double>);
static_assert(is_constructible_v<variant<vector<vector<int>>, optional<int>, int>, int>);
static_assert(is_constructible_v<variant<vector<vector<int>>, optional<int>>, int>);
static_assert(is_constructible_v<variant<vector<int>, optional<int>, float>, int>);
static_assert(is_constructible_v<variant<bool>, convertible_bool>);
static_assert(is_constructible_v<variant<bool, int>, convertible_bool>);
static_assert(is_constructible_v<variant<convertible_bool>, bool>);
static_assert(is_constructible_v<variant<float, bool, convertible_bool>, convertible_bool>);
static_assert(is_constructible_v<variant<float, bool, convertible_bool>, bool>);
static_assert(is_constructible_v<variant<char, int>, bool>);

#ifndef __clang__ // TRANSITION, ...
static_assert(!is_constructible_v<variant<double_double>, int>);
#endif // !__clang__
static_assert(!is_constructible_v<variant<float>, unsigned int>);
static_assert(!is_constructible_v<variant<float, long int, long long int>, int>);

void test_variant_constructor_P0608R3() {
// P0608R3 runtime checks
variant<string, bool> a = "abc"; // string
assert(a.index() == 0);
assert(get<0>(a) == string("abc"));
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved

variant<char, optional<char16_t>> b = u'\u2043'; // optional<char16_t>
assert(b.index() == 1);
assert(get<optional<char16_t>>(b) == u'\u2043');

double c_data = 3.14;
variant<int, reference_wrapper<double>> c = c_data; // reference_wrapper<double>
assert(c.index() == 1);
assert(get<1>(c) == c_data);

using T1 = variant<float, int>;
T1 d;
assert(d.index() == 0);
d = 0; // int
assert(d.index() == 1);

using T2 = variant<float, long>;
T2 e;
assert(e.index() == 0);
e = 0; // long
assert(e.index() == 1);

variant<float, int> f = 'a'; // int
assert(f.index() == 1);
assert(get<int>(f) == 97);

variant<float, long> g = 0; // long
assert(g.index() == 1);

variant<float, long, double> h = 0; // long
assert(h.index() == 1);

variant<float, vector<int>, long long int> i = 0; // long long int
assert(i.index() == 2);

variant<float, int, long long int> j = 'a'; // int
assert(j.index() == 1);
assert(get<int>(j) == 97);
}

void test_variant_constructor_P1957R2() {
bitset<4> a_bitset("0101");
bool a_data = a_bitset[2];
variant<bool, int> a = a_data; // bool
assert(a.index() == 0);
assert(get<0>(a));

bitset<4> b_bitset("0101");
variant<bool, int> b = b_bitset[2]; // bool
variant<bool, int> b2 = b_bitset[1]; // bool
assert(b.index() == 0);
assert(get<0>(b));
assert(b2.index() == 0);
assert(!get<0>(b2));
}

void test_variant_constructor_more_tests() {
variant<char, int, float, bool, vector<bool>> a = true; // bool
assert(a.index() == 3);

variant<bool, int> b = convertible_bool{true}; // bool
assert(b.index() == 0);
assert(get<0>(b));

variant<char, int, bool> c = false; // bool
assert(c.index() == 2);

variant<float, bool, convertible_bool> d = convertible_bool{true}; // convertible_bool
assert(d.index() == 2);

variant<float, bool, convertible_bool> e = bool{}; // bool
assert(e.index() == 1);
assert(!get<1>(e));

variant<float, bool> f = convertible_bool{false}; // bool
assert(f.index() == 1);
assert(!get<1>(f));

variant<bool, int> g = true_type{}; // bool
assert(g.index() == 0);
assert(get<0>(g));
}

int main() {
MichaelRizkalla marked this conversation as resolved.
Show resolved Hide resolved
test_variant_constructor_P0608R3();
test_variant_constructor_P1957R2();
test_variant_constructor_more_tests();
}