Add std::variant, std::tuple, std::array, std::reference_wrapper, and reference/const/volatile specializations for IsOwner and IsView

Fixes: #1960
PiperOrigin-RevId: 953386746
Change-Id: If5184a5b5201964f3f5d8aeb48675a546afe26a9
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index 78f7c86..6ca2dc8 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -35,12 +35,15 @@
 #ifndef ABSL_META_TYPE_TRAITS_H_
 #define ABSL_META_TYPE_TRAITS_H_
 
+#include <array>
 #include <cstddef>
 #include <functional>
 #include <string>
 #include <string_view>
+#include <tuple>
 #include <type_traits>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include "absl/base/attributes.h"
@@ -564,15 +567,32 @@
 template <typename T>
 struct IsOwner : IsOwnerImpl<T> {};
 
+template <typename T>
+struct IsOwner<T&> : std::false_type {};
+
+template <typename T>
+struct IsOwner<T&&> : std::false_type {};
+
+template <typename T>
+struct IsOwner<const T> : IsOwner<T> {};
+
+template <typename T>
+struct IsOwner<volatile T> : IsOwner<T> {};
+
+template <typename T>
+struct IsOwner<const volatile T> : IsOwner<T> {};
+
+template <typename T>
+struct IsOwner<std::reference_wrapper<T>> : std::false_type {};
+
+template <typename T, std::size_t N>
+struct IsOwner<std::array<T, N>>
+    : std::conditional_t<N != 0, IsOwner<T>, std::false_type> {};
+
 // This allows incomplete types to be used for associative containers, and also
 // expands the set of types we can handle to include std::pair.
 template <typename T1, typename T2>
-struct IsOwner<std::pair<T1, T2>>
-    : std::bool_constant<
-          std::conditional_t<std::is_reference_v<T1>, std::false_type,
-                             IsOwner<std::remove_cv_t<T1>>>::value &&
-          std::conditional_t<std::is_reference_v<T2>, std::false_type,
-                             IsOwner<std::remove_cv_t<T2>>>::value> {};
+struct IsOwner<std::pair<T1, T2>> : IsOwner<std::tuple<T1, T2>> {};
 
 template <typename T, typename Traits, typename Alloc>
 struct IsOwner<std::basic_string<T, Traits, Alloc>> : std::true_type {};
@@ -580,6 +600,17 @@
 template <typename T, typename Alloc>
 struct IsOwner<std::vector<T, Alloc>> : std::true_type {};
 
+template <typename... T>
+struct IsOwner<std::tuple<T...>>
+    : std::bool_constant<(sizeof...(T) > 0) &&
+                         // Uses a C++17 fold expression where '...' unpacks the
+                         // parameter pack T, and 'true &&' provides the base
+                         // case for the logical AND operation across all types.
+                         (true && ... && IsOwner<T>::value)> {};
+
+template <typename... T>
+struct IsOwner<std::variant<T...>> : IsOwner<std::tuple<T...>> {};
+
 // Detects if a class's definition has declared itself to be a view by declaring
 //   using absl_internal_is_view = std::true_type;
 // as a member.
@@ -607,16 +638,47 @@
 struct IsView
     : std::bool_constant<std::is_pointer_v<T> || IsViewImpl<T>::value> {};
 
+template <typename T>
+struct IsView<T&> : std::true_type {};
+
+template <typename T>
+struct IsView<T&&> : std::true_type {};
+
+template <typename T>
+struct IsView<const T> : IsView<T> {};
+
+template <typename T>
+struct IsView<volatile T> : IsView<T> {};
+
+template <typename T>
+struct IsView<const volatile T> : IsView<T> {};
+
+template <typename T>
+struct IsView<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T, std::size_t N>
+struct IsView<std::array<T, N>>
+    : std::conditional_t<N != 0, IsView<T>, std::false_type> {};
+
 // This allows incomplete types to be used for associative containers, and also
 // expands the set of types we can handle to include std::pair.
 template <typename T1, typename T2>
-struct IsView<std::pair<T1, T2>>
-    : std::bool_constant<IsView<std::remove_cv_t<T1>>::value &&
-                         IsView<std::remove_cv_t<T2>>::value> {};
+struct IsView<std::pair<T1, T2>> : IsView<std::tuple<T1, T2>> {};
 
 template <typename Char, typename Traits>
 struct IsView<std::basic_string_view<Char, Traits>> : std::true_type {};
 
+template <typename... T>
+struct IsView<std::tuple<T...>>
+    : std::bool_constant<(sizeof...(T) > 0) &&
+                         // Uses a C++17 fold expression where '...' unpacks the
+                         // parameter pack T, and 'true &&' provides the base
+                         // case for the logical AND operation across all types.
+                         (true && ... && IsView<T>::value)> {};
+
+template <typename... T>
+struct IsView<std::variant<T...>> : IsView<std::tuple<T...>> {};
+
 #ifdef __cpp_lib_span
 template <typename T>
 struct IsView<std::span<T>> : std::true_type {};
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index 9734fc1..3675a0f 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -14,11 +14,15 @@
 
 #include "absl/meta/type_traits.h"
 
+#include <array>
 #include <cstdint>
+#include <functional>
 #include <string>
 #include <string_view>
+#include <tuple>
 #include <type_traits>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include "gtest/gtest.h"
@@ -32,6 +36,11 @@
 using ::testing::StaticAssertTypeEq;
 
 template <typename T>
+using IsViewAndNotOwner =
+    std::conjunction<absl::type_traits_internal::IsView<T>,
+                     std::negation<absl::type_traits_internal::IsOwner<T>>>;
+
+template <typename T>
 using IsOwnerAndNotView =
     std::conjunction<absl::type_traits_internal::IsOwner<T>,
                       std::negation<absl::type_traits_internal::IsView<T>>>;
@@ -45,9 +54,9 @@
               "string is an owner, not a view");
 static_assert(IsOwnerAndNotView<std::wstring>::value,
               "wstring is an owner, not a view");
-static_assert(!IsOwnerAndNotView<std::string_view>::value,
+static_assert(IsViewAndNotOwner<std::string_view>::value,
               "string_view is a view, not an owner");
-static_assert(!IsOwnerAndNotView<std::wstring_view>::value,
+static_assert(IsViewAndNotOwner<std::wstring_view>::value,
               "wstring_view is a view, not an owner");
 
 template <class T, class U>
@@ -340,6 +349,104 @@
   static_assert(!absl::is_trivially_relocatable<S>::value, "");
 }
 
+TEST(Ownership, References) {
+  static_assert(IsViewAndNotOwner<std::string&>::value,
+                "std::string& is a view, not an owner");
+  static_assert(IsViewAndNotOwner<int&>::value, "int& is a view, not an owner");
+
+  static_assert(IsViewAndNotOwner<std::string&&>::value,
+                "std::string&& is a view, not an owner");
+  static_assert(IsViewAndNotOwner<int&&>::value,
+                "int&& is a view, not an owner");
+
+  static_assert(IsViewAndNotOwner<std::reference_wrapper<std::string>>::value,
+                "std::reference_wrapper<std::string> is a view, not an owner");
+  static_assert(IsViewAndNotOwner<std::reference_wrapper<int>>::value,
+                "std::reference_wrapper<int> is a view, not an owner");
+}
+
+TEST(Ownership, CVQualifiers) {
+  static_assert(IsOwnerAndNotView<const std::string>::value,
+                "const std::string is an owner, not a view");
+  static_assert(IsViewAndNotOwner<const std::string_view>::value,
+                "const std::string_view is a view, not an owner");
+
+  static_assert(IsOwnerAndNotView<volatile std::string>::value,
+                "volatile std::string is an owner, not a view");
+  static_assert(IsViewAndNotOwner<volatile std::string_view>::value,
+                "volatile std::string_view is a view, not an owner");
+
+  static_assert(IsOwnerAndNotView<const volatile std::string>::value,
+                "const volatile std::string is an owner, not a view");
+  static_assert(IsViewAndNotOwner<const volatile std::string_view>::value,
+                "const volatile std::string_view is a view, not an owner");
+}
+
+TEST(Ownership, Array) {
+  static_assert(!IsOwnerAndNotView<std::array<std::string, 0>>::value,
+                "empty array is not an owner");
+  static_assert(!IsViewAndNotOwner<std::array<std::string, 0>>::value,
+                "empty array is not a view");
+
+  static_assert(!IsOwnerAndNotView<std::array<std::string_view, 0>>::value,
+                "empty array is not an owner");
+  static_assert(!IsViewAndNotOwner<std::array<std::string_view, 0>>::value,
+                "empty array is not a view");
+
+  static_assert(IsOwnerAndNotView<std::array<std::string, 5>>::value,
+                "array of owners is an owner");
+  static_assert(IsViewAndNotOwner<std::array<std::string_view, 5>>::value,
+                "array of views is a view");
+}
+
+TEST(Ownership, Variant) {
+  static_assert(!IsOwnerAndNotView<std::variant<>>::value,
+                "empty variant is not an owner");
+  static_assert(!IsViewAndNotOwner<std::variant<>>::value,
+                "empty variant is not a view");
+
+  static_assert(IsOwnerAndNotView<std::variant<std::string, std::vector<char>,
+                                               std::vector<int>>>::value,
+                "aggregate of owners is an owner");
+  static_assert(IsViewAndNotOwner<
+                    std::variant<std::wstring_view, std::string_view>>::value,
+                "aggregate of views is a view");
+
+  static_assert(
+      !IsOwnerAndNotView<std::variant<const char*, std::string>>::value,
+      "variant of mixed-ownership types is not considered an owner");
+  static_assert(
+      !IsViewAndNotOwner<std::variant<const char*, std::string>>::value,
+      "variant of mixed-ownership types is not considered a view");
+
+  static_assert(
+      IsViewAndNotOwner<std::variant<std::reference_wrapper<const int>>>::value,
+      "variant of reference is considered a view");
+}
+
+TEST(Ownership, Tuple) {
+  static_assert(!IsOwnerAndNotView<std::tuple<>>::value,
+                "empty tuple is not an owner");
+  static_assert(!IsViewAndNotOwner<std::tuple<>>::value,
+                "empty tuple is not a view");
+
+  static_assert(
+      IsOwnerAndNotView<
+          std::tuple<std::string, std::vector<char>, std::vector<int>>>::value,
+      "aggregate of owners is an owner");
+  static_assert(
+      IsViewAndNotOwner<std::tuple<std::wstring_view, std::string_view>>::value,
+      "aggregate of views is a view");
+
+  static_assert(!IsOwnerAndNotView<std::tuple<const char*, std::string>>::value,
+                "tuple of mixed-ownership types is not considered an owner");
+  static_assert(!IsViewAndNotOwner<std::tuple<const char*, std::string>>::value,
+                "tuple of mixed-ownership types is not considered a view");
+
+  static_assert(IsViewAndNotOwner<std::tuple<const int&>>::value,
+                "tuple of reference is considered a view");
+}
+
 #ifdef ABSL_HAVE_CONSTANT_EVALUATED
 
 constexpr int64_t NegateIfConstantEvaluated(int64_t i) {