[libcxx-commits] [libcxx] 51faba3 - [libc++] Implement P0655R1 visit<R>: Explicit Return Type for visit

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 25 08:14:52 PST 2021


Author: Ruslan Arutyunyan
Date: 2021-01-25T11:14:45-05:00
New Revision: 51faba35fd81fbd3af407a29c136895a718ccd96

URL: https://github.com/llvm/llvm-project/commit/51faba35fd81fbd3af407a29c136895a718ccd96
DIFF: https://github.com/llvm/llvm-project/commit/51faba35fd81fbd3af407a29c136895a718ccd96.diff

LOG: [libc++] Implement P0655R1 visit<R>: Explicit Return Type for visit

Differential Revision: https://reviews.llvm.org/D92044

Added: 
    libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp

Modified: 
    libcxx/docs/Cxx2aStatusPaperStatus.csv
    libcxx/include/variant
    libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp
    libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
    libcxx/test/support/variant_test_helpers.h

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv
index efdcb1b67161..8991a03b2a5c 100644
--- a/libcxx/docs/Cxx2aStatusPaperStatus.csv
+++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv
@@ -59,7 +59,7 @@
 "`P0595R2 <https://wg21.link/P0595R2>`__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0"
 "`P0602R4 <https://wg21.link/P0602R4>`__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0"
 "`P0608R3 <https://wg21.link/P0608R3>`__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0"
-"`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","* *",""
+"`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","|Complete|","12.0"
 "`P0771R1 <https://wg21.link/P0771R1>`__","LWG","std::function move constructor should be noexcept","San Diego","|Complete|","6.0"
 "`P0896R4 <https://wg21.link/P0896R4>`__","LWG","The One Ranges Proposal","San Diego","* *",""
 "`P0899R1 <https://wg21.link/P0899R1>`__","LWG","P0899R1 - LWG 3016 is not a defect","San Diego","|Nothing To Do|",""

diff  --git a/libcxx/include/variant b/libcxx/include/variant
index daa3dd2d71c6..cb46f09bdc06 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -169,6 +169,9 @@ namespace std {
   template <class Visitor, class... Variants>
   constexpr see below visit(Visitor&&, Variants&&...);
 
+  template <class R, class Visitor, class... Variants>
+  constexpr R visit(Visitor&&, Variants&&...); // since C++20
+
   // 20.7.7, class monostate
   struct monostate;
 
@@ -583,6 +586,16 @@ struct __variant {
         __make_value_visitor(_VSTD::forward<_Visitor>(__visitor)),
         _VSTD::forward<_Vs>(__vs)...);
   }
+#if _LIBCPP_STD_VER > 17
+  template <class _Rp, class _Visitor, class... _Vs>
+  inline _LIBCPP_INLINE_VISIBILITY
+  static constexpr _Rp __visit_value(_Visitor&& __visitor,
+                                     _Vs&&... __vs) {
+    return __visit_alt(
+        __make_value_visitor<_Rp>(_VSTD::forward<_Visitor>(__visitor)),
+        _VSTD::forward<_Vs>(__vs)...);
+  }
+#endif
 
 private:
   template <class _Visitor, class... _Values>
@@ -605,12 +618,43 @@ private:
     _Visitor&& __visitor;
   };
 
+#if _LIBCPP_STD_VER > 17
+  template <class _Rp, class _Visitor>
+  struct __value_visitor_return_type {
+    template <class... _Alts>
+    inline _LIBCPP_INLINE_VISIBILITY
+    constexpr _Rp operator()(_Alts&&... __alts) const {
+      __std_visit_exhaustive_visitor_check<
+          _Visitor,
+          decltype((_VSTD::forward<_Alts>(__alts).__value))...>();
+      if constexpr (is_void_v<_Rp>) {
+        _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor),
+                                  _VSTD::forward<_Alts>(__alts).__value...);
+      }
+      else {
+        return _VSTD::__invoke_constexpr(_VSTD::forward<_Visitor>(__visitor),
+                                         _VSTD::forward<_Alts>(__alts).__value...);
+      }
+    }
+
+    _Visitor&& __visitor;
+  };
+#endif
+
   template <class _Visitor>
   inline _LIBCPP_INLINE_VISIBILITY
   static constexpr auto __make_value_visitor(_Visitor&& __visitor) {
     return __value_visitor<_Visitor>{_VSTD::forward<_Visitor>(__visitor)};
   }
+
+#if _LIBCPP_STD_VER > 17
+  template <class _Rp, class _Visitor>
+  inline _LIBCPP_INLINE_VISIBILITY
+  static constexpr auto __make_value_visitor(_Visitor&& __visitor) {
+    return __value_visitor_return_type<_Rp, _Visitor>{_VSTD::forward<_Visitor>(__visitor)};
+  }
 };
+#endif
 
 } // namespace __visitation
 
@@ -1594,18 +1638,37 @@ constexpr bool operator>=(const variant<_Types...>& __lhs,
 template <class _Visitor, class... _Vs>
 inline _LIBCPP_INLINE_VISIBILITY
 _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
-constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
-  using __variant_detail::__visitation::__variant;
-  bool __results[] = {__vs.valueless_by_exception()...};
-  for (bool __result : __results) {
-    if (__result) {
+constexpr void __throw_if_valueless(_Visitor&& __visitor, _Vs&&... __vs) {
+  const bool __valueless = (... || __vs.valueless_by_exception());
+  if (__valueless) {
       __throw_bad_variant_access();
-    }
   }
+}
+
+template <class _Visitor, class... _Vs>
+inline _LIBCPP_INLINE_VISIBILITY
+_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
+constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
+  using __variant_detail::__visitation::__variant;
+  _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor),
+                              _VSTD::forward<_Vs>(__vs)...);
   return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor),
                                   _VSTD::forward<_Vs>(__vs)...);
 }
 
+#if _LIBCPP_STD_VER > 17
+template <class _Rp, class _Visitor, class... _Vs>
+inline _LIBCPP_INLINE_VISIBILITY
+_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
+constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) {
+  using __variant_detail::__visitation::__variant;
+  _VSTD::__throw_if_valueless(_VSTD::forward<_Visitor>(__visitor),
+                              _VSTD::forward<_Vs>(__vs)...);
+  return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor),
+                                       _VSTD::forward<_Vs>(__vs)...);
+}
+#endif
+
 struct _LIBCPP_TEMPLATE_VIS monostate {};
 
 inline _LIBCPP_INLINE_VISIBILITY

diff  --git a/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp
index af3cd759dbef..94ca6cf81ff6 100644
--- a/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp
+++ b/libcxx/test/std/utilities/variant/variant.visit/robust_against_adl.pass.cpp
@@ -32,6 +32,10 @@ constexpr bool test(bool do_it)
         std::variant<Holder<Incomplete>*, int> v = nullptr;
         std::visit([](auto){}, v);
         std::visit([](auto) -> Holder<Incomplete>* { return nullptr; }, v);
+#if TEST_STD_VER > 17
+        std::visit<void>([](auto){}, v);
+        std::visit<void*>([](auto) -> Holder<Incomplete>* { return nullptr; }, v);
+#endif
     }
     return true;
 }

diff  --git a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
index 0edb83be04d4..b88ecc037d51 100644
--- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
+++ b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
@@ -27,70 +27,8 @@
 #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>
-  ForwardingCallObject& operator()(Args&&...) & {
-    set_call<Args &&...>(CT_NonConst | CT_LValue);
-    return *this;
-  }
-
-  template <class... Args>
-  const ForwardingCallObject& operator()(Args&&...) const & {
-    set_call<Args &&...>(CT_Const | CT_LValue);
-    return *this;
-  }
-
-  template <class... Args>
-  ForwardingCallObject&& operator()(Args&&...) && {
-    set_call<Args &&...>(CT_NonConst | CT_RValue);
-    return std::move(*this);
-  }
-
-  template <class... Args>
-  const ForwardingCallObject&& operator()(Args&&...) const && {
-    set_call<Args &&...>(CT_Const | CT_RValue);
-    return std::move(*this);
-  }
-
-  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{};
@@ -296,18 +234,6 @@ void test_return_type() {
   }
 }
 
-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{};

diff  --git a/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp
new file mode 100644
index 000000000000..7e569e2e9e42
--- /dev/null
+++ b/libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp
@@ -0,0 +1,430 @@
+// -*- 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++03, c++11, c++14, c++17
+
+// Throwing bad_variant_access is supported starting in macosx10.13
+// XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions
+// XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions
+// XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions
+// XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions
+
+// <variant>
+// template <class R, class Visitor, class... Variants>
+// constexpr R visit(Visitor&& vis, Variants&&... vars);
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include "test_macros.h"
+#include "variant_test_helpers.h"
+
+template <typename ReturnType>
+void test_call_operator_forwarding() {
+  using Fn = ForwardingCallObject;
+  Fn obj{};
+  const Fn &cobj = obj;
+  { // test call operator forwarding - no variant
+    std::visit<ReturnType>(obj);
+    assert(Fn::check_call<>(CT_NonConst | CT_LValue));
+    std::visit<ReturnType>(cobj);
+    assert(Fn::check_call<>(CT_Const | CT_LValue));
+    std::visit<ReturnType>(std::move(obj));
+    assert(Fn::check_call<>(CT_NonConst | CT_RValue));
+    std::visit<ReturnType>(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<ReturnType>(obj, v);
+    assert(Fn::check_call<int &>(CT_NonConst | CT_LValue));
+    std::visit<ReturnType>(cobj, v);
+    assert(Fn::check_call<int &>(CT_Const | CT_LValue));
+    std::visit<ReturnType>(std::move(obj), v);
+    assert(Fn::check_call<int &>(CT_NonConst | CT_RValue));
+    std::visit<ReturnType>(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<ReturnType>(obj, v);
+    assert(Fn::check_call<long &>(CT_NonConst | CT_LValue));
+    std::visit<ReturnType>(cobj, v);
+    assert(Fn::check_call<long &>(CT_Const | CT_LValue));
+    std::visit<ReturnType>(std::move(obj), v);
+    assert(Fn::check_call<long &>(CT_NonConst | CT_RValue));
+    std::visit<ReturnType>(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<int>(obj, v, v2);
+    assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue)));
+    std::visit<ReturnType>(cobj, v, v2);
+    assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue)));
+    std::visit<ReturnType>(std::move(obj), v, v2);
+    assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue)));
+    std::visit<ReturnType>(std::move(cobj), v, v2);
+    assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue)));
+  }
+  {
+    using V = std::variant<int, long, double, std::string>;
+    V v1(42l), v2("hello"), v3(101), v4(1.1);
+    std::visit<ReturnType>(obj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_LValue)));
+    std::visit<ReturnType>(cobj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_LValue)));
+    std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int &, double &>(CT_NonConst | CT_RValue)));
+    std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int &, double &>(CT_Const | CT_RValue)));
+  }
+  {
+    using V = std::variant<int, long, double, int*, std::string>;
+    V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
+    std::visit<ReturnType>(obj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_LValue)));
+    std::visit<ReturnType>(cobj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_LValue)));
+    std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_NonConst | CT_RValue)));
+    std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int *&, double &>(CT_Const | CT_RValue)));
+  }
+}
+
+template <typename ReturnType>
+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<ReturnType>(obj, v);
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(obj, cv);
+    assert(Fn::check_call<const int &>(Val));
+    std::visit<ReturnType>(obj, std::move(v));
+    assert(Fn::check_call<int &&>(Val));
+    std::visit<ReturnType>(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<ReturnType>(obj, v);
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(obj, cv);
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(obj, std::move(v));
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(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<ReturnType>(obj, v);
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(obj, cv);
+    assert(Fn::check_call<int &>(Val));
+    std::visit<ReturnType>(obj, std::move(v));
+    assert(Fn::check_call<int &&>(Val));
+    std::visit<ReturnType>(obj, std::move(cv));
+    assert(Fn::check_call<int &&>(Val));
+  }
+#endif
+  { // multi argument - multi variant
+    using V = std::variant<int, std::string, long>;
+    V v1(42), v2("hello"), v3(43l);
+    std::visit<ReturnType>(obj, v1, v2, v3);
+    assert((Fn::check_call<int &, std::string &, long &>(Val)));
+    std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3));
+    assert((Fn::check_call<const int &, const std::string &, long &&>(Val)));
+  }
+  {
+    using V = std::variant<int, long, double, std::string>;
+    V v1(42l), v2("hello"), v3(101), v4(1.1);
+    std::visit<ReturnType>(obj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int &, double &>(Val)));
+    std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
+    assert((Fn::check_call<const long &, const std::string &, int &&, double &&>(Val)));
+  }
+  {
+    using V = std::variant<int, long, double, int*, std::string>;
+    V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
+    std::visit<ReturnType>(obj, v1, v2, v3, v4);
+    assert((Fn::check_call<long &, std::string &, int *&, double &>(Val)));
+    std::visit<ReturnType>(obj, std::as_const(v1), std::as_const(v2), std::move(v3), std::move(v4));
+    assert((Fn::check_call<const long &, const std::string &, int *&&, double &&>(Val)));
+  }
+}
+
+template <typename ReturnType>
+void test_return_type() {
+  using Fn = ForwardingCallObject;
+  Fn obj{};
+  const Fn &cobj = obj;
+  { // test call operator forwarding - no variant
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj))), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj))), ReturnType>);
+  }
+  { // test call operator forwarding - single variant, single arg
+    using V = std::variant<int>;
+    V v(42);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v)), ReturnType>);
+  }
+  { // test call operator forwarding - single variant, multi arg
+    using V = std::variant<int, long, double>;
+    V v(42l);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v)), ReturnType>);
+  }
+  { // 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");
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v, v2)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v, v2)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v, v2)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v, v2)), ReturnType>);
+  }
+  {
+    using V = std::variant<int, long, double, std::string>;
+    V v1(42l), v2("hello"), v3(101), v4(1.1);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4)), ReturnType>);
+  }
+  {
+    using V = std::variant<int, long, double, int*, std::string>;
+    V v1(42l), v2("hello"), v3(nullptr), v4(1.1);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(obj, v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(cobj, v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(obj), v1, v2, v3, v4)), ReturnType>);
+    static_assert(std::is_same_v<decltype(std::visit<ReturnType>(std::move(cobj), v1, v2, v3, v4)), ReturnType>);
+  }
+}
+
+void test_constexpr_void() {
+  constexpr ReturnFirst obj{};
+  constexpr ReturnArity aobj{};
+  {
+    using V = std::variant<int>;
+    constexpr V v(42);
+    static_assert((std::visit<void>(obj, v), 42) == 42, "");
+  }
+  {
+    using V = std::variant<short, long, char>;
+    constexpr V v(42l);
+    static_assert((std::visit<void>(obj, v), 42) == 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<void>(aobj, v1, v2, v3), 3) == 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<void>(aobj, v1, v2, v3), 3) == 3, "");
+  }
+  {
+    using V = std::variant<int, long, double, int *>;
+    constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
+    static_assert((std::visit<void>(aobj, v1, v2, v3, v4), 4) == 4, "");
+  }
+  {
+    using V = std::variant<int, long, double, long long, int *>;
+    constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
+    static_assert((std::visit<void>(aobj, v1, v2, v3, v4), 4) == 4, "");
+  }
+}
+
+void test_constexpr_int() {
+  constexpr ReturnFirst obj{};
+  constexpr ReturnArity aobj{};
+  {
+    using V = std::variant<int>;
+    constexpr V v(42);
+    static_assert(std::visit<int>(obj, v) == 42, "");
+  }
+  {
+    using V = std::variant<short, long, char>;
+    constexpr V v(42l);
+    static_assert(std::visit<int>(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<int>(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<int>(aobj, v1, v2, v3) == 3, "");
+  }
+  {
+    using V = std::variant<int, long, double, int *>;
+    constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
+    static_assert(std::visit<int>(aobj, v1, v2, v3, v4) == 4, "");
+  }
+  {
+    using V = std::variant<int, long, double, long long, int *>;
+    constexpr V v1(42l), v2(101), v3(nullptr), v4(1.1);
+    static_assert(std::visit<int>(aobj, v1, v2, v3, v4) == 4, "");
+  }
+}
+
+template <typename ReturnType>
+void test_exceptions() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  ReturnArity obj{};
+  auto test = [&](auto &&... args) {
+    try {
+      std::visit<ReturnType>(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));
+  }
+  {
+    using V = std::variant<int, long, double, MakeEmptyT>;
+    V v1(42l), v2(101), v3(202), v4(1.1);
+    makeEmpty(v1);
+    assert(test(v1, v2, v3, v4));
+  }
+  {
+    using V = std::variant<int, long, double, long long, MakeEmptyT>;
+    V v1(42l), v2(101), v3(202), v4(1.1);
+    makeEmpty(v1);
+    makeEmpty(v2);
+    makeEmpty(v3);
+    makeEmpty(v4);
+    assert(test(v1, v2, v3, v4));
+  }
+#endif
+}
+
+// See https://bugs.llvm.org/show_bug.cgi?id=31916
+template <typename ReturnType>
+void test_caller_accepts_nonconst() {
+  struct A {};
+  struct Visitor {
+    auto operator()(A&) {
+      if constexpr (!std::is_void_v<ReturnType>)
+      {
+        return ReturnType{};
+      }
+    }
+  };
+  std::variant<A> v;
+  std::visit<ReturnType>(Visitor{}, v);
+}
+
+void test_constexpr_explicit_side_effect() {
+  auto test_lambda = [](int arg) constexpr {
+    std::variant<int> v = 101;
+    std::visit<void>([arg](int& x) constexpr { x = arg; }, v);
+    return std::get<int>(v);
+  };
+
+  static_assert(test_lambda(202) == 202, "");
+}
+
+int main(int, char**) {
+  test_call_operator_forwarding<void>();
+  test_argument_forwarding<void>();
+  test_return_type<void>();
+  test_constexpr_void();
+  test_exceptions<void>();
+  test_caller_accepts_nonconst<void>();
+  test_call_operator_forwarding<int>();
+  test_argument_forwarding<int>();
+  test_return_type<int>();
+  test_constexpr_int();
+  test_exceptions<int>();
+  test_caller_accepts_nonconst<int>();
+  test_constexpr_explicit_side_effect();
+
+  return 0;
+}

diff  --git a/libcxx/test/support/variant_test_helpers.h b/libcxx/test/support/variant_test_helpers.h
index 0e040931e0cd..92faf7988bc6 100644
--- a/libcxx/test/support/variant_test_helpers.h
+++ b/libcxx/test/support/variant_test_helpers.h
@@ -14,6 +14,7 @@
 #include <cassert>
 
 #include "test_macros.h"
+#include "type_id.h"
 
 #if TEST_STD_VER <= 14
 #error This file requires C++17
@@ -85,5 +86,83 @@ void makeEmpty(Variant& v) {
 }
 #endif // TEST_HAS_NO_EXCEPTIONS
 
+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>
+  ForwardingCallObject& operator()(Args&&...) & {
+    set_call<Args &&...>(CT_NonConst | CT_LValue);
+    return *this;
+  }
+
+  template <class... Args>
+  const ForwardingCallObject& operator()(Args&&...) const & {
+    set_call<Args &&...>(CT_Const | CT_LValue);
+    return *this;
+  }
+
+  template <class... Args>
+  ForwardingCallObject&& operator()(Args&&...) && {
+    set_call<Args &&...>(CT_NonConst | CT_RValue);
+    return std::move(*this);
+  }
+
+  template <class... Args>
+  const ForwardingCallObject&& operator()(Args&&...) const && {
+    set_call<Args &&...>(CT_Const | CT_RValue);
+    return std::move(*this);
+  }
+
+  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;
+  }
+
+  // To check explicit return type for visit<R>
+  constexpr operator int() const
+  {
+    return 0;
+  }
+
+  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;
+
+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);
+  }
+};
 
 #endif // SUPPORT_VARIANT_TEST_HELPERS_H


        


More information about the libcxx-commits mailing list