From 3d01c7c2f20ffc062d01a525b8e567931c2b089c Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Mon, 8 Apr 2019 22:45:48 +0300 Subject: [PATCH] Fix visit 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. From-SVN: r270216 --- libstdc++-v3/ChangeLog | 19 +++++ libstdc++-v3/include/std/variant | 78 +++++++++++++++---- .../testsuite/20_util/variant/visit_neg.cc | 45 +++++++++++ .../testsuite/20_util/variant/visit_r.cc | 14 ++++ 4 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/variant/visit_neg.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 7ea0fd0d962..f363c44df4a 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,22 @@ +2019-04-08 Ville Voutilainen + + Fix visit 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 * testsuite/27_io/filesystem/iterators/caching.cc: Add diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index fdf04cf624a..7bab47231e7 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -138,7 +138,9 @@ namespace __variant constexpr variant_alternative_t<_Np, variant<_Types...>> const&& get(const variant<_Types...>&&); - template + template constexpr decltype(auto) __do_visit(_Visitor&& __visitor, _Variants&&... __variants); @@ -868,12 +870,15 @@ namespace __variant // tuple, std::index_sequence<1, 2>> // The returned multi-dimensional vtable can be fast accessed by the visitor // using index calculation. - template + template struct __gen_vtable_impl; - template 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, tuple<_Variants...>, std::index_sequence<__indices..., __index>>::_S_apply(); } } }; - template 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()...); + 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()...); + 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 + template 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 + template 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(std::forward<_Visitor>(__visitor), + std::forward<_Variants>(__variants)...); else - return __do_visit(std::forward<_Visitor>(__visitor), - std::forward<_Variants>(__variants)...); + return __do_visit(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 index 00000000000..ae6fdd11b95 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/visit_neg.cc @@ -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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include +#include + +// { 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 v; + std::visit(Visitor(), v); + } +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/20_util/variant/visit_r.cc b/libstdc++-v3/testsuite/20_util/variant/visit_r.cc index 5eed0cf1a49..55f5f8bcb9e 100644 --- a/libstdc++-v3/testsuite/20_util/variant/visit_r.cc +++ b/libstdc++-v3/testsuite/20_util/variant/visit_r.cc @@ -42,8 +42,22 @@ test01() static_assert(std::is_void_v(Visitor{}, v1, v2))>); } +void test02() +{ + struct Visitor + { + int operator()(double) {return 42;} + double operator()(int) {return 0.02;} + }; + std::variant v; + std::visit(Visitor(), v); + std::visit(Visitor(), v); +} + + int main() { test01(); + test02(); } -- 2.30.2