123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- // -*- 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.h"
- 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;
- }
|