Enable `variant` P0608R3 in C++17, update LLVM, overhaul `variant`/`any`/`optional` tests (#4713)

This commit is contained in:
Stephan T. Lavavej 2024-06-17 22:59:30 -07:00 коммит произвёл GitHub
Родитель b483591d25
Коммит 18c09c48f5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 3541 добавлений и 2081 удалений

Просмотреть файл

@ -15,7 +15,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/llvm/llvm-project.git",
"commitHash": "2e2b6b53f5f63179b52168ee156df7c76b90bc71"
"commitHash": "12fcca0afeb08fbe41d79c5387cfacb249992bb4"
}
}
},

@ -1 +1 @@
Subproject commit ded04bf5d32a4fd5e0919053a598443f9d773549
Subproject commit 12fcca0afeb08fbe41d79c5387cfacb249992bb4

Просмотреть файл

@ -862,24 +862,17 @@ using _Variant_destroy_layer = conditional_t<conjunction_v<is_trivially_destruct
#pragma warning(disable : 5215) // '%s' a function parameter with volatile qualified type is deprecated in C++20
#endif // ^^^ not Clang ^^^
#if _HAS_CXX20
// build Ti x[] = {std::forward<T>(t)};
template <size_t _Idx, class _TargetType>
auto _Construct_array(_TargetType (&&)[1]) -> _Meta_list<integral_constant<size_t, _Idx>, _TargetType>;
template <size_t _Idx, class _TargetType, class _InitializerType>
using _Variant_type_resolver = decltype(_STD _Construct_array<_Idx, _TargetType>({_STD declval<_InitializerType>()}));
#endif // _HAS_CXX20
template <size_t _Idx, class _TargetType>
struct _Variant_init_single_overload {
#if _HAS_CXX20
template <class _InitializerType>
auto operator()(_TargetType, _InitializerType&&) -> _Variant_type_resolver<_Idx, _TargetType, _InitializerType>;
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
template <class _InitializerType>
auto operator()(_TargetType, _InitializerType&&) -> _Meta_list<integral_constant<size_t, _Idx>, _TargetType>;
#endif // ^^^ !_HAS_CXX20 ^^^
};
template <class _Indices, class... _Types>

Просмотреть файл

@ -121,6 +121,7 @@
// P0602R4 Propagating Copy/Move Triviality In variant/optional
// P0604R0 invoke_result, is_invocable, is_nothrow_invocable
// P0607R0 Inline Variables For The STL
// P0608R3 Improving variant's Converting Constructor/Assignment
// P0682R1 Repairing Elementary String Conversions
// P0739R0 Improving Class Template Argument Deduction For The STL
// P0858R0 Constexpr Iterator Requirements
@ -185,7 +186,6 @@
// P0586R2 Integer Comparison Functions
// P0591R4 Utility Functions For Uses-Allocator Construction
// P0595R2 is_constant_evaluated()
// P0608R3 Improving variant's Converting Constructor/Assignment
// P0616R0 Using move() In <numeric>
// P0631R8 <numbers> Math Constants
// P0645R10 <format> Text Formatting

Просмотреть файл

@ -307,6 +307,7 @@ tests\P0053R7_cpp_synchronized_buffered_ostream
tests\P0067R5_charconv
tests\P0083R3_splicing_maps_and_sets
tests\P0088R3_variant
tests\P0088R3_variant_msvc
tests\P0092R1_polishing_chrono
tests\P0122R7_span
tests\P0122R7_span_death

Просмотреть файл

@ -1,56 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# This is `usual_17_matrix.lst`, with `/DCONSTEXPR_NOTHROW` added to C1XX permissive configurations since the test is
# sensitive to our permissive-mode treatment of calls to potentially-throwing functions as `noexcept` when they are
# constant expressions.
RUNALL_INCLUDE ..\prefix.lst
RUNALL_CROSSLIST
* PM_CL="/w14640 /Zc:threadSafeInit-"
RUNALL_CROSSLIST
PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:noexceptTypes-"
ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE"
ASAN PM_CL="/EHsc /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++20"
ASAN PM_CL="/EHsc /MD /std:c++20 -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-"
ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor"
ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t-"
ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-"
ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /fp:except /Zc:preprocessor"
ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /fp:except /Zc:preprocessor -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++17 /permissive-"
ASAN PM_CL="/EHsc /MDd /std:c++17 /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++20 /permissive-"
ASAN PM_CL="/EHsc /MDd /std:c++20 /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive-"
ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze:only /analyze:autolog-"
ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-"
# No corresponding ASAN config, since the above differs from another config only in IDL
PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /fp:strict"
ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-"
ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE"
ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE -fsanitize=address /Zi" PM_LINK="/debug"
PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog-"
ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- -fsanitize=address /Zi" PM_LINK="/debug"
# With /clr /std:c++20, extreme compiler memory consumption causes test timeouts.
PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE"
PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE"
PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-"
PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-"
PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive-"
PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-"
PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++latest /permissive-"
PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MDd /std:c++17 /DTEST_PERMISSIVE"
PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-"
PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MTd /std:c++latest /permissive- /fp:strict"
RUNALL_INCLUDE ..\usual_17_matrix.lst

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\usual_17_matrix.lst

Просмотреть файл

@ -0,0 +1,844 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#define _SILENCE_CXX23_ALIGNED_UNION_DEPRECATION_WARNING
#define _LIBCXX_IN_DEVCRT
#include <msvc_stdlib_force_include.h> // Must precede any other libc++ headers
// Include Standard headers:
#include <cassert>
#include <cstddef>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
// Include llvm-project/libcxx/test/support headers:
#include <test_macros.h>
#include <type_id.h>
#include <variant_test_helpers.h>
namespace msvc {
namespace size {
template <class T>
using element =
std::conditional<std::is_reference<T>::value, std::reference_wrapper<std::remove_reference_t<T>>, T>;
template <std::size_t N>
using index_t =
std::conditional_t<(N < static_cast<std::size_t>(std::numeric_limits<signed char>::max())), signed char,
std::conditional_t<(N < static_cast<std::size_t>(std::numeric_limits<short>::max())), short, int>>;
template <class... Ts>
struct fake_variant {
std::aligned_union_t<0, typename element<Ts>::type...> data_;
index_t<sizeof...(Ts)> index_;
};
template <class... Ts>
constexpr bool check_size = sizeof(std::variant<Ts...>) == sizeof(fake_variant<Ts...>);
template <int>
struct empty {};
struct not_empty {
int i;
};
struct __declspec(empty_bases) many_bases : empty<0>, empty<1>, empty<2>, empty<3> {};
static_assert(check_size<bool>);
static_assert(check_size<char>);
static_assert(check_size<unsigned char>);
static_assert(check_size<int>);
static_assert(check_size<unsigned int>);
static_assert(check_size<long>);
static_assert(check_size<long long>);
static_assert(check_size<float>);
static_assert(check_size<double>);
static_assert(check_size<void*>);
static_assert(check_size<empty<0>>);
static_assert(check_size<not_empty>);
static_assert(check_size<many_bases>);
static_assert(check_size<bool, char, short, int, long, long long, float, double, long double, void*, empty<0>,
empty<1>, not_empty, many_bases>);
} // namespace size
namespace big_variant {
#ifdef __EDG__
constexpr std::size_t big = 20;
#else // C1XX and Clang
constexpr std::size_t big = 64;
#endif // tune value of "big" to a bit less than the largest variant the front-end can handle
constexpr std::size_t n = 16;
template <std::size_t Size, std::size_t I, std::size_t N>
void test_gets() {
using V = std::_Meta_repeat_n_c<Size, std::string, std::variant>;
V v{std::in_place_index<I>, "Hello, world!"};
assert(std::get<I>(v) == "Hello, world!");
if constexpr (N != 0) {
test_gets<Size, I + (Size - I - 1) / N, N - 1>();
}
}
template <std::size_t Size>
void test_size() {
if constexpr (Size <= big) {
using V = std::_Meta_repeat_n_c<Size, std::string, std::variant>;
// test 0, and n equally spaced indices including big - 1
test_gets < Size, 0, n<Size ? n : Size>();
constexpr std::size_t i = Size / 2;
V v1{std::in_place_index<i>, "Hello, world!"};
V v2 = v1;
assert(std::get<i>(v2) == "Hello, world!");
V v3 = std::move(v1);
assert(std::get<i>(v3) == "Hello, world!");
constexpr std::size_t j = Size <= 2 ? 0 : i + 1;
v2.template emplace<j>("Goodbye, world!");
v1 = v2;
assert(std::get<j>(v1) == "Goodbye, world!");
v2 = std::move(v3);
assert(std::get<i>(v2) == "Hello, world!");
v1.swap(v2);
assert(std::get<j>(v2) == "Goodbye, world!");
assert(std::get<i>(v1) == "Hello, world!");
auto visitor = [](const std::string& s) { return s; };
assert(std::visit(visitor, v1) == "Hello, world!");
}
}
void run_test() {
// test with all of the power-of-four sizes used for the switch blocks in visit and _Variant_raw_visit
test_size<1>();
test_size<3>();
test_size<15>();
test_size<63>();
test_size<255>();
test_size<big>();
}
} // namespace big_variant
namespace derived_variant {
void run_test() {
// Extension: std::visit accepts types derived from a specialization of variant.
{
struct my_variant : std::variant<int, char, double> {
using std::variant<int, char, double>::variant;
};
my_variant v1{42};
my_variant v2{3.14};
auto visitor1 = [](auto&& x) { return static_cast<double>(x); };
assert(std::visit(visitor1, v1) == 42.0);
assert(std::visit(visitor1, v2) == 3.14);
auto visitor2 = [](auto&& x, auto&& y) { return static_cast<double>(x + y); };
assert(std::visit(visitor2, v1, v2) == 45.14);
}
{
struct MakeEmptyT {
MakeEmptyT() = default;
MakeEmptyT(MakeEmptyT&&) {
throw 42;
}
MakeEmptyT& operator=(MakeEmptyT&&) {
throw 42;
}
};
struct my_variant : std::variant<int, MakeEmptyT> {
using std::variant<int, MakeEmptyT>::variant;
};
my_variant v{42};
try {
v = my_variant{std::in_place_type<MakeEmptyT>};
abort();
} catch (int) {
assert(v.valueless_by_exception());
}
auto very_useful_visitor = [](auto&&...) { abort(); };
try {
std::visit(very_useful_visitor, v);
abort();
} catch (const std::bad_variant_access&) {
}
try {
std::visit(very_useful_visitor, my_variant{42}, v);
abort();
} catch (const std::bad_variant_access&) {
}
try {
std::visit(very_useful_visitor, v, my_variant{42});
abort();
} catch (const std::bad_variant_access&) {
}
}
}
} // namespace derived_variant
template <class Fn>
struct mobile_visitor {
mobile_visitor() = default;
mobile_visitor(const mobile_visitor&) {
abort();
}
mobile_visitor(mobile_visitor&&) {
abort();
}
mobile_visitor& operator=(const mobile_visitor&) {
abort();
return *this;
}
mobile_visitor& operator=(mobile_visitor&&) {
abort();
return *this;
}
template <class... Args>
constexpr decltype(auto) operator()(Args&&... args) const {
return Fn{}(std::forward<Args>(args)...);
}
};
template <class Fn>
struct immobile_visitor : mobile_visitor<Fn> {
immobile_visitor() = default;
immobile_visitor(const immobile_visitor&) = delete;
immobile_visitor& operator=(const immobile_visitor&) = delete;
};
template <class T>
struct convert_to {
template <class U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
constexpr T operator()(U&& u) const {
return std::forward<U>(u);
}
};
namespace visit {
void test_immobile_function() {
{
// Validate that visit need not copy or move the visitor
using V = std::variant<int, double>;
immobile_visitor<convert_to<double>> visitor{};
assert(std::visit(visitor, V{42}) == 42.0);
assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14);
assert(std::visit(std::move(visitor), V{1729}) == 1729.0);
assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414);
}
{
// Validate that visit does not copy or move the visitor
using V = std::variant<int, double>;
mobile_visitor<convert_to<double>> visitor{};
assert(std::visit(visitor, V{42}) == 42.0);
assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14);
assert(std::visit(std::move(visitor), V{1729}) == 1729.0);
assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414);
}
}
void run_test() {
test_immobile_function();
}
} // namespace visit
namespace visit_R {
#if _HAS_CXX20
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
enum CallType : unsigned int { CT_None, CT_NonConst = 1, CT_Const = 2, CT_LValue = 4, CT_RValue = 8 };
constexpr CallType operator|(CallType LHS, CallType RHS) {
return static_cast<CallType>(static_cast<unsigned int>(LHS) | static_cast<unsigned int>(RHS));
}
struct ForwardingCallObject {
template <class... Args>
bool operator()(Args&&...) & {
set_call<Args&&...>(CT_NonConst | CT_LValue);
return {};
}
template <class... Args>
char operator()(Args&&...) const& {
set_call<Args&&...>(CT_Const | CT_LValue);
return {};
}
// Don't allow the call operator to be invoked as an rvalue.
template <class... Args>
short operator()(Args&&...) && {
set_call<Args&&...>(CT_NonConst | CT_RValue);
return {};
}
template <class... Args>
int operator()(Args&&...) const&& {
set_call<Args&&...>(CT_Const | CT_RValue);
return {};
}
template <class... Args>
static void set_call(CallType type) {
assert(last_call_type == CT_None);
assert(last_call_args == nullptr);
last_call_type = type;
last_call_args = std::addressof(makeArgumentID<Args...>());
}
template <class... Args>
static bool check_call(CallType type) {
bool result = last_call_type == type && last_call_args && *last_call_args == makeArgumentID<Args...>();
last_call_type = CT_None;
last_call_args = nullptr;
return result;
}
static CallType last_call_type;
static const TypeID* last_call_args;
};
CallType ForwardingCallObject::last_call_type = CT_None;
const TypeID* ForwardingCallObject::last_call_args = nullptr;
template <class R>
void test_call_operator_forwarding() {
using Fn = ForwardingCallObject;
Fn obj{};
const Fn& cobj = obj;
{ // test call operator forwarding - no variant
std::visit<R>(obj);
assert(Fn::check_call<>(CT_NonConst | CT_LValue));
std::visit<R>(cobj);
assert(Fn::check_call<>(CT_Const | CT_LValue));
std::visit<R>(std::move(obj));
assert(Fn::check_call<>(CT_NonConst | CT_RValue));
std::visit<R>(std::move(cobj));
assert(Fn::check_call<>(CT_Const | CT_RValue));
}
{ // test call operator forwarding - single variant, single arg
using V = std::variant<int>;
V v(42);
std::visit<R>(obj, v);
assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
std::visit<R>(cobj, v);
assert(Fn::check_call<int&>(CT_Const | CT_LValue));
std::visit<R>(std::move(obj), v);
assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
std::visit<R>(std::move(cobj), v);
assert(Fn::check_call<int&>(CT_Const | CT_RValue));
}
{ // test call operator forwarding - single variant, multi arg
using V = std::variant<int, long, double>;
V v(42l);
std::visit<R>(obj, v);
assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
std::visit<R>(cobj, v);
assert(Fn::check_call<long&>(CT_Const | CT_LValue));
std::visit<R>(std::move(obj), v);
assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
std::visit<R>(std::move(cobj), v);
assert(Fn::check_call<long&>(CT_Const | CT_RValue));
}
{ // test call operator forwarding - multi variant, multi arg
using V = std::variant<int, long, double>;
using V2 = std::variant<int*, std::string>;
V v(42l);
V2 v2("hello");
std::visit<R>(obj, v, v2);
assert((Fn::check_call<long&, std::string&>(CT_NonConst | CT_LValue)));
std::visit<R>(cobj, v, v2);
assert((Fn::check_call<long&, std::string&>(CT_Const | CT_LValue)));
std::visit<R>(std::move(obj), v, v2);
assert((Fn::check_call<long&, std::string&>(CT_NonConst | CT_RValue)));
std::visit<R>(std::move(cobj), v, v2);
assert((Fn::check_call<long&, std::string&>(CT_Const | CT_RValue)));
}
}
template <class R>
void test_argument_forwarding() {
using Fn = ForwardingCallObject;
Fn obj{};
const auto Val = CT_LValue | CT_NonConst;
{ // single argument - value type
using V = std::variant<int>;
V v(42);
const V& cv = v;
std::visit<R>(obj, v);
assert(Fn::check_call<int&>(Val));
std::visit<R>(obj, cv);
assert(Fn::check_call<const int&>(Val));
std::visit<R>(obj, std::move(v));
assert(Fn::check_call<int&&>(Val));
std::visit<R>(obj, std::move(cv));
assert(Fn::check_call<const int&&>(Val));
}
}
struct ReturnFirst {
template <class F, class... Args>
constexpr F operator()(F f, Args&&...) const {
return f;
}
};
struct ReturnArity {
template <class... Args>
constexpr int operator()(Args&&...) const {
return sizeof...(Args);
}
};
struct simple_base {
int x;
constexpr explicit simple_base(int i) noexcept : x{i} {}
};
template <int>
struct simple_derived : simple_base {
using simple_base::simple_base;
};
constexpr bool test_constexpr() {
constexpr ReturnFirst obj{};
constexpr ReturnArity aobj{};
{
using V = std::variant<int>;
constexpr V v(42);
static_assert(std::visit<int>(obj, v) == 42);
}
{
using V = std::variant<short, long, char>;
constexpr V v(42l);
static_assert(std::visit<long>(obj, v) == 42);
}
{
using V1 = std::variant<int>;
using V2 = std::variant<int, char*, long long>;
using V3 = std::variant<bool, int, int>;
constexpr V1 v1;
constexpr V2 v2(nullptr);
constexpr V3 v3;
static_assert(std::visit<double>(aobj, v1, v2, v3) == 3.0);
}
{
using V1 = std::variant<int>;
using V2 = std::variant<int, char*, long long>;
using V3 = std::variant<void*, int, int>;
constexpr V1 v1;
constexpr V2 v2(nullptr);
constexpr V3 v3;
static_assert(std::visit<long long>(aobj, v1, v2, v3) == 3LL);
}
{
using V = std::variant<simple_derived<0>, simple_derived<1>, simple_derived<2>>;
V v{simple_derived<1>{42}};
auto&& b = std::visit<simple_base&>(std::identity{}, v);
ASSERT_SAME_TYPE(decltype(b), simple_base&);
assert(b.x == 42);
auto&& cb = std::visit<const simple_base&>(std::identity{}, std::as_const(v));
ASSERT_SAME_TYPE(decltype(cb), const simple_base&);
assert(cb.x == 42);
auto&& rb = std::visit<simple_base&&>(std::identity{}, std::move(v));
ASSERT_SAME_TYPE(decltype(rb), simple_base&&);
assert(rb.x == 42);
auto&& crb = std::visit<const simple_base&&>(std::identity{}, std::move(std::as_const(v)));
ASSERT_SAME_TYPE(decltype(crb), const simple_base&&);
assert(crb.x == 42);
}
return true;
}
void test_exceptions() {
#ifndef TEST_HAS_NO_EXCEPTIONS
ReturnArity obj{};
auto test = [&](auto&&... args) {
try {
std::visit<void>(obj, args...);
} catch (const std::bad_variant_access&) {
return true;
} catch (...) {
}
return false;
};
{
using V = std::variant<int, MakeEmptyT>;
V v;
makeEmpty(v);
assert(test(v));
}
{
using V = std::variant<int, MakeEmptyT>;
using V2 = std::variant<long, std::string, void*>;
V v;
makeEmpty(v);
V2 v2("hello");
assert(test(v, v2));
}
{
using V = std::variant<int, MakeEmptyT>;
using V2 = std::variant<long, std::string, void*>;
V v;
makeEmpty(v);
V2 v2("hello");
assert(test(v2, v));
}
{
using V = std::variant<int, MakeEmptyT>;
using V2 = std::variant<long, std::string, void*, MakeEmptyT>;
V v;
makeEmpty(v);
V2 v2;
makeEmpty(v2);
assert(test(v, v2));
}
#endif
}
// See LLVM-31916
void test_caller_accepts_nonconst() {
struct A {};
struct Visitor {
void operator()(A&) {}
};
std::variant<A> v;
std::visit<void>(Visitor{}, v);
}
struct mobile_data {
int x;
/* implicit */ mobile_data(int i) : x{i} {}
mobile_data(const mobile_data&) {
abort();
}
mobile_data(mobile_data&&) {
abort();
}
mobile_data& operator=(const mobile_data&) {
abort();
return *this;
}
mobile_data& operator=(mobile_data&&) {
abort();
return *this;
}
};
struct immobile_data : mobile_data {
using mobile_data::mobile_data;
immobile_data(const immobile_data&) = delete;
immobile_data& operator=(const immobile_data&) = delete;
};
void test_perfect_return() {
{
// Verify that a return object need not be copied/moved
using R = immobile_data;
assert(std::visit<R>(std::identity{}, std::variant<int, short>{13}).x == 13);
assert(std::visit<R>(std::identity{}, std::variant<int, short>{short{42}}).x == 42);
// Verify that conversions to an object that can't be copied/moved are correctly handled
struct convertible_to_immobile_one {
operator immobile_data() const {
return immobile_data{1729};
}
};
struct convertible_to_immobile_other {
operator immobile_data() const {
return immobile_data{1138};
}
};
using VarTestConv = std::variant<convertible_to_immobile_one, convertible_to_immobile_other>;
assert(std::visit<R>(std::identity{}, VarTestConv{convertible_to_immobile_one{}}).x == 1729);
assert(std::visit<R>(std::identity{}, VarTestConv{convertible_to_immobile_other{}}).x == 1138);
auto immobile_converter = [](auto src) -> immobile_data { return src; };
assert(std::visit<R>(immobile_converter, VarTestConv{convertible_to_immobile_one{}}).x == 1729);
assert(std::visit<R>(immobile_converter, VarTestConv{convertible_to_immobile_other{}}).x == 1138);
}
{
// Verify that a returned object is not copied/moved/modified
using R = mobile_data;
assert(std::visit<R>(std::identity{}, std::variant<int, short>{13}).x == 13);
assert(std::visit<R>(std::identity{}, std::variant<int, short>{short{42}}).x == 42);
// Verify that a returned reference is not copied/moved/modified
auto visitor1 = [x = R{1729}](auto) mutable -> R& { return x; };
assert(std::visit<R&>(visitor1, std::variant<int, short>{13}).x == 1729);
auto visitor2 = [x = R{1138}](auto) mutable -> R&& { return std::move(x); };
assert(std::visit<R&&>(visitor2, std::variant<int, short>{13}).x == 1138);
}
}
void test_immobile_function() {
{
// Validate that visit need not copy or move the visitor
using V = std::variant<int, double>;
immobile_visitor<std::identity> visitor{};
assert(std::visit<double>(visitor, V{42}) == 42.0);
assert(std::visit<double>(std::as_const(visitor), V{3.14}) == 3.14);
assert(std::visit<double>(std::move(visitor), V{1729}) == 1729.0);
assert(std::visit<double>(std::move(std::as_const(visitor)), V{1.414}) == 1.414);
}
{
// Validate that visit does not copy or move the visitor
using V = std::variant<int, double>;
mobile_visitor<std::identity> visitor{};
assert(std::visit<double>(visitor, V{42}) == 42.0);
assert(std::visit<double>(std::as_const(visitor), V{3.14}) == 3.14);
assert(std::visit<double>(std::move(visitor), V{1729}) == 1729.0);
assert(std::visit<double>(std::move(std::as_const(visitor)), V{1.414}) == 1.414);
}
}
void run_test() {
test_call_operator_forwarding<void>();
test_call_operator_forwarding<const void>();
test_call_operator_forwarding<long>();
test_argument_forwarding<void>();
test_argument_forwarding<const void>();
test_argument_forwarding<long long>();
test_constexpr();
static_assert(test_constexpr());
test_exceptions();
test_caller_accepts_nonconst();
test_perfect_return();
test_immobile_function();
}
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
void run_test() {}
#endif // _HAS_CXX20
} // namespace visit_R
namespace visit_pointer_to_member {
struct base {
int x;
int f() const {
return x;
}
};
struct derived : base {
int y;
};
void run_test() {
using V = std::variant<base, derived>;
assert(std::visit(&base::x, V{base{13}}) == 13);
assert(std::visit(&base::x, V{derived{{42}, 29}}) == 42);
assert(std::visit(&base::f, V{base{13}}) == 13);
assert(std::visit(&base::f, V{derived{{42}, 29}}) == 42);
}
} // namespace visit_pointer_to_member
template <class, class = void>
constexpr bool has_type = false;
template <class T>
constexpr bool has_type<T, std::void_t<typename T::type>> = true;
// Verify that `_Meta_at_<_Meta_list<>, size_t(-1)>` has no member named `type`, and that instantiating it doesn't
// consume the entire compiler heap.
static_assert(!has_type<std::_Meta_at_<std::_Meta_list<>, static_cast<std::size_t>(-1)>>);
namespace vso468746 {
// Defend against regression of VSO-468746
// "std::function's converting constructor/assignment should be unusable for performing copy/move assignments"
void run_test() {
struct S {
std::variant<std::function<S()>> member;
};
static_assert(
sizeof(std::variant<std::function<S()>>) == sizeof(std::function<S()>) + alignof(std::function<S()>));
static_assert(sizeof(S::member) == sizeof(std::variant<std::function<S()>>));
static_assert(sizeof(S) >= sizeof(S::member));
}
} // namespace vso468746
namespace vso492097 {
// Defend against regression of VSO-492097
// The compiler was not correctly emitting constant data for variants constructed from the address of static
// duration variables with a constexpr operator& due to mishandling initialization of nested classes with
// anonymous union members.
template <class T>
struct wrap {
T val_;
constexpr T* operator&() {
return &val_;
}
};
void run_test() {
static wrap<int> intVar = {42};
static wrap<double> doubleVar = {3.14};
using V = std::variant<int*, double*>;
static constexpr V v1(&intVar);
static constexpr V v2(&doubleVar);
static_assert(v1.index() == 0);
assert(*std::get<0>(v1) == 42);
static_assert(v2.index() == 1);
assert(*std::get<1>(v2) == 3.14);
}
} // namespace vso492097
namespace vso508126 {
void run_test() {
struct S {};
static_assert(!std::is_copy_constructible_v<volatile S>);
static_assert(!std::is_copy_constructible_v<std::variant<volatile S>>);
}
} // namespace vso508126
namespace DevCom1031281 {
// Compilers may warn when initializing a variant from a "weird" argument, e.g., std::variant<short>{some_int}
// is potentially narrowing. Compilers should not, however, emit such diagnostics from the metaprogramming that
// determines which alternative a variant initialization would activate. We don't want to emit warnings when
// determining implicit conversion sequences early in overload resolution.
void Overload(int) {}
void Overload(std::variant<unsigned short>) {}
void run_test() {
Overload(42);
}
} // namespace DevCom1031281
namespace gh2770 {
// Previous metaprogramming to validate the type requirements for std::visit required typelists too long for
// Clang.
struct S {
template <class T0, class T1, class T2, class T3, class T4>
int operator()(T0, T1, T2, T3, T4) const {
return 1729;
}
};
void run_test() {
using V = std::variant<char, int, long, long long>;
assert(std::visit(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729);
#if _HAS_CXX20
assert(std::visit<int>(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729);
#endif // _HAS_CXX20
}
} // namespace gh2770
namespace assign_cv {
template <class T>
struct TypeIdentityImpl {
using type = T;
};
template <class T>
using TypeIdentity = typename TypeIdentityImpl<T>::type;
struct CvAssignable {
CvAssignable() = default;
CvAssignable(const CvAssignable&) = default;
CvAssignable(CvAssignable&&) = default;
CvAssignable& operator=(const CvAssignable&) = default;
CvAssignable& operator=(CvAssignable&&) = default;
template <class T = CvAssignable>
CvAssignable(const volatile TypeIdentity<T>&) noexcept {}
template <class T = CvAssignable>
CvAssignable(const volatile TypeIdentity<T>&&) noexcept {}
template <class T = CvAssignable>
constexpr CvAssignable& operator=(const volatile TypeIdentity<T>&) noexcept {
return *this;
}
template <class T = CvAssignable>
constexpr CvAssignable& operator=(const volatile TypeIdentity<T>&&) noexcept {
return *this;
}
template <class T = CvAssignable>
constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity<T>&) const volatile noexcept {
return *this;
}
template <class T = CvAssignable>
constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity<T>&&) const volatile noexcept {
return *this;
}
};
void run_test() {
using std::swap;
{
std::variant<const int> oc{};
oc.emplace<0>(0);
static_assert(!std::is_copy_assignable_v<decltype(oc)>);
static_assert(!std::is_move_assignable_v<decltype(oc)>);
static_assert(!std::is_swappable_v<decltype(oc)>);
std::variant<volatile int> ov{};
std::variant<volatile int> ov2{};
ov.emplace<0>(0);
swap(ov, ov);
ov = ov2;
ov = std::move(ov2);
std::variant<const volatile int> ocv{};
ocv.emplace<0>(0);
static_assert(!std::is_copy_assignable_v<decltype(ocv)>);
static_assert(!std::is_move_assignable_v<decltype(ocv)>);
static_assert(!std::is_swappable_v<decltype(ocv)>);
}
{
std::variant<const CvAssignable> oc{};
std::variant<const CvAssignable> oc2{};
oc.emplace<0>(CvAssignable{});
swap(oc, oc);
oc = oc2;
oc = std::move(oc2);
std::variant<volatile CvAssignable> ov{};
std::variant<volatile CvAssignable> ov2{};
ov.emplace<0>(CvAssignable{});
swap(ov, ov);
ov = ov2;
ov = std::move(ov2);
std::variant<const volatile CvAssignable> ocv{};
std::variant<const volatile CvAssignable> ocv2{};
ocv.emplace<0>(CvAssignable{});
swap(ocv, ocv);
ocv = ocv2;
ocv = std::move(ocv2);
}
}
} // namespace assign_cv
} // namespace msvc
int main() {
msvc::big_variant::run_test();
msvc::derived_variant::run_test();
msvc::visit::run_test();
msvc::visit_R::run_test();
msvc::visit_pointer_to_member::run_test();
msvc::vso468746::run_test();
msvc::vso508126::run_test();
msvc::vso492097::run_test();
msvc::DevCom1031281::run_test();
msvc::gh2770::run_test();
msvc::assign_cv::run_test();
}

Просмотреть файл

@ -4,7 +4,7 @@
// Organization of this file:
// * a short header (including this comment)
// * `// LLVM SOURCES BEGIN`
// * The contents of several libc++ `test/std/utilities/any` test files, each delimited by `// -- BEGIN/END: <filename>`
// * The contents of several libc++ test files, each delimited by `// -- BEGIN/END: <filename>`
// comments. These contents have been modified to merge many tests into one by:
// (1) changing `int main(int, char**)` to `int run_test()`, and
// (2) wrapping everything other than comments and includes in a unique namespace per-file, using namespace nesting
@ -15,23 +15,17 @@
// the MSVC-specific test cases.
//
// The LLVM sources are updated manually:
// 1. Navigate a bash prompt to `libcxx` in an LLVM monorepo.
// 2. Redirect the output of the bash loop:
// for f in $(find test/std/utilities/any -name '*.pass.cpp');
// do echo "// -- BEGIN: $f";
// sed -e 's/int main(int, char\*\*)/int run_test()/; s/FIXME/TODO/g' < $f;
// echo -e "// -- END: $f\n";
// done
// 1. Navigate a bash prompt to `llvm-project/libcxx`.
// 2. Redirect the output of:
// ../../tools/scripts/transform_llvm.sh test/std/utilities/any
// into a file.
// 3. Replicate the namespace structure from here into that file, use its content to replace everything between the
// "LLVM SOURCES BEGIN"/"END" delimiters, and ensure that `main` properly calls each of the `run_test` functions.
// 4. Restore the TRANSITION-commented workarounds.
//
// Yes, this is an awkward hand process; notably the required headers can change without notice. We should investigate
// running the libc++ tests directly in all of our configurations so we needn't replicate this subset of files.
#define _HAS_DEPRECATED_IS_LITERAL_TYPE 1
#define _SILENCE_CXX17_IS_LITERAL_TYPE_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#define _LIBCXX_IN_DEVCRT
#include <msvc_stdlib_force_include.h> // Must precede any other libc++ headers
#include <stdlib.h>
@ -41,6 +35,9 @@
#include "count_new.h"
#pragma warning(pop)
// Silence a warning emitted by test/std/utilities/any/any.class/any.cons/default.pass.cpp below.
#pragma warning(disable : 4640) // construction of local static object is not thread-safe
// clang-format off
// LLVM SOURCES BEGIN
// -- BEGIN: test/std/utilities/any/any.class/any.assign/copy.pass.cpp
@ -53,7 +50,6 @@
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -255,7 +251,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -364,7 +359,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -577,7 +571,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -681,6 +674,7 @@ int run_test() {
//===----------------------------------------------------------------------===//
// <any>
// any() noexcept;
@ -702,15 +696,15 @@ int run_test()
, "Must be default constructible"
);
}
#ifndef _M_CEE // TRANSITION, VSO-1664382
{
struct TestConstexpr : public std::any {
constexpr TestConstexpr() : std::any() {}
};
#ifdef _LIBCPP_SAFE_STATIC
_LIBCPP_SAFE_STATIC static std::any a;
((void)a);
#endif
static TEST_CONSTINIT std::any a;
(void)a;
}
#endif // ^^^ no workaround ^^^
{
DisableAllocationGuard g; ((void)g);
const std::any a;
@ -732,7 +726,6 @@ int run_test()
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -755,7 +748,7 @@ int run_test()
#include "test_macros.h"
#include "test_convertible.h"
namespace ctor::in_place {
namespace ctor::in_place_type {
template <class Type>
void test_in_place_type() {
// constructing from a small type should perform no allocations.
@ -917,7 +910,7 @@ int run_test() {
return 0;
}
} // namespace ctor::in_place
} // namespace ctor::in_place_type
// -- END: test/std/utilities/any/any.class/any.cons/in_place_type.pass.cpp
// -- BEGIN: test/std/utilities/any/any.class/any.cons/move.pass.cpp
@ -930,7 +923,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -1034,7 +1026,6 @@ int run_test()
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -1192,7 +1183,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -1209,11 +1199,11 @@ int run_test() {
namespace modifiers::emplace {
struct Tracked {
static int count;
Tracked() {++count;}
Tracked(Tracked const&) noexcept {++count;}
Tracked& operator=(Tracked const&) = default;
~Tracked() { --count; }
static int count;
Tracked() { ++count; }
Tracked(Tracked const&) noexcept { ++count; }
Tracked& operator=(Tracked const&) = default;
~Tracked() { --count; }
};
int Tracked::count = 0;
@ -1482,7 +1472,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -1546,7 +1535,6 @@ int run_test()
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -1681,6 +1669,7 @@ int run_test()
//===----------------------------------------------------------------------===//
// <any>
// any::has_value() noexcept
@ -1694,7 +1683,6 @@ int run_test()
namespace observers::has_value {
int run_test()
{
// noexcept test
{
std::any a;
ASSERT_NOEXCEPT(a.has_value());
@ -1748,7 +1736,8 @@ int run_test()
//===----------------------------------------------------------------------===//
// XFAIL: libcpp-no-rtti
// <any>
@ -1787,34 +1776,6 @@ int run_test()
} // namespace observers::type
// -- END: test/std/utilities/any/any.class/any.observers/type.pass.cpp
// -- BEGIN: test/std/utilities/any/any.class/not_literal_type.pass.cpp
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// <any>
// [Note any is a not a literal type --end note]
#include <any>
#include <type_traits>
#include "test_macros.h"
namespace not_literal {
int run_test() {
static_assert(!std::is_literal_type<std::any>::value, "");
return 0;
}
} // namespace not_literal
// -- END: test/std/utilities/any/any.class/not_literal_type.pass.cpp
// -- BEGIN: test/std/utilities/any/any.nonmembers/any.cast/any_cast_pointer.pass.cpp
//===----------------------------------------------------------------------===//
//
@ -1825,7 +1786,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -2011,7 +1971,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -2323,7 +2282,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -2466,7 +2424,6 @@ int run_test() {
//===----------------------------------------------------------------------===//
// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions
// <any>
@ -3222,7 +3179,7 @@ int main() {
ctor::copy::run_test();
ctor::default_::run_test();
ctor::in_place::run_test();
ctor::in_place_type::run_test();
ctor::move::run_test();
ctor::value::run_test();
@ -3233,8 +3190,6 @@ int main() {
observers::has_value::run_test();
observers::type::run_test();
not_literal::run_test();
nonmembers::cast::pointer::run_test();
nonmembers::cast::reference::run_test();
nonmembers::make_any::run_test();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,4 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\usual_20_matrix.lst
RUNALL_INCLUDE ..\usual_17_matrix.lst

Просмотреть файл

@ -1,8 +1,6 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# When updating this file, also update tests\P0088R3_variant\env.lst to match
RUNALL_INCLUDE .\prefix.lst
RUNALL_CROSSLIST
* PM_CL="/w14640 /Zc:threadSafeInit-"

Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# This script is used to regenerate the P0220R1_any, P0220R1_optional, and P0088R3_variant tests.
SED_SCRIPTS='s/int main\(int, char\*\*\)/int run_test()/;'
SED_SCRIPTS+='s/FI[X]ME/TODO/g;'
SED_SCRIPTS+='s@// (REQUIRES|UNSUPPORTED|XFAIL):.*$@@;'
for f in $(find $* -name '*.pass.cpp' -not -name '*nothing_to_do*');
do
echo "// -- BEGIN: $f";
sed -E -e "$SED_SCRIPTS" < $f;
echo -e "// -- END: $f\n";
done