[libcxx-commits] [libcxx] 0324b46 - [libc++] [C++2b] [P2162] Allow inheritance from std::variant.

Marek Kurdej via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 25 10:20:57 PDT 2021


Author: Marek Kurdej
Date: 2021-03-25T18:20:50+01:00
New Revision: 0324b46cd873abc4fabe19f4bd468d10398ffd0d

URL: https://github.com/llvm/llvm-project/commit/0324b46cd873abc4fabe19f4bd468d10398ffd0d
DIFF: https://github.com/llvm/llvm-project/commit/0324b46cd873abc4fabe19f4bd468d10398ffd0d.diff

LOG: [libc++] [C++2b] [P2162] Allow inheritance from std::variant.

This patch changes the variant even in pre-C++2b.
It should not break anything, only allow use cases that didn't work previously.

Notes:
 `__as_variant` is used in `__visitation::__variant::__visit_alt`, but I haven't used it in `__visitation::__variant::__visit_alt_at`.
That's because it is used only in `__visit_value_at`, which in turn is always used on variant specializations (that's in comparison operators).

* https://wg21.link/P2162

Reviewed By: ldionne, #libc, Quuxplusone

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

Added: 
    

Modified: 
    libcxx/docs/Cxx2bStatusPaperStatus.csv
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/include/variant
    libcxx/include/version
    libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
    libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
    libcxx/test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Cxx2bStatusPaperStatus.csv b/libcxx/docs/Cxx2bStatusPaperStatus.csv
index 79b5db076937..9cbd990b0bb9 100644
--- a/libcxx/docs/Cxx2bStatusPaperStatus.csv
+++ b/libcxx/docs/Cxx2bStatusPaperStatus.csv
@@ -7,7 +7,7 @@
 "`P1682R3 <https://wg21.link/P1682R3>`__","LWG","std::to_underlying for enumerations","February 2021","|Complete|","13.0"
 "`P2017R1 <https://wg21.link/P2017R1>`__","LWG","Conditionally borrowed ranges","February 2021","",""
 "`P2160R1 <https://wg21.link/P2160R1>`__","LWG","Locks lock lockables","February 2021","",""
-"`P2162R2 <https://wg21.link/P2162R2>`__","LWG","Inheriting from std::variant","February 2021","",""
+"`P2162R2 <https://wg21.link/P2162R2>`__","LWG","Inheriting from std::variant","February 2021","|Complete|","13.0"
 "`P2212R2 <https://wg21.link/P2212R2>`__","LWG","Relax Requirements for time_point::clock","February 2021","",""
 "`P2259R1 <https://wg21.link/P2259R1>`__","LWG","Repairing input range adaptors and counted_iterator","February 2021","",""
 "","","","","",""

diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 40a6bdaf3a4b..806b30f22d74 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -162,7 +162,7 @@ Status
     ------------------------------------------------- -----------------
     ``__cpp_lib_unordered_map_try_emplace``           ``201411L``
     ------------------------------------------------- -----------------
-    ``__cpp_lib_variant``                             ``201606L``
+    ``__cpp_lib_variant``                             ``202102L``
     ------------------------------------------------- -----------------
     ``__cpp_lib_void_t``                              ``201411L``
     ------------------------------------------------- -----------------

diff  --git a/libcxx/include/variant b/libcxx/include/variant
index 770dd335bae2..4e3db1f7afbc 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -318,6 +318,33 @@ using __variant_index_t =
 template <class _IndexType>
 constexpr _IndexType __variant_npos = static_cast<_IndexType>(-1);
 
+template <class... _Types>
+class _LIBCPP_TEMPLATE_VIS variant;
+
+template <class... _Types>
+_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&
+__as_variant(variant<_Types...>& __vs) noexcept {
+  return __vs;
+}
+
+template <class... _Types>
+_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>&
+__as_variant(const variant<_Types...>& __vs) noexcept {
+  return __vs;
+}
+
+template <class... _Types>
+_LIBCPP_INLINE_VISIBILITY constexpr variant<_Types...>&&
+__as_variant(variant<_Types...>&& __vs) noexcept {
+  return _VSTD::move(__vs);
+}
+
+template <class... _Types>
+_LIBCPP_INLINE_VISIBILITY constexpr const variant<_Types...>&&
+__as_variant(const variant<_Types...>&& __vs) noexcept {
+  return _VSTD::move(__vs);
+}
+
 namespace __find_detail {
 
 template <class _Tp, class... _Types>
@@ -564,8 +591,9 @@ struct __variant {
   inline _LIBCPP_INLINE_VISIBILITY
   static constexpr decltype(auto) __visit_alt(_Visitor&& __visitor,
                                               _Vs&&... __vs) {
-    return __base::__visit_alt(_VSTD::forward<_Visitor>(__visitor),
-                               _VSTD::forward<_Vs>(__vs).__impl...);
+    return __base::__visit_alt(
+        _VSTD::forward<_Visitor>(__visitor),
+        _VSTD::__as_variant(_VSTD::forward<_Vs>(__vs)).__impl...);
   }
 
   template <class _Visitor, class... _Vs>
@@ -586,6 +614,7 @@ 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
@@ -1637,18 +1666,21 @@ constexpr bool operator>=(const variant<_Types...>& __lhs,
 
 template <class... _Vs>
 inline _LIBCPP_INLINE_VISIBILITY
-_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
-constexpr void __throw_if_valueless(_Vs&&... __vs) {
-  const bool __valueless = (... || __vs.valueless_by_exception());
+    _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr void
+    __throw_if_valueless(_Vs&&... __vs) {
+  const bool __valueless =
+      (... || _VSTD::__as_variant(__vs).valueless_by_exception());
   if (__valueless) {
-      __throw_bad_variant_access();
+    __throw_bad_variant_access();
   }
 }
 
-template <class _Visitor, class... _Vs>
+template <
+    class _Visitor, class... _Vs,
+    typename = void_t<decltype(_VSTD::__as_variant(_VSTD::declval<_Vs>()))...> >
 inline _LIBCPP_INLINE_VISIBILITY
-_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
-constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
+    _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<_Vs>(__vs)...);
   return __variant::__visit_value(_VSTD::forward<_Visitor>(__visitor),
@@ -1656,10 +1688,12 @@ constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
 }
 
 #if _LIBCPP_STD_VER > 17
-template <class _Rp, class _Visitor, class... _Vs>
+template <
+    class _Rp, class _Visitor, class... _Vs,
+    typename = void_t<decltype(_VSTD::__as_variant(_VSTD::declval<_Vs>()))...> >
 inline _LIBCPP_INLINE_VISIBILITY
-_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
-constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs) {
+    _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS constexpr _Rp
+    visit(_Visitor&& __visitor, _Vs&&... __vs) {
   using __variant_detail::__visitation::__variant;
   _VSTD::__throw_if_valueless(_VSTD::forward<_Vs>(__vs)...);
   return __variant::__visit_value<_Rp>(_VSTD::forward<_Visitor>(__visitor),

diff  --git a/libcxx/include/version b/libcxx/include/version
index 040d72e1254b..7ff5f914d76c 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -160,7 +160,7 @@ __cpp_lib_type_trait_variable_templates                 201510L <type_traits>
 __cpp_lib_uncaught_exceptions                           201411L <exception>
 __cpp_lib_unordered_map_try_emplace                     201411L <unordered_map>
 __cpp_lib_unwrap_ref                                    201811L <functional>
-__cpp_lib_variant                                       201606L <variant>
+__cpp_lib_variant                                       202102L <variant>
 __cpp_lib_void_t                                        201411L <type_traits>
 
 */
@@ -259,7 +259,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_type_trait_variable_templates        201510L
 # define __cpp_lib_uncaught_exceptions                  201411L
 # define __cpp_lib_unordered_map_try_emplace            201411L
-# define __cpp_lib_variant                              201606L
+# define __cpp_lib_variant                              202102L
 # define __cpp_lib_void_t                               201411L
 #endif
 

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp
index 6d198d1121f7..f34c68183c15 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.pass.cpp
@@ -16,7 +16,7 @@
 // Test the feature test macros defined by <variant>
 
 /*  Constant             Value
-    __cpp_lib_variant    201606L [C++17]
+    __cpp_lib_variant    202102L [C++17]
 */
 
 #include <variant>
@@ -39,8 +39,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++17"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++17"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++17"
 # endif
 
 #elif TEST_STD_VER == 20
@@ -48,8 +48,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++20"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++20"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++20"
 # endif
 
 #elif TEST_STD_VER > 20
@@ -57,8 +57,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++2b"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++2b"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++2b"
 # endif
 
 #endif // TEST_STD_VER > 20

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index 83b286b05630..593d63d3406c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
@@ -150,7 +150,7 @@
     __cpp_lib_uncaught_exceptions                  201411L [C++17]
     __cpp_lib_unordered_map_try_emplace            201411L [C++17]
     __cpp_lib_unwrap_ref                           201811L [C++20]
-    __cpp_lib_variant                              201606L [C++17]
+    __cpp_lib_variant                              202102L [C++17]
     __cpp_lib_void_t                               201411L [C++17]
 */
 
@@ -2090,8 +2090,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++17"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++17"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++17"
 # endif
 
 # ifndef __cpp_lib_void_t
@@ -3277,8 +3277,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++20"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++20"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++20"
 # endif
 
 # ifndef __cpp_lib_void_t
@@ -4491,8 +4491,8 @@
 # ifndef __cpp_lib_variant
 #   error "__cpp_lib_variant should be defined in c++2b"
 # endif
-# if __cpp_lib_variant != 201606L
-#   error "__cpp_lib_variant should have the value 201606L in c++2b"
+# if __cpp_lib_variant != 202102L
+#   error "__cpp_lib_variant should have the value 202102L in c++2b"
 # endif
 
 # ifndef __cpp_lib_void_t

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 676c5fc1f05b..c3cc7eb77d0b 100644
--- a/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
+++ b/libcxx/test/std/utilities/variant/variant.visit/visit.pass.cpp
@@ -348,6 +348,86 @@ void test_caller_accepts_nonconst() {
   std::visit(Visitor{}, v);
 }
 
+struct MyVariant : std::variant<short, long, float> {};
+
+namespace std {
+template <size_t Index>
+void get(const MyVariant&) {
+  assert(false);
+}
+} // namespace std
+
+void test_derived_from_variant() {
+  auto v1 = MyVariant{42};
+  const auto cv1 = MyVariant{142};
+  std::visit([](auto x) { assert(x == 42); }, v1);
+  std::visit([](auto x) { assert(x == 142); }, cv1);
+  std::visit([](auto x) { assert(x == -1.25f); }, MyVariant{-1.25f});
+  std::visit([](auto x) { assert(x == 42); }, std::move(v1));
+  std::visit([](auto x) { assert(x == 142); }, std::move(cv1));
+
+  // Check that visit does not take index nor valueless_by_exception members from the base class.
+  struct EvilVariantBase {
+    int index;
+    char valueless_by_exception;
+  };
+
+  struct EvilVariant1 : std::variant<int, long, double>,
+                        std::tuple<int>,
+                        EvilVariantBase {
+    using std::variant<int, long, double>::variant;
+  };
+
+  std::visit([](auto x) { assert(x == 12); }, EvilVariant1{12});
+  std::visit([](auto x) { assert(x == 12.3); }, EvilVariant1{12.3});
+
+  // Check that visit unambiguously picks the variant, even if the other base has __impl member.
+  struct ImplVariantBase {
+    struct Callable {
+      bool operator()();
+    };
+
+    Callable __impl;
+  };
+
+  struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
+    using std::variant<int, long, double>::variant;
+  };
+
+  std::visit([](auto x) { assert(x == 12); }, EvilVariant2{12});
+  std::visit([](auto x) { assert(x == 12.3); }, EvilVariant2{12.3});
+}
+
+struct any_visitor {
+  template <typename T>
+  void operator()(const T&) const {}
+};
+
+template <typename T, typename = decltype(std::visit(
+                          std::declval<any_visitor&>(), std::declval<T>()))>
+constexpr bool has_visit(int) {
+  return true;
+}
+
+template <typename T>
+constexpr bool has_visit(...) {
+  return false;
+}
+
+void test_sfinae() {
+  struct BadVariant : std::variant<short>, std::variant<long, float> {};
+  struct BadVariant2 : private std::variant<long, float> {};
+  struct GoodVariant : std::variant<long, float> {};
+  struct GoodVariant2 : GoodVariant {};
+
+  static_assert(!has_visit<int>(0));
+  static_assert(!has_visit<BadVariant>(0));
+  static_assert(!has_visit<BadVariant2>(0));
+  static_assert(has_visit<std::variant<int>>(0));
+  static_assert(has_visit<GoodVariant>(0));
+  static_assert(has_visit<GoodVariant2>(0));
+}
+
 int main(int, char**) {
   test_call_operator_forwarding();
   test_argument_forwarding();
@@ -355,6 +435,8 @@ int main(int, char**) {
   test_constexpr();
   test_exceptions();
   test_caller_accepts_nonconst();
+  test_derived_from_variant();
+  test_sfinae();
 
   return 0;
 }

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
index 7e569e2e9e42..459b75f717f5 100644
--- 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
@@ -411,6 +411,99 @@ void test_constexpr_explicit_side_effect() {
   static_assert(test_lambda(202) == 202, "");
 }
 
+void test_derived_from_variant() {
+  struct MyVariant : std::variant<short, long, float> {};
+
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == 42);
+        return true;
+      },
+      MyVariant{42});
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == -1.3f);
+        return true;
+      },
+      MyVariant{-1.3f});
+
+  // Check that visit does not take index nor valueless_by_exception members from the base class.
+  struct EvilVariantBase {
+    int index;
+    char valueless_by_exception;
+  };
+
+  struct EvilVariant1 : std::variant<int, long, double>,
+                        std::tuple<int>,
+                        EvilVariantBase {
+    using std::variant<int, long, double>::variant;
+  };
+
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == 12);
+        return true;
+      },
+      EvilVariant1{12});
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == 12.3);
+        return true;
+      },
+      EvilVariant1{12.3});
+
+  // Check that visit unambiguously picks the variant, even if the other base has __impl member.
+  struct ImplVariantBase {
+    struct Callable {
+      bool operator()();
+    };
+
+    Callable __impl;
+  };
+
+  struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase {
+    using std::variant<int, long, double>::variant;
+  };
+
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == 12);
+        return true;
+      },
+      EvilVariant2{12});
+  std::visit<bool>(
+      [](auto x) {
+        assert(x == 12.3);
+        return true;
+      },
+      EvilVariant2{12.3});
+}
+
+struct any_visitor {
+  template <typename T>
+  bool operator()(const T&) {
+    return true;
+  }
+};
+
+template <typename T, typename = decltype(std::visit<bool>(
+                          std::declval<any_visitor&>(), std::declval<T>()))>
+constexpr bool has_visit(int) {
+  return true;
+}
+
+template <typename T>
+constexpr bool has_visit(...) {
+  return false;
+}
+
+void test_sfinae() {
+  struct BadVariant : std::variant<short>, std::variant<long, float> {};
+
+  static_assert(has_visit<std::variant<int> >(int()));
+  static_assert(!has_visit<BadVariant>(int()));
+}
+
 int main(int, char**) {
   test_call_operator_forwarding<void>();
   test_argument_forwarding<void>();
@@ -425,6 +518,8 @@ int main(int, char**) {
   test_exceptions<int>();
   test_caller_accepts_nonconst<int>();
   test_constexpr_explicit_side_effect();
+  test_derived_from_variant();
+  test_sfinae();
 
   return 0;
 }

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d6fa8361853f..c3db978f05f7 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -627,7 +627,7 @@ def add_version_header(tc):
     "headers": ["functional"],
   }, {
     "name": "__cpp_lib_variant",
-    "values": { "c++17": 201606 },
+    "values": { "c++17": 202102 },
     "headers": ["variant"],
   }, {
     "name": "__cpp_lib_void_t",


        


More information about the libcxx-commits mailing list