about summary refs log tree commit diff
path: root/absl/types/optional_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/types/optional_test.cc')
-rw-r--r--absl/types/optional_test.cc50
1 files changed, 50 insertions, 0 deletions
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc
index 25b44b171c9d..65f43871e1c0 100644
--- a/absl/types/optional_test.cc
+++ b/absl/types/optional_test.cc
@@ -24,6 +24,17 @@
 #include "absl/meta/type_traits.h"
 #include "absl/strings/string_view.h"
 
+struct Hashable {};
+
+namespace std {
+template <>
+struct hash<Hashable> {
+  size_t operator()(const Hashable&) { return 0; }
+};
+}  // namespace std
+
+struct NonHashable {};
+
 namespace {
 
 std::string TypeQuals(std::string&) { return "&"; }
@@ -1434,6 +1445,17 @@ TEST(optionalTest, ValueType) {
       (std::is_same<absl::optional<int>::value_type, absl::nullopt_t>::value));
 }
 
+template <typename T>
+struct is_hash_enabled_for {
+  template <typename U, typename = decltype(std::hash<U>()(std::declval<U>()))>
+  static std::true_type test(int);
+
+  template <typename U>
+  static std::false_type test(...);
+
+  static constexpr bool value = decltype(test<T>(0))::value;
+};
+
 TEST(optionalTest, Hash) {
   std::hash<absl::optional<int>> hash;
   std::set<size_t> hashcodes;
@@ -1442,6 +1464,34 @@ TEST(optionalTest, Hash) {
     hashcodes.insert(hash(i));
   }
   EXPECT_GT(hashcodes.size(), 90);
+
+  static_assert(is_hash_enabled_for<absl::optional<int>>::value, "");
+  static_assert(is_hash_enabled_for<absl::optional<Hashable>>::value, "");
+
+#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
+                          _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
+  // For MSVC and libc++ (< 4.0 and c++14), std::hash primary template has a
+  // static_assert to catch any user-defined type that doesn't provide a hash
+  // specialization. So instantiating std::hash<absl::optional<T>> will result
+  // in a hard error which is not SFINAE friendly.
+#define ABSL_STD_HASH_NOT_SFINAE_FRIENDLY 1
+#endif
+
+#ifndef ABSL_STD_HASH_NOT_SFINAE_FRIENDLY
+  static_assert(!is_hash_enabled_for<absl::optional<NonHashable>>::value, "");
+#endif
+
+  // libstdc++ std::optional is missing remove_const_t, i.e. it's using
+  // std::hash<T> rather than std::hash<std::remove_const_t<T>>.
+  // Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82262
+#ifndef __GLIBCXX__
+  static_assert(is_hash_enabled_for<absl::optional<const int>>::value, "");
+  static_assert(is_hash_enabled_for<absl::optional<const Hashable>>::value, "");
+  std::hash<absl::optional<const int>> c_hash;
+  for (int i = 0; i < 100; ++i) {
+    EXPECT_EQ(hash(i), c_hash(i));
+  }
+#endif
 }
 
 struct MoveMeNoThrow {