Fix visit<R> for variant.
authorVille Voutilainen <ville.voutilainen@gmail.com>
Mon, 8 Apr 2019 19:45:48 +0000 (22:45 +0300)
committerVille Voutilainen <ville@gcc.gnu.org>
Mon, 8 Apr 2019 19:45:48 +0000 (22:45 +0300)
* include/std/variant (__do_visit): Add a template parameter
for enforcing same return types for visit.
(__gen_vtable_impl): Likewise.
(_S_apply_single_alt): Adjust.
(__visit_invoke_impl): New. Handle casting to void.
(__do_visit_invoke): New. Enforces same return types.
(__do_visit_invoke_r): New. Converts return types.
(__visit_invoke): Adjust.
(__gen_vtable):  Add a template parameter for enforcing
same return types for visit.
* testsuite/20_util/variant/visit_r.cc: Add a test for a visitor with
different return types.
* testsuite/20_util/variant/visit_neg.cc: New. Ensures that
visitors with different return types don't accidentally
compile with regular visitation.

From-SVN: r270216

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/variant
libstdc++-v3/testsuite/20_util/variant/visit_neg.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/variant/visit_r.cc

index 7ea0fd0d96272fb98ced387ac60d3edc05181e6c..f363c44df4ae10e90a1a18e1bc0e52ff20947426 100644 (file)
@@ -1,3 +1,22 @@
+2019-04-08  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       Fix visit<R> for variant.
+       * include/std/variant (__do_visit): Add a template parameter
+       for enforcing same return types for visit.
+       (__gen_vtable_impl): Likewise.
+       (_S_apply_single_alt): Adjust.
+       (__visit_invoke_impl): New. Handle casting to void.
+       (__do_visit_invoke): New. Enforces same return types.
+       (__do_visit_invoke_r): New. Converts return types.
+       (__visit_invoke): Adjust.
+       (__gen_vtable):  Add a template parameter for enforcing
+       same return types for visit.
+       * testsuite/20_util/variant/visit_r.cc: Add a test for a visitor with
+       different return types.
+       * testsuite/20_util/variant/visit_neg.cc: New. Ensures that
+       visitors with different return types don't accidentally
+       compile with regular visitation.
+
 2019-04-08  Christophe Lyon  <christophe.lyon@linaro.org>
 
        * testsuite/27_io/filesystem/iterators/caching.cc: Add
index fdf04cf624a16148bba9e0a6433684a92e8d4c4a..7bab47231e73215435da6ceb3242fbd5c510e8d4 100644 (file)
@@ -138,7 +138,9 @@ namespace __variant
     constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
     get(const variant<_Types...>&&);
 
-  template<bool __use_index=false, typename _Visitor, typename... _Variants>
+  template<bool __use_index=false,
+          bool __same_return_types = true,
+          typename _Visitor, typename... _Variants>
     constexpr decltype(auto)
     __do_visit(_Visitor&& __visitor, _Variants&&... __variants);
 
@@ -868,12 +870,15 @@ namespace __variant
   //                       tuple<V1&&, V2&&>, std::index_sequence<1, 2>>
   // The returned multi-dimensional vtable can be fast accessed by the visitor
   // using index calculation.
-  template<typename _Array_type, typename _Variant_tuple, typename _Index_seq>
+  template<bool __same_return_types,
+          typename _Array_type, typename _Variant_tuple, typename _Index_seq>
     struct __gen_vtable_impl;
 
-  template<typename _Result_type, typename _Visitor, size_t... __dimensions,
+  template<bool __same_return_types,
+          typename _Result_type, typename _Visitor, size_t... __dimensions,
           typename... _Variants, size_t... __indices>
     struct __gen_vtable_impl<
+        __same_return_types,
        _Multi_array<_Result_type (*)(_Visitor, _Variants...), __dimensions...>,
        tuple<_Variants...>, std::index_sequence<__indices...>>
     {
@@ -915,10 +920,12 @@ namespace __variant
          if constexpr (__do_cookie)
            {
              __element = __gen_vtable_impl<
+               __same_return_types,
                _Tp,
                tuple<_Variants...>,
                std::index_sequence<__indices..., __index>>::_S_apply();
              *__cookie_element = __gen_vtable_impl<
+               __same_return_types,
                _Tp,
                tuple<_Variants...>,
                std::index_sequence<__indices..., variant_npos>>::_S_apply();
@@ -926,15 +933,18 @@ namespace __variant
          else
            {
              __element = __gen_vtable_impl<
+               __same_return_types,
                remove_reference_t<decltype(__element)>, tuple<_Variants...>,
                std::index_sequence<__indices..., __index>>::_S_apply();
            }
        }
     };
 
-  template<typename _Result_type, typename _Visitor, typename... _Variants,
+  template<bool __same_return_types,
+          typename _Result_type, typename _Visitor, typename... _Variants,
           size_t... __indices>
     struct __gen_vtable_impl<
+      __same_return_types,
       _Multi_array<_Result_type (*)(_Visitor, _Variants...)>,
                   tuple<_Variants...>, std::index_sequence<__indices...>>
     {
@@ -952,25 +962,56 @@ namespace __variant
        }
 
       static constexpr decltype(auto)
-      __visit_invoke(_Visitor&& __visitor, _Variants... __vars)
+      __visit_invoke_impl(_Visitor&& __visitor, _Variants... __vars)
       {
-       if constexpr (is_same_v<_Result_type, __variant_idx_cookie>)
-         return std::__invoke(std::forward<_Visitor>(__visitor),
+       if constexpr (is_same_v<_Result_type, __variant_idx_cookie>)
+         return std::__invoke(std::forward<_Visitor>(__visitor),
+            __element_by_index_or_cookie<__indices>(
+              std::forward<_Variants>(__vars))...,
+              integral_constant<size_t, __indices>()...);
+        else if constexpr (!__same_return_types &&
+                          std::is_void_v<_Result_type>)
+         return (void)std::__invoke(std::forward<_Visitor>(__visitor),
            __element_by_index_or_cookie<__indices>(
-             std::forward<_Variants>(__vars))...,
-             integral_constant<size_t, __indices>()...);
+             std::forward<_Variants>(__vars))...);
        else
          return std::__invoke(std::forward<_Visitor>(__visitor),
            __element_by_index_or_cookie<__indices>(
              std::forward<_Variants>(__vars))...);
       }
 
+      static constexpr decltype(auto)
+      __do_visit_invoke(_Visitor&& __visitor, _Variants... __vars)
+      {
+       return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
+                                  std::forward<_Variants>(__vars)...);
+      }
+
+      static constexpr _Result_type
+      __do_visit_invoke_r(_Visitor&& __visitor, _Variants... __vars)
+      {
+       return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
+                                  std::forward<_Variants>(__vars)...);
+      }
+
+      static constexpr decltype(auto)
+      __visit_invoke(_Visitor&& __visitor, _Variants... __vars)
+      {
+       if constexpr (__same_return_types)
+         return __do_visit_invoke(std::forward<_Visitor>(__visitor),
+                                  std::forward<_Variants>(__vars)...);
+       else
+         return __do_visit_invoke_r(std::forward<_Visitor>(__visitor),
+                                    std::forward<_Variants>(__vars)...);
+      }
+
       static constexpr auto
       _S_apply()
       { return _Array_type{&__visit_invoke}; }
     };
 
-  template<typename _Result_type, typename _Visitor, typename... _Variants>
+  template<bool __same_return_types,
+          typename _Result_type, typename _Visitor, typename... _Variants>
     struct __gen_vtable
     {
       using _Func_ptr = _Result_type (*)(_Visitor&&, _Variants...);
@@ -981,7 +1022,8 @@ namespace __variant
       static constexpr _Array_type
       _S_apply()
       {
-       return __gen_vtable_impl<_Array_type, tuple<_Variants...>,
+       return __gen_vtable_impl<__same_return_types,
+                                _Array_type, tuple<_Variants...>,
                                 std::index_sequence<>>::_S_apply();
       }
 
@@ -1582,7 +1624,9 @@ namespace __variant
          std::get<0>(std::forward<_Variants>(__variants))...);
     }
 
-  template<bool __use_index, typename _Visitor, typename... _Variants>
+  template<bool __use_index,
+          bool __same_return_types,
+          typename _Visitor, typename... _Variants>
     constexpr decltype(auto)
     __do_visit(_Visitor&& __visitor, _Variants&&... __variants)
     {
@@ -1592,6 +1636,7 @@ namespace __variant
                   std::forward<_Variants>(__variants)...));
 
       constexpr auto& __vtable = __detail::__variant::__gen_vtable<
+       __same_return_types,
        _Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
 
       auto __func_ptr = __vtable._M_access(__variants.index()...);
@@ -1618,12 +1663,13 @@ namespace __variant
       if ((__variants.valueless_by_exception() || ...))
        __throw_bad_variant_access("Unexpected index");
 
+
       if constexpr (std::is_void_v<_Res>)
-       (void) __do_visit(std::forward<_Visitor>(__visitor),
-                         std::forward<_Variants>(__variants)...);
+        (void) __do_visit<false, false>(std::forward<_Visitor>(__visitor),
+                                       std::forward<_Variants>(__variants)...);
       else
-       return __do_visit(std::forward<_Visitor>(__visitor),
-                         std::forward<_Variants>(__variants)...);
+       return __do_visit<false, false>(std::forward<_Visitor>(__visitor),
+                                       std::forward<_Variants>(__variants)...);
     }
 #endif
 
diff --git a/libstdc++-v3/testsuite/20_util/variant/visit_neg.cc b/libstdc++-v3/testsuite/20_util/variant/visit_neg.cc
new file mode 100644 (file)
index 0000000..ae6fdd1
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <variant>
+#include <testsuite_hooks.h>
+
+// { dg-error "invalid conversion" "" { target *-*-* } 0 }
+// { dg-prune-output "in 'constexpr' expansion" }
+
+void
+test01()
+{
+  {
+    struct Visitor
+    {
+      double operator()(double) {return 0.02;}
+      void operator()(int) {}
+    };
+    std::variant<int, double> v;
+    std::visit(Visitor(), v);
+  }
+}
+
+int
+main()
+{
+  test01();
+}
index 5eed0cf1a49976e0d538be9ba0685502b75af29b..55f5f8bcb9e59fa0d0611ceae558600097622b9e 100644 (file)
@@ -42,8 +42,22 @@ test01()
   static_assert(std::is_void_v<decltype(std::visit<void>(Visitor{}, v1, v2))>);
 }
 
+void test02()
+{
+  struct Visitor
+  {
+    int operator()(double) {return 42;}
+    double operator()(int) {return 0.02;}
+  };
+  std::variant<int, double> v;
+  std::visit<int>(Visitor(), v);
+  std::visit<const void>(Visitor(), v);
+}
+
+
 int
 main()
 {
   test01();
+  test02();
 }