225 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++2310 11// <variant>12 13// class variant;14 15// template<class Self, class Visitor>16// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++2617 18#include <cassert>19#include <memory>20#include <string>21#include <tuple>22#include <type_traits>23#include <utility>24#include <variant>25 26#include "test_macros.h"27#include "variant_test_helpers.h"28 29void test_call_operator_forwarding() {30 using Fn = ForwardingCallObject;31 Fn obj{};32 const Fn& cobj = obj;33 34 { // test call operator forwarding - single variant, single arg35 using V = std::variant<int>;36 V v(42);37 38 v.visit(obj);39 assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));40 v.visit(cobj);41 assert(Fn::check_call<int&>(CT_Const | CT_LValue));42 v.visit(std::move(obj));43 assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));44 v.visit(std::move(cobj));45 assert(Fn::check_call<int&>(CT_Const | CT_RValue));46 }47 { // test call operator forwarding - single variant, multi arg48 using V = std::variant<int, long, double>;49 V v(42L);50 51 v.visit(obj);52 assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));53 v.visit(cobj);54 assert(Fn::check_call<long&>(CT_Const | CT_LValue));55 v.visit(std::move(obj));56 assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));57 v.visit(std::move(cobj));58 assert(Fn::check_call<long&>(CT_Const | CT_RValue));59 }60}61 62// Applies to non-member `std::visit` only.63void test_argument_forwarding() {64 using Fn = ForwardingCallObject;65 Fn obj{};66 const auto val = CT_LValue | CT_NonConst;67 68 { // single argument - value type69 using V = std::variant<int>;70 V v(42);71 const V& cv = v;72 73 v.visit(obj);74 assert(Fn::check_call<int&>(val));75 cv.visit(obj);76 assert(Fn::check_call<const int&>(val));77 std::move(v).visit(obj);78 assert(Fn::check_call<int&&>(val));79 std::move(cv).visit(obj);80 assert(Fn::check_call<const int&&>(val));81 }82}83 84void test_return_type() {85 using Fn = ForwardingCallObject;86 Fn obj{};87 const Fn& cobj = obj;88 89 { // test call operator forwarding - single variant, single arg90 using V = std::variant<int>;91 V v(42);92 93 static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);94 static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);95 static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);96 static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);97 }98 { // test call operator forwarding - single variant, multi arg99 using V = std::variant<int, long, double>;100 V v(42L);101 102 static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);103 static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);104 static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);105 static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);106 }107}108 109void test_constexpr() {110 constexpr ReturnFirst obj{};111 112 {113 using V = std::variant<int>;114 constexpr V v(42);115 116 static_assert(v.visit(obj) == 42);117 }118 {119 using V = std::variant<short, long, char>;120 constexpr V v(42L);121 122 static_assert(v.visit(obj) == 42);123 }124}125 126void test_exceptions() {127#ifndef TEST_HAS_NO_EXCEPTIONS128 ReturnArity obj{};129 130 auto test = [&](auto&& v) {131 try {132 v.visit(obj);133 } catch (const std::bad_variant_access&) {134 return true;135 } catch (...) {136 }137 return false;138 };139 140 {141 using V = std::variant<int, MakeEmptyT>;142 V v;143 makeEmpty(v);144 145 assert(test(v));146 }147#endif148}149 150// See https://llvm.org/PR31916151void test_caller_accepts_nonconst() {152 struct A {};153 struct Visitor {154 void operator()(A&) {}155 };156 std::variant<A> v;157 158 v.visit(Visitor{});159}160 161struct MyVariant : std::variant<short, long, float> {};162 163// FIXME: This is UB according to [namespace.std]164namespace std {165template <std::size_t Index>166void get(const MyVariant&) {167 assert(false);168}169} // namespace std170 171void test_derived_from_variant() {172 auto v1 = MyVariant{42};173 const auto cv1 = MyVariant{142};174 175 v1.visit([](auto x) { assert(x == 42); });176 cv1.visit([](auto x) { assert(x == 142); });177 MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); });178 std::move(v1).visit([](auto x) { assert(x == 42); });179 std::move(cv1).visit([](auto x) { assert(x == 142); });180 181 // Check that visit does not take index nor valueless_by_exception members from the base class.182 struct EvilVariantBase {183 int index;184 char valueless_by_exception;185 };186 187 struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {188 using std::variant<int, long, double>::variant;189 };190 191 EvilVariant1{12}.visit([](auto x) { assert(x == 12); });192 EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); });193 194 // Check that visit unambiguously picks the variant, even if the other base has __impl member.195 struct ImplVariantBase {196 struct Callable {197 bool operator()() const {198 assert(false);199 return false;200 }201 };202 203 Callable __impl;204 };205 206 struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {207 using std::variant<int, long, double>::variant;208 };209 210 EvilVariant2{12}.visit([](auto x) { assert(x == 12); });211 EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); });212}213 214int main(int, char**) {215 test_call_operator_forwarding();216 test_argument_forwarding();217 test_return_type();218 test_constexpr();219 test_exceptions();220 test_caller_accepts_nonconst();221 test_derived_from_variant();222 223 return 0;224}225