You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
8.7 KiB
C++
316 lines
8.7 KiB
C++
// -*- C++ -*-
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
|
|
|
// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions
|
|
|
|
// <variant>
|
|
// template <class Visitor, class... Variants>
|
|
// constexpr see below visit(Visitor&& vis, Variants&&... vars);
|
|
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include "test_macros.h"
|
|
#include "type_id.h"
|
|
#include "variant_test_helpers.hpp"
|
|
|
|
enum CallType : unsigned {
|
|
CT_None,
|
|
CT_NonConst = 1,
|
|
CT_Const = 2,
|
|
CT_LValue = 4,
|
|
CT_RValue = 8
|
|
};
|
|
|
|
inline constexpr CallType operator|(CallType LHS, CallType RHS) {
|
|
return static_cast<CallType>(static_cast<unsigned>(LHS) |
|
|
static_cast<unsigned>(RHS));
|
|
}
|
|
|
|
struct ForwardingCallObject {
|
|
|
|
template <class... Args> bool operator()(Args &&...) & {
|
|
set_call<Args &&...>(CT_NonConst | CT_LValue);
|
|
return true;
|
|
}
|
|
|
|
template <class... Args> bool operator()(Args &&...) const & {
|
|
set_call<Args &&...>(CT_Const | CT_LValue);
|
|
return true;
|
|
}
|
|
|
|
// Don't allow the call operator to be invoked as an rvalue.
|
|
template <class... Args> bool operator()(Args &&...) && {
|
|
set_call<Args &&...>(CT_NonConst | CT_RValue);
|
|
return true;
|
|
}
|
|
|
|
template <class... Args> bool operator()(Args &&...) const && {
|
|
set_call<Args &&...>(CT_Const | CT_RValue);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
|
|
void test_call_operator_forwarding() {
|
|
using Fn = ForwardingCallObject;
|
|
Fn obj{};
|
|
const Fn &cobj = obj;
|
|
{ // test call operator forwarding - no variant
|
|
std::visit(obj);
|
|
assert(Fn::check_call<>(CT_NonConst | CT_LValue));
|
|
std::visit(cobj);
|
|
assert(Fn::check_call<>(CT_Const | CT_LValue));
|
|
std::visit(std::move(obj));
|
|
assert(Fn::check_call<>(CT_NonConst | CT_RValue));
|
|
std::visit(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(obj, v);
|
|
assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
|
|
std::visit(cobj, v);
|
|
assert(Fn::check_call<int &>(CT_Const | CT_LValue));
|
|
std::visit(std::move(obj), v);
|
|
assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
|
|
std::visit(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(obj, v);
|
|
assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
|
|
std::visit(cobj, v);
|
|
assert(Fn::check_call<long &>(CT_Const | CT_LValue));
|
|
std::visit(std::move(obj), v);
|
|
assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
|
|
std::visit(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(obj, v, v2);
|
|
assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
|
|
std::visit(cobj, v, v2);
|
|
assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
|
|
std::visit(std::move(obj), v, v2);
|
|
assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
|
|
std::visit(std::move(cobj), v, v2);
|
|
assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
|
|
}
|
|
}
|
|
|
|
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(obj, v);
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, cv);
|
|
assert(Fn::check_call<const int &>(Val));
|
|
std::visit(obj, std::move(v));
|
|
assert(Fn::check_call<int &&>(Val));
|
|
std::visit(obj, std::move(cv));
|
|
assert(Fn::check_call<const int &&>(Val));
|
|
}
|
|
#if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
|
|
{ // single argument - lvalue reference
|
|
using V = std::variant<int &>;
|
|
int x = 42;
|
|
V v(x);
|
|
const V &cv = v;
|
|
std::visit(obj, v);
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, cv);
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, std::move(v));
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, std::move(cv));
|
|
assert(Fn::check_call<int &>(Val));
|
|
}
|
|
{ // single argument - rvalue reference
|
|
using V = std::variant<int &&>;
|
|
int x = 42;
|
|
V v(std::move(x));
|
|
const V &cv = v;
|
|
std::visit(obj, v);
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, cv);
|
|
assert(Fn::check_call<int &>(Val));
|
|
std::visit(obj, std::move(v));
|
|
assert(Fn::check_call<int &&>(Val));
|
|
std::visit(obj, std::move(cv));
|
|
assert(Fn::check_call<int &&>(Val));
|
|
}
|
|
{ // multi argument - multi variant
|
|
using S = const std::string &;
|
|
using V = std::variant<int, S, long &&>;
|
|
const std::string str = "hello";
|
|
long l = 43;
|
|
V v1(42);
|
|
const V &cv1 = v1;
|
|
V v2(str);
|
|
const V &cv2 = v2;
|
|
V v3(std::move(l));
|
|
const V &cv3 = v3;
|
|
std::visit(obj, v1, v2, v3);
|
|
assert((Fn::check_call<int &, S, long &>(Val)));
|
|
std::visit(obj, cv1, cv2, std::move(v3));
|
|
assert((Fn::check_call<const int &, S, long &&>(Val)));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct ReturnFirst {
|
|
template <class... Args> constexpr int operator()(int f, Args &&...) const {
|
|
return f;
|
|
}
|
|
};
|
|
|
|
struct ReturnArity {
|
|
template <class... Args> constexpr int operator()(Args &&...) const {
|
|
return sizeof...(Args);
|
|
}
|
|
};
|
|
|
|
void test_constexpr() {
|
|
constexpr ReturnFirst obj{};
|
|
constexpr ReturnArity aobj{};
|
|
{
|
|
using V = std::variant<int>;
|
|
constexpr V v(42);
|
|
static_assert(std::visit(obj, v) == 42, "");
|
|
}
|
|
{
|
|
using V = std::variant<short, long, char>;
|
|
constexpr V v(42l);
|
|
static_assert(std::visit(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(aobj, v1, v2, v3) == 3, "");
|
|
}
|
|
{
|
|
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(aobj, v1, v2, v3) == 3, "");
|
|
}
|
|
}
|
|
|
|
void test_exceptions() {
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
|
ReturnArity obj{};
|
|
auto test = [&](auto &&... args) {
|
|
try {
|
|
std::visit(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 https://bugs.llvm.org/show_bug.cgi?id=31916
|
|
void test_caller_accepts_nonconst() {
|
|
struct A {};
|
|
struct Visitor {
|
|
void operator()(A&) {}
|
|
};
|
|
std::variant<A> v;
|
|
std::visit(Visitor{}, v);
|
|
}
|
|
|
|
int main(int, char**) {
|
|
test_call_operator_forwarding();
|
|
test_argument_forwarding();
|
|
test_constexpr();
|
|
test_exceptions();
|
|
test_caller_accepts_nonconst();
|
|
|
|
return 0;
|
|
}
|