Home | History | Annotate | Download | only in variant.visit
      1 // -*- C++ -*-
      2 //===----------------------------------------------------------------------===//
      3 //
      4 //                     The LLVM Compiler Infrastructure
      5 //
      6 // This file is dual licensed under the MIT and the University of Illinois Open
      7 // Source Licenses. See LICENSE.TXT for details.
      8 //
      9 //===----------------------------------------------------------------------===//
     10 
     11 // UNSUPPORTED: c++98, c++03, c++11, c++14
     12 
     13 // XFAIL: with_system_cxx_lib=macosx10.12
     14 // XFAIL: with_system_cxx_lib=macosx10.11
     15 // XFAIL: with_system_cxx_lib=macosx10.10
     16 // XFAIL: with_system_cxx_lib=macosx10.9
     17 // XFAIL: with_system_cxx_lib=macosx10.7
     18 // XFAIL: with_system_cxx_lib=macosx10.8
     19 
     20 // <variant>
     21 // template <class Visitor, class... Variants>
     22 // constexpr see below visit(Visitor&& vis, Variants&&... vars);
     23 
     24 #include <cassert>
     25 #include <memory>
     26 #include <string>
     27 #include <type_traits>
     28 #include <utility>
     29 #include <variant>
     30 
     31 #include "test_macros.h"
     32 #include "type_id.h"
     33 #include "variant_test_helpers.hpp"
     34 
     35 enum CallType : unsigned {
     36   CT_None,
     37   CT_NonConst = 1,
     38   CT_Const = 2,
     39   CT_LValue = 4,
     40   CT_RValue = 8
     41 };
     42 
     43 inline constexpr CallType operator|(CallType LHS, CallType RHS) {
     44   return static_cast<CallType>(static_cast<unsigned>(LHS) |
     45                                static_cast<unsigned>(RHS));
     46 }
     47 
     48 struct ForwardingCallObject {
     49 
     50   template <class... Args> bool operator()(Args &&...) & {
     51     set_call<Args &&...>(CT_NonConst | CT_LValue);
     52     return true;
     53   }
     54 
     55   template <class... Args> bool operator()(Args &&...) const & {
     56     set_call<Args &&...>(CT_Const | CT_LValue);
     57     return true;
     58   }
     59 
     60   // Don't allow the call operator to be invoked as an rvalue.
     61   template <class... Args> bool operator()(Args &&...) && {
     62     set_call<Args &&...>(CT_NonConst | CT_RValue);
     63     return true;
     64   }
     65 
     66   template <class... Args> bool operator()(Args &&...) const && {
     67     set_call<Args &&...>(CT_Const | CT_RValue);
     68     return true;
     69   }
     70 
     71   template <class... Args> static void set_call(CallType type) {
     72     assert(last_call_type == CT_None);
     73     assert(last_call_args == nullptr);
     74     last_call_type = type;
     75     last_call_args = std::addressof(makeArgumentID<Args...>());
     76   }
     77 
     78   template <class... Args> static bool check_call(CallType type) {
     79     bool result = last_call_type == type && last_call_args &&
     80                   *last_call_args == makeArgumentID<Args...>();
     81     last_call_type = CT_None;
     82     last_call_args = nullptr;
     83     return result;
     84   }
     85 
     86   static CallType last_call_type;
     87   static const TypeID *last_call_args;
     88 };
     89 
     90 CallType ForwardingCallObject::last_call_type = CT_None;
     91 const TypeID *ForwardingCallObject::last_call_args = nullptr;
     92 
     93 void test_call_operator_forwarding() {
     94   using Fn = ForwardingCallObject;
     95   Fn obj{};
     96   const Fn &cobj = obj;
     97   { // test call operator forwarding - no variant
     98     std::visit(obj);
     99     assert(Fn::check_call<>(CT_NonConst | CT_LValue));
    100     std::visit(cobj);
    101     assert(Fn::check_call<>(CT_Const | CT_LValue));
    102     std::visit(std::move(obj));
    103     assert(Fn::check_call<>(CT_NonConst | CT_RValue));
    104     std::visit(std::move(cobj));
    105     assert(Fn::check_call<>(CT_Const | CT_RValue));
    106   }
    107   { // test call operator forwarding - single variant, single arg
    108     using V = std::variant<int>;
    109     V v(42);
    110     std::visit(obj, v);
    111     assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
    112     std::visit(cobj, v);
    113     assert(Fn::check_call<int &>(CT_Const | CT_LValue));
    114     std::visit(std::move(obj), v);
    115     assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
    116     std::visit(std::move(cobj), v);
    117     assert(Fn::check_call<int &>(CT_Const | CT_RValue));
    118   }
    119   { // test call operator forwarding - single variant, multi arg
    120     using V = std::variant<int, long, double>;
    121     V v(42l);
    122     std::visit(obj, v);
    123     assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
    124     std::visit(cobj, v);
    125     assert(Fn::check_call<long &>(CT_Const | CT_LValue));
    126     std::visit(std::move(obj), v);
    127     assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
    128     std::visit(std::move(cobj), v);
    129     assert(Fn::check_call<long &>(CT_Const | CT_RValue));
    130   }
    131   { // test call operator forwarding - multi variant, multi arg
    132     using V = std::variant<int, long, double>;
    133     using V2 = std::variant<int *, std::string>;
    134     V v(42l);
    135     V2 v2("hello");
    136     std::visit(obj, v, v2);
    137     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
    138     std::visit(cobj, v, v2);
    139     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
    140     std::visit(std::move(obj), v, v2);
    141     assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
    142     std::visit(std::move(cobj), v, v2);
    143     assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
    144   }
    145 }
    146 
    147 void test_argument_forwarding() {
    148   using Fn = ForwardingCallObject;
    149   Fn obj{};
    150   const auto Val = CT_LValue | CT_NonConst;
    151   { // single argument - value type
    152     using V = std::variant<int>;
    153     V v(42);
    154     const V &cv = v;
    155     std::visit(obj, v);
    156     assert(Fn::check_call<int &>(Val));
    157     std::visit(obj, cv);
    158     assert(Fn::check_call<const int &>(Val));
    159     std::visit(obj, std::move(v));
    160     assert(Fn::check_call<int &&>(Val));
    161     std::visit(obj, std::move(cv));
    162     assert(Fn::check_call<const int &&>(Val));
    163   }
    164 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
    165   { // single argument - lvalue reference
    166     using V = std::variant<int &>;
    167     int x = 42;
    168     V v(x);
    169     const V &cv = v;
    170     std::visit(obj, v);
    171     assert(Fn::check_call<int &>(Val));
    172     std::visit(obj, cv);
    173     assert(Fn::check_call<int &>(Val));
    174     std::visit(obj, std::move(v));
    175     assert(Fn::check_call<int &>(Val));
    176     std::visit(obj, std::move(cv));
    177     assert(Fn::check_call<int &>(Val));
    178   }
    179   { // single argument - rvalue reference
    180     using V = std::variant<int &&>;
    181     int x = 42;
    182     V v(std::move(x));
    183     const V &cv = v;
    184     std::visit(obj, v);
    185     assert(Fn::check_call<int &>(Val));
    186     std::visit(obj, cv);
    187     assert(Fn::check_call<int &>(Val));
    188     std::visit(obj, std::move(v));
    189     assert(Fn::check_call<int &&>(Val));
    190     std::visit(obj, std::move(cv));
    191     assert(Fn::check_call<int &&>(Val));
    192   }
    193   { // multi argument - multi variant
    194     using S = const std::string &;
    195     using V = std::variant<int, S, long &&>;
    196     const std::string str = "hello";
    197     long l = 43;
    198     V v1(42);
    199     const V &cv1 = v1;
    200     V v2(str);
    201     const V &cv2 = v2;
    202     V v3(std::move(l));
    203     const V &cv3 = v3;
    204     std::visit(obj, v1, v2, v3);
    205     assert((Fn::check_call<int &, S, long &>(Val)));
    206     std::visit(obj, cv1, cv2, std::move(v3));
    207     assert((Fn::check_call<const int &, S, long &&>(Val)));
    208   }
    209 #endif
    210 }
    211 
    212 struct ReturnFirst {
    213   template <class... Args> constexpr int operator()(int f, Args &&...) const {
    214     return f;
    215   }
    216 };
    217 
    218 struct ReturnArity {
    219   template <class... Args> constexpr int operator()(Args &&...) const {
    220     return sizeof...(Args);
    221   }
    222 };
    223 
    224 void test_constexpr() {
    225   constexpr ReturnFirst obj{};
    226   constexpr ReturnArity aobj{};
    227   {
    228     using V = std::variant<int>;
    229     constexpr V v(42);
    230     static_assert(std::visit(obj, v) == 42, "");
    231   }
    232   {
    233     using V = std::variant<short, long, char>;
    234     constexpr V v(42l);
    235     static_assert(std::visit(obj, v) == 42, "");
    236   }
    237   {
    238     using V1 = std::variant<int>;
    239     using V2 = std::variant<int, char *, long long>;
    240     using V3 = std::variant<bool, int, int>;
    241     constexpr V1 v1;
    242     constexpr V2 v2(nullptr);
    243     constexpr V3 v3;
    244     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
    245   }
    246   {
    247     using V1 = std::variant<int>;
    248     using V2 = std::variant<int, char *, long long>;
    249     using V3 = std::variant<void *, int, int>;
    250     constexpr V1 v1;
    251     constexpr V2 v2(nullptr);
    252     constexpr V3 v3;
    253     static_assert(std::visit(aobj, v1, v2, v3) == 3, "");
    254   }
    255 }
    256 
    257 void test_exceptions() {
    258 #ifndef TEST_HAS_NO_EXCEPTIONS
    259   ReturnArity obj{};
    260   auto test = [&](auto &&... args) {
    261     try {
    262       std::visit(obj, args...);
    263     } catch (const std::bad_variant_access &) {
    264       return true;
    265     } catch (...) {
    266     }
    267     return false;
    268   };
    269   {
    270     using V = std::variant<int, MakeEmptyT>;
    271     V v;
    272     makeEmpty(v);
    273     assert(test(v));
    274   }
    275   {
    276     using V = std::variant<int, MakeEmptyT>;
    277     using V2 = std::variant<long, std::string, void *>;
    278     V v;
    279     makeEmpty(v);
    280     V2 v2("hello");
    281     assert(test(v, v2));
    282   }
    283   {
    284     using V = std::variant<int, MakeEmptyT>;
    285     using V2 = std::variant<long, std::string, void *>;
    286     V v;
    287     makeEmpty(v);
    288     V2 v2("hello");
    289     assert(test(v2, v));
    290   }
    291   {
    292     using V = std::variant<int, MakeEmptyT>;
    293     using V2 = std::variant<long, std::string, void *, MakeEmptyT>;
    294     V v;
    295     makeEmpty(v);
    296     V2 v2;
    297     makeEmpty(v2);
    298     assert(test(v, v2));
    299   }
    300 #endif
    301 }
    302 
    303 // See https://bugs.llvm.org/show_bug.cgi?id=31916
    304 void test_caller_accepts_nonconst() {
    305   struct A {};
    306   struct Visitor {
    307     void operator()(A&) {}
    308   };
    309   std::variant<A> v;
    310   std::visit(Visitor{}, v);
    311 }
    312 
    313 int main() {
    314   test_call_operator_forwarding();
    315   test_argument_forwarding();
    316   test_constexpr();
    317   test_exceptions();
    318   test_caller_accepts_nonconst();
    319 }
    320