408 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++1410 11// <variant>12// template <class Visitor, class... Variants>13// constexpr see below visit(Visitor&& vis, Variants&&... vars);14 15#include <cassert>16#include <memory>17#include <string>18#include <tuple>19#include <type_traits>20#include <utility>21#include <variant>22 23#include "test_macros.h"24#include "variant_test_helpers.h"25 26void test_call_operator_forwarding() {27 using Fn = ForwardingCallObject;28 Fn obj{};29 const Fn &cobj = obj;30 { // test call operator forwarding - no variant31 std::visit(obj);32 assert(Fn::check_call<>(CT_NonConst | CT_LValue));33 std::visit(cobj);34 assert(Fn::check_call<>(CT_Const | CT_LValue));35 std::visit(std::move(obj));36 assert(Fn::check_call<>(CT_NonConst | CT_RValue));37 std::visit(std::move(cobj));38 assert(Fn::check_call<>(CT_Const | CT_RValue));39 }40 { // test call operator forwarding - single variant, single arg41 using V = std::variant<int>;42 V v(42);43 std::visit(obj, v);44 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));45 std::visit(cobj, v);46 assert(Fn::check_call<int &>(CT_Const | CT_LValue));47 std::visit(std::move(obj), v);48 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));49 std::visit(std::move(cobj), v);50 assert(Fn::check_call<int &>(CT_Const | CT_RValue));51 }52 { // test call operator forwarding - single variant, multi arg53 using V = std::variant<int, long, double>;54 V v(42l);55 std::visit(obj, v);56 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));57 std::visit(cobj, v);58 assert(Fn::check_call<long &>(CT_Const | CT_LValue));59 std::visit(std::move(obj), v);60 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));61 std::visit(std::move(cobj), v);62 assert(Fn::check_call<long &>(CT_Const | CT_RValue));63 }64 { // test call operator forwarding - multi variant, multi arg65 using V = std::variant<int, long, double>;66 using V2 = std::variant<int *, std::string>;67 V v(42l);68 V2 v2("hello");69 std::visit(obj, v, v2);70 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));71 std::visit(cobj, v, v2);72 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));73 std::visit(std::move(obj), v, v2);74 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));75 std::visit(std::move(cobj), v, v2);76 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));77 }78 {79 using V = std::variant<int, long, double, std::string>;80 V v1(42l), v2("hello"), v3(101), v4(1.1);81 std::visit(obj, v1, v2, v3, v4);82 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue)));83 std::visit(cobj, v1, v2, v3, v4);84 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue)));85 std::visit(std::move(obj), v1, v2, v3, v4);86 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue)));87 std::visit(std::move(cobj), v1, v2, v3, v4);88 assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue)));89 }90 {91 using V = std::variant<int, long, double, int*, std::string>;92 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);93 std::visit(obj, v1, v2, v3, v4);94 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue)));95 std::visit(cobj, v1, v2, v3, v4);96 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue)));97 std::visit(std::move(obj), v1, v2, v3, v4);98 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue)));99 std::visit(std::move(cobj), v1, v2, v3, v4);100 assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue)));101 }102}103 104void test_argument_forwarding() {105 using Fn = ForwardingCallObject;106 Fn obj{};107 const auto Val = CT_LValue | CT_NonConst;108 { // single argument - value type109 using V = std::variant<int>;110 V v(42);111 const V &cv = v;112 std::visit(obj, v);113 assert(Fn::check_call<int &>(Val));114 std::visit(obj, cv);115 assert(Fn::check_call<const int &>(Val));116 std::visit(obj, std::move(v));117 assert(Fn::check_call<int &&>(Val));118 std::visit(obj, std::move(cv));119 assert(Fn::check_call<const int &&>(Val));120 }121 { // multi argument - multi variant122 using V = std::variant<int, std::string, long>;123 V v1(42), v2("hello"), v3(43l);124 std::visit(obj, v1, v2, v3);125 assert((Fn::check_call<int &, std::string &, long &>(Val)));126 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3));127 assert((Fn::check_call<const int &, const std::string &, long &&>(Val)));128 }129 {130 using V = std::variant<int, long, double, std::string>;131 V v1(42l), v2("hello"), v3(101), v4(1.1);132 std::visit(obj, v1, v2, v3, v4);133 assert((Fn::check_call<long &, std::string &, int &, double &>(Val)));134 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));135 assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val)));136 }137 {138 using V = std::variant<int, long, double, int*, std::string>;139 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);140 std::visit(obj, v1, v2, v3, v4);141 assert((Fn::check_call<long &, std::string &, int *&, double &>(Val)));142 std::visit(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));143 assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val)));144 }145}146 147void test_return_type() {148 using Fn = ForwardingCallObject;149 Fn obj{};150 const Fn &cobj = obj;151 { // test call operator forwarding - no variant152 static_assert(std::is_same_v<decltype(std::visit(obj)), Fn&>);153 static_assert(std::is_same_v<decltype(std::visit(cobj)), const Fn&>);154 static_assert(std::is_same_v<decltype(std::visit(std::move(obj))), Fn&&>);155 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj))), const Fn&&>);156 }157 { // test call operator forwarding - single variant, single arg158 using V = std::variant<int>;159 V v(42);160 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);161 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);162 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);163 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);164 }165 { // test call operator forwarding - single variant, multi arg166 using V = std::variant<int, long, double>;167 V v(42l);168 static_assert(std::is_same_v<decltype(std::visit(obj, v)), Fn&>);169 static_assert(std::is_same_v<decltype(std::visit(cobj, v)), const Fn&>);170 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v)), Fn&&>);171 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v)), const Fn&&>);172 }173 { // test call operator forwarding - multi variant, multi arg174 using V = std::variant<int, long, double>;175 using V2 = std::variant<int *, std::string>;176 V v(42l);177 V2 v2("hello");178 static_assert(std::is_same_v<decltype(std::visit(obj, v, v2)), Fn&>);179 static_assert(std::is_same_v<decltype(std::visit(cobj, v, v2)), const Fn&>);180 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v, v2)), Fn&&>);181 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v, v2)), const Fn&&>);182 }183 {184 using V = std::variant<int, long, double, std::string>;185 V v1(42l), v2("hello"), v3(101), v4(1.1);186 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);187 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);188 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);189 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);190 }191 {192 using V = std::variant<int, long, double, int*, std::string>;193 V v1(42l), v2("hello"), v3(nullptr), v4(1.1);194 static_assert(std::is_same_v<decltype(std::visit(obj, v1, v2, v3, v4)), Fn&>);195 static_assert(std::is_same_v<decltype(std::visit(cobj, v1, v2, v3, v4)), const Fn&>);196 static_assert(std::is_same_v<decltype(std::visit(std::move(obj), v1, v2, v3, v4)), Fn&&>);197 static_assert(std::is_same_v<decltype(std::visit(std::move(cobj), v1, v2, v3, v4)), const Fn&&>);198 }199}200 201void test_constexpr() {202 constexpr ReturnFirst obj{};203 constexpr ReturnArity aobj{};204 {205 using V = std::variant<int>;206 constexpr V v(42);207 static_assert(std::visit(obj, v) == 42, "");208 }209 {210 using V = std::variant<short, long, char>;211 constexpr V v(42l);212 static_assert(std::visit(obj, v) == 42, "");213 }214 {215 using V1 = std::variant<int>;216 using V2 = std::variant<int, char *, long long>;217 using V3 = std::variant<bool, int, int>;218 constexpr V1 v1;219 constexpr V2 v2(nullptr);220 constexpr V3 v3;221 static_assert(std::visit(aobj, v1, v2, v3) == 3, "");222 }223 {224 using V1 = std::variant<int>;225 using V2 = std::variant<int, char *, long long>;226 using V3 = std::variant<void *, int, int>;227 constexpr V1 v1;228 constexpr V2 v2(nullptr);229 constexpr V3 v3;230 static_assert(std::visit(aobj, v1, v2, v3) == 3, "");231 }232 {233 using V = std::variant<int, long, double, int *>;234 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);235 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");236 }237 {238 using V = std::variant<int, long, double, long long, int *>;239 constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);240 static_assert(std::visit(aobj, v1, v2, v3, v4) == 4, "");241 }242}243 244void test_exceptions() {245#ifndef TEST_HAS_NO_EXCEPTIONS246 ReturnArity obj{};247 auto test = [&](auto &&... args) {248 try {249 std::visit(obj, args...);250 } catch (const std::bad_variant_access &) {251 return true;252 } catch (...) {253 }254 return false;255 };256 {257 using V = std::variant<int, MakeEmptyT>;258 V v;259 makeEmpty(v);260 assert(test(v));261 }262 {263 using V = std::variant<int, MakeEmptyT>;264 using V2 = std::variant<long, std::string, void *>;265 V v;266 makeEmpty(v);267 V2 v2("hello");268 assert(test(v, v2));269 }270 {271 using V = std::variant<int, MakeEmptyT>;272 using V2 = std::variant<long, std::string, void *>;273 V v;274 makeEmpty(v);275 V2 v2("hello");276 assert(test(v2, v));277 }278 {279 using V = std::variant<int, MakeEmptyT>;280 using V2 = std::variant<long, std::string, void *, MakeEmptyT>;281 V v;282 makeEmpty(v);283 V2 v2;284 makeEmpty(v2);285 assert(test(v, v2));286 }287 {288 using V = std::variant<int, long, double, MakeEmptyT>;289 V v1(42l), v2(101), v3(202), v4(1.1);290 makeEmpty(v1);291 assert(test(v1, v2, v3, v4));292 }293 {294 using V = std::variant<int, long, double, long long, MakeEmptyT>;295 V v1(42l), v2(101), v3(202), v4(1.1);296 makeEmpty(v1);297 makeEmpty(v2);298 makeEmpty(v3);299 makeEmpty(v4);300 assert(test(v1, v2, v3, v4));301 }302#endif303}304 305// See https://llvm.org/PR31916306void test_caller_accepts_nonconst() {307 struct A {};308 struct Visitor {309 void operator()(A&) {}310 };311 std::variant<A> v;312 std::visit(Visitor{}, v);313}314 315struct MyVariant : std::variant<short, long, float> {};316 317// FIXME: This is UB according to [namespace.std]318namespace std {319template <std::size_t Index>320void get(const MyVariant&) {321 assert(false);322}323} // namespace std324 325void test_derived_from_variant() {326 auto v1 = MyVariant{42};327 const auto cv1 = MyVariant{142};328 std::visit([](auto x) { assert(x == 42); }, v1);329 std::visit([](auto x) { assert(x == 142); }, cv1);330 std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f});331 std::visit([](auto x) { assert(x == 42); }, std::move(v1));332 std::visit([](auto x) { assert(x == 142); }, std::move(cv1));333 334 // Check that visit does not take index nor valueless_by_exception members from the base class.335 struct EvilVariantBase {336 int index;337 char valueless_by_exception;338 };339 340 struct EvilVariant1 : std::variant<int, long, double>,341 std::tuple<int>,342 EvilVariantBase {343 using std::variant<int, long, double>::variant;344 };345 346 std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12});347 std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3});348 349 // Check that visit unambiguously picks the variant, even if the other base has __impl member.350 struct ImplVariantBase {351 struct Callable {352 bool operator()() const { assert(false); return false; }353 };354 355 Callable __impl;356 };357 358 struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {359 using std::variant<int, long, double>::variant;360 };361 362 std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12});363 std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3});364}365 366struct any_visitor {367 template <typename T>368 void operator()(const T&) const {}369};370 371template <typename T, typename = decltype(std::visit(372 std::declval<any_visitor&>(), std::declval<T>()))>373constexpr bool has_visit(int) {374 return true;375}376 377template <typename T>378constexpr bool has_visit(...) {379 return false;380}381 382void test_sfinae() {383 struct BadVariant : std::variant<short>, std::variant<long, float> {};384 struct BadVariant2 : private std::variant<long, float> {};385 struct GoodVariant : std::variant<long, float> {};386 struct GoodVariant2 : GoodVariant {};387 388 static_assert(!has_visit<int>(0));389 static_assert(!has_visit<BadVariant>(0));390 static_assert(!has_visit<BadVariant2>(0));391 static_assert(has_visit<std::variant<int>>(0));392 static_assert(has_visit<GoodVariant>(0));393 static_assert(has_visit<GoodVariant2>(0));394}395 396int main(int, char**) {397 test_call_operator_forwarding();398 test_argument_forwarding();399 test_return_type();400 test_constexpr();401 test_exceptions();402 test_caller_accepts_nonconst();403 test_derived_from_variant();404 test_sfinae();405 406 return 0;407}408