Provide AbslHashValue for std::filesystem::path in C++17

This is somewhat tricky to implement because path equality is not
straightforward. See
https://github.com/abseil/abseil-cpp/pull/1560#issuecomment-1799983471
for discussion.

This re-lands 3bd86026c93da5a40006fd53403dff9d5f5e30e3 with a fix
for iOS 13 unavailability of std::filesystem::path.

Roll-forward of 524ebb7ea91d2955dc4c68c7e6fd2bed620621b5.

Closes #655
Closes #1560

PiperOrigin-RevId: 583365100
Change-Id: Id49735c49d123e0cd6a620a2b5b5a12d94129f94
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 111f375..59fe8de 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -48,6 +48,10 @@
 #include "absl/types/optional.h"
 #include "absl/types/variant.h"
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#include <filesystem>  // NOLINT
+#endif
+
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #include <string_view>
 #endif
@@ -480,6 +484,43 @@
 #endif
 }
 
+TEST(HashValueTest, StdFilesystemPath) {
+#ifndef ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+  GTEST_SKIP() << "std::filesystem::path is unavailable on this platform";
+#else
+  EXPECT_TRUE((is_hashable<std::filesystem::path>::value));
+
+  // clang-format off
+  const auto kTestCases = std::make_tuple(
+      std::filesystem::path(),
+      std::filesystem::path("/"),
+#ifndef __GLIBCXX__
+      // libstdc++ has a known issue normalizing "//".
+      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106452
+      std::filesystem::path("//"),
+#endif
+      std::filesystem::path("/a/b"),
+      std::filesystem::path("/a//b"),
+      std::filesystem::path("a/b"),
+      std::filesystem::path("a/b/"),
+      std::filesystem::path("a//b"),
+      std::filesystem::path("a//b/"),
+      std::filesystem::path("c:/"),
+      std::filesystem::path("c:\\"),
+      std::filesystem::path("c:\\/"),
+      std::filesystem::path("c:\\//"),
+      std::filesystem::path("c://"),
+      std::filesystem::path("c://\\"),
+      std::filesystem::path("/e/p"),
+      std::filesystem::path("/s/../e/p"),
+      std::filesystem::path("e/p"),
+      std::filesystem::path("s/../e/p"));
+  // clang-format on
+
+  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(kTestCases));
+#endif
+}
+
 TEST(HashValueTest, StdArray) {
   EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
 
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index e97cb31..c94a621 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -19,6 +19,11 @@
 #ifndef ABSL_HASH_INTERNAL_HASH_H_
 #define ABSL_HASH_INTERNAL_HASH_H_
 
+#ifdef __APPLE__
+#include <Availability.h>
+#include <TargetConditionals.h>
+#endif
+
 #include <algorithm>
 #include <array>
 #include <bitset>
@@ -56,6 +61,10 @@
 #include "absl/types/variant.h"
 #include "absl/utility/utility.h"
 
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#include <filesystem>  // NOLINT
+#endif
+
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #include <string_view>
 #endif
@@ -578,6 +587,28 @@
 
 #endif  // ABSL_HAVE_STD_STRING_VIEW
 
+#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
+    (!defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) ||        \
+     __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000)
+
+#define ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE 1
+
+// Support std::filesystem::path. The SFINAE is required because some string
+// types are implicitly convertible to std::filesystem::path.
+template <typename Path, typename H,
+          typename = absl::enable_if_t<
+              std::is_same_v<Path, std::filesystem::path>>>
+H AbslHashValue(H hash_state, const Path& path) {
+  // This is implemented by deferring to the standard library to compute the
+  // hash.  The standard library requires that for two paths, `p1 == p2`, then
+  // `hash_value(p1) == hash_value(p2)`. `AbslHashValue` has the same
+  // requirement. Since `operator==` does platform specific matching, deferring
+  // to the standard library is the simplest approach.
+  return H::combine(std::move(hash_state), std::filesystem::hash_value(path));
+}
+
+#endif  // ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+
 // -----------------------------------------------------------------------------
 // AbslHashValue for Sequence Containers
 // -----------------------------------------------------------------------------