xref: /llvm-project/libcxx/test/std/utilities/variant/variant.visit.member/visit.pass.cpp (revision 7f845cba2ccc2ab637b8e40fbafb9f83a2d67c70)
13412bc76SHristo Hristov //===----------------------------------------------------------------------===//
23412bc76SHristo Hristov //
33412bc76SHristo Hristov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43412bc76SHristo Hristov // See https://llvm.org/LICENSE.txt for license information.
53412bc76SHristo Hristov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63412bc76SHristo Hristov //
73412bc76SHristo Hristov //===----------------------------------------------------------------------===//
83412bc76SHristo Hristov 
93412bc76SHristo Hristov // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
103412bc76SHristo Hristov // The tested functionality needs deducing this.
113412bc76SHristo Hristov // XFAIL: apple-clang
123412bc76SHristo Hristov 
133412bc76SHristo Hristov // <variant>
143412bc76SHristo Hristov 
153412bc76SHristo Hristov // class variant;
163412bc76SHristo Hristov 
173412bc76SHristo Hristov // template<class Self, class Visitor>
183412bc76SHristo Hristov //   constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26
193412bc76SHristo Hristov 
203412bc76SHristo Hristov #include <cassert>
213412bc76SHristo Hristov #include <memory>
223412bc76SHristo Hristov #include <string>
232a385514SNikolas Klauser #include <tuple>
243412bc76SHristo Hristov #include <type_traits>
253412bc76SHristo Hristov #include <utility>
263412bc76SHristo Hristov #include <variant>
273412bc76SHristo Hristov 
283412bc76SHristo Hristov #include "test_macros.h"
293412bc76SHristo Hristov #include "variant_test_helpers.h"
303412bc76SHristo Hristov 
313412bc76SHristo Hristov void test_call_operator_forwarding() {
323412bc76SHristo Hristov   using Fn = ForwardingCallObject;
333412bc76SHristo Hristov   Fn obj{};
343412bc76SHristo Hristov   const Fn& cobj = obj;
353412bc76SHristo Hristov 
363412bc76SHristo Hristov   { // test call operator forwarding - single variant, single arg
373412bc76SHristo Hristov     using V = std::variant<int>;
383412bc76SHristo Hristov     V v(42);
393412bc76SHristo Hristov 
403412bc76SHristo Hristov     v.visit(obj);
413412bc76SHristo Hristov     assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
423412bc76SHristo Hristov     v.visit(cobj);
433412bc76SHristo Hristov     assert(Fn::check_call<int&>(CT_Const | CT_LValue));
443412bc76SHristo Hristov     v.visit(std::move(obj));
453412bc76SHristo Hristov     assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
463412bc76SHristo Hristov     v.visit(std::move(cobj));
473412bc76SHristo Hristov     assert(Fn::check_call<int&>(CT_Const | CT_RValue));
483412bc76SHristo Hristov   }
493412bc76SHristo Hristov   { // test call operator forwarding - single variant, multi arg
503412bc76SHristo Hristov     using V = std::variant<int, long, double>;
513412bc76SHristo Hristov     V v(42L);
523412bc76SHristo Hristov 
533412bc76SHristo Hristov     v.visit(obj);
543412bc76SHristo Hristov     assert(Fn::check_call<long&>(CT_NonConst | CT_LValue));
553412bc76SHristo Hristov     v.visit(cobj);
563412bc76SHristo Hristov     assert(Fn::check_call<long&>(CT_Const | CT_LValue));
573412bc76SHristo Hristov     v.visit(std::move(obj));
583412bc76SHristo Hristov     assert(Fn::check_call<long&>(CT_NonConst | CT_RValue));
593412bc76SHristo Hristov     v.visit(std::move(cobj));
603412bc76SHristo Hristov     assert(Fn::check_call<long&>(CT_Const | CT_RValue));
613412bc76SHristo Hristov   }
623412bc76SHristo Hristov }
633412bc76SHristo Hristov 
643412bc76SHristo Hristov // Applies to non-member `std::visit` only.
653412bc76SHristo Hristov void test_argument_forwarding() {
663412bc76SHristo Hristov   using Fn = ForwardingCallObject;
673412bc76SHristo Hristov   Fn obj{};
683412bc76SHristo Hristov   const auto val = CT_LValue | CT_NonConst;
693412bc76SHristo Hristov 
703412bc76SHristo Hristov   { // single argument - value type
713412bc76SHristo Hristov     using V = std::variant<int>;
723412bc76SHristo Hristov     V v(42);
733412bc76SHristo Hristov     const V& cv = v;
743412bc76SHristo Hristov 
753412bc76SHristo Hristov     v.visit(obj);
763412bc76SHristo Hristov     assert(Fn::check_call<int&>(val));
773412bc76SHristo Hristov     cv.visit(obj);
783412bc76SHristo Hristov     assert(Fn::check_call<const int&>(val));
793412bc76SHristo Hristov     std::move(v).visit(obj);
803412bc76SHristo Hristov     assert(Fn::check_call<int&&>(val));
813412bc76SHristo Hristov     std::move(cv).visit(obj);
823412bc76SHristo Hristov     assert(Fn::check_call<const int&&>(val));
833412bc76SHristo Hristov   }
843412bc76SHristo Hristov }
853412bc76SHristo Hristov 
863412bc76SHristo Hristov void test_return_type() {
873412bc76SHristo Hristov   using Fn = ForwardingCallObject;
883412bc76SHristo Hristov   Fn obj{};
893412bc76SHristo Hristov   const Fn& cobj = obj;
903412bc76SHristo Hristov 
913412bc76SHristo Hristov   { // test call operator forwarding - single variant, single arg
923412bc76SHristo Hristov     using V = std::variant<int>;
933412bc76SHristo Hristov     V v(42);
943412bc76SHristo Hristov 
953412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
963412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
973412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
983412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
993412bc76SHristo Hristov   }
1003412bc76SHristo Hristov   { // test call operator forwarding - single variant, multi arg
1013412bc76SHristo Hristov     using V = std::variant<int, long, double>;
1023412bc76SHristo Hristov     V v(42L);
1033412bc76SHristo Hristov 
1043412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>);
1053412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>);
1063412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>);
1073412bc76SHristo Hristov     static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>);
1083412bc76SHristo Hristov   }
1093412bc76SHristo Hristov }
1103412bc76SHristo Hristov 
1113412bc76SHristo Hristov void test_constexpr() {
1123412bc76SHristo Hristov   constexpr ReturnFirst obj{};
1133412bc76SHristo Hristov 
1143412bc76SHristo Hristov   {
1153412bc76SHristo Hristov     using V = std::variant<int>;
1163412bc76SHristo Hristov     constexpr V v(42);
1173412bc76SHristo Hristov 
1183412bc76SHristo Hristov     static_assert(v.visit(obj) == 42);
1193412bc76SHristo Hristov   }
1203412bc76SHristo Hristov   {
1213412bc76SHristo Hristov     using V = std::variant<short, long, char>;
1223412bc76SHristo Hristov     constexpr V v(42L);
1233412bc76SHristo Hristov 
1243412bc76SHristo Hristov     static_assert(v.visit(obj) == 42);
1253412bc76SHristo Hristov   }
1263412bc76SHristo Hristov }
1273412bc76SHristo Hristov 
1283412bc76SHristo Hristov void test_exceptions() {
1293412bc76SHristo Hristov #ifndef TEST_HAS_NO_EXCEPTIONS
1303412bc76SHristo Hristov   ReturnArity obj{};
1313412bc76SHristo Hristov 
1323412bc76SHristo Hristov   auto test = [&](auto&& v) {
1333412bc76SHristo Hristov     try {
1343412bc76SHristo Hristov       v.visit(obj);
1353412bc76SHristo Hristov     } catch (const std::bad_variant_access&) {
1363412bc76SHristo Hristov       return true;
1373412bc76SHristo Hristov     } catch (...) {
1383412bc76SHristo Hristov     }
1393412bc76SHristo Hristov     return false;
1403412bc76SHristo Hristov   };
1413412bc76SHristo Hristov 
1423412bc76SHristo Hristov   {
1433412bc76SHristo Hristov     using V = std::variant<int, MakeEmptyT>;
1443412bc76SHristo Hristov     V v;
1453412bc76SHristo Hristov     makeEmpty(v);
1463412bc76SHristo Hristov 
1473412bc76SHristo Hristov     assert(test(v));
1483412bc76SHristo Hristov   }
1493412bc76SHristo Hristov #endif
1503412bc76SHristo Hristov }
1513412bc76SHristo Hristov 
1523412bc76SHristo Hristov // See https://llvm.org/PR31916
1533412bc76SHristo Hristov void test_caller_accepts_nonconst() {
1543412bc76SHristo Hristov   struct A {};
1553412bc76SHristo Hristov   struct Visitor {
1563412bc76SHristo Hristov     void operator()(A&) {}
1573412bc76SHristo Hristov   };
1583412bc76SHristo Hristov   std::variant<A> v;
1593412bc76SHristo Hristov 
1603412bc76SHristo Hristov   v.visit(Visitor{});
1613412bc76SHristo Hristov }
1623412bc76SHristo Hristov 
1633412bc76SHristo Hristov struct MyVariant : std::variant<short, long, float> {};
1643412bc76SHristo Hristov 
165*5dfdac74SNikolas Klauser // FIXME: This is UB according to [namespace.std]
1663412bc76SHristo Hristov namespace std {
1673412bc76SHristo Hristov template <std::size_t Index>
1683412bc76SHristo Hristov void get(const MyVariant&) {
1693412bc76SHristo Hristov   assert(false);
1703412bc76SHristo Hristov }
1713412bc76SHristo Hristov } // namespace std
1723412bc76SHristo Hristov 
1733412bc76SHristo Hristov void test_derived_from_variant() {
1743412bc76SHristo Hristov   auto v1        = MyVariant{42};
1753412bc76SHristo Hristov   const auto cv1 = MyVariant{142};
1763412bc76SHristo Hristov 
1773412bc76SHristo Hristov   v1.visit([](auto x) { assert(x == 42); });
1783412bc76SHristo Hristov   cv1.visit([](auto x) { assert(x == 142); });
1793412bc76SHristo Hristov   MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); });
1803412bc76SHristo Hristov   std::move(v1).visit([](auto x) { assert(x == 42); });
1813412bc76SHristo Hristov   std::move(cv1).visit([](auto x) { assert(x == 142); });
1823412bc76SHristo Hristov 
1833412bc76SHristo Hristov   // Check that visit does not take index nor valueless_by_exception members from the base class.
1843412bc76SHristo Hristov   struct EvilVariantBase {
1853412bc76SHristo Hristov     int index;
1863412bc76SHristo Hristov     char valueless_by_exception;
1873412bc76SHristo Hristov   };
1883412bc76SHristo Hristov 
1893412bc76SHristo Hristov   struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase {
1903412bc76SHristo Hristov     using std::variant<int, long, double>::variant;
1913412bc76SHristo Hristov   };
1923412bc76SHristo Hristov 
1933412bc76SHristo Hristov   EvilVariant1{12}.visit([](auto x) { assert(x == 12); });
1943412bc76SHristo Hristov   EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); });
1953412bc76SHristo Hristov 
1963412bc76SHristo Hristov   // Check that visit unambiguously picks the variant, even if the other base has __impl member.
1973412bc76SHristo Hristov   struct ImplVariantBase {
1983412bc76SHristo Hristov     struct Callable {
1993412bc76SHristo Hristov       bool operator()() const {
2003412bc76SHristo Hristov         assert(false);
2013412bc76SHristo Hristov         return false;
2023412bc76SHristo Hristov       }
2033412bc76SHristo Hristov     };
2043412bc76SHristo Hristov 
2053412bc76SHristo Hristov     Callable __impl;
2063412bc76SHristo Hristov   };
2073412bc76SHristo Hristov 
2083412bc76SHristo Hristov   struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
2093412bc76SHristo Hristov     using std::variant<int, long, double>::variant;
2103412bc76SHristo Hristov   };
2113412bc76SHristo Hristov 
2123412bc76SHristo Hristov   EvilVariant2{12}.visit([](auto x) { assert(x == 12); });
2133412bc76SHristo Hristov   EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); });
2143412bc76SHristo Hristov }
2153412bc76SHristo Hristov 
2163412bc76SHristo Hristov int main(int, char**) {
2173412bc76SHristo Hristov   test_call_operator_forwarding();
2183412bc76SHristo Hristov   test_argument_forwarding();
2193412bc76SHristo Hristov   test_return_type();
2203412bc76SHristo Hristov   test_constexpr();
2213412bc76SHristo Hristov   test_exceptions();
2223412bc76SHristo Hristov   test_caller_accepts_nonconst();
2233412bc76SHristo Hristov   test_derived_from_variant();
2243412bc76SHristo Hristov 
2253412bc76SHristo Hristov   return 0;
2263412bc76SHristo Hristov }
227