[libc++] LWG3480: make (recursive_)directory_iterator C++20 ranges

Implement LWG3480 which enables `directory_iterator` and
`recursive_directory_iterator` to be both a `borrowed_range` and a
`view`.

Reviewed By: ldionne, #libc

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

GitOrigin-RevId: 1fa27f2a10e8eec15387aba7eeff65d7d332b9af
diff --git a/docs/Status/Cxx2bIssues.csv b/docs/Status/Cxx2bIssues.csv
index 12a293b..1fb9541 100644
--- a/docs/Status/Cxx2bIssues.csv
+++ b/docs/Status/Cxx2bIssues.csv
@@ -110,7 +110,7 @@
 `3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","",""
 `3422 <https://wg21.link/LWG3422>`__,"Issues of ``seed_seq``'s constructors","October 2021","",""
 `3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case","October 2021","",""
-`3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","October 2021","",""
+`3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","October 2021","|Complete|","14.0"
 `3498 <https://wg21.link/LWG3498>`__,"Inconsistent ``noexcept``-specifiers for ``basic_syncbuf``","October 2021","",""
 `3535 <https://wg21.link/LWG3535>`__,"``join_view::iterator::iterator_category`` and ``::iterator_concept`` lie","October 2021","",""
 `3554 <https://wg21.link/LWG3554>`__,"``chrono::parse`` needs ``const charT*`` and ``basic_string_view<charT>`` overloads","October 2021","",""
diff --git a/docs/Status/RangesIssues.csv b/docs/Status/RangesIssues.csv
index 58367d7..4e5abfa 100644
--- a/docs/Status/RangesIssues.csv
+++ b/docs/Status/RangesIssues.csv
@@ -85,7 +85,7 @@
 `LWG3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel",,
 `LWG3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4",,
 `LWG3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case",,
-`LWG3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges",,
+`LWG3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","|Complete|","14.0"
 `LWG3535 <https://wg21.link/LWG3535>`__,"``join_view::iterator::iterator_category`` and ``::iterator_concept`` lie",,
 `LWG3559 <https://wg21.link/LWG3559>`__,"Semantic requirements of ``sized_range`` is circular",,
 `LWG3560 <https://wg21.link/LWG3560>`__,"``ranges::equal`` and ``ranges::is_permutation`` should short-circuit for ``sized_ranges``",,
diff --git a/include/filesystem b/include/filesystem
index a11d2a4..1fd77af 100644
--- a/include/filesystem
+++ b/include/filesystem
@@ -11,7 +11,7 @@
 /*
     filesystem synopsis
 
-    namespace std { namespace filesystem {
+    namespace std::filesystem {
 
     class path;
 
@@ -48,13 +48,13 @@
 
     // enable directory_iterator range-based for statements
     directory_iterator begin(directory_iterator iter) noexcept;
-    directory_iterator end(const directory_iterator&) noexcept;
+    directory_iterator end(directory_iterator) noexcept;
 
     class recursive_directory_iterator;
 
     // enable recursive_directory_iterator range-based for statements
     recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
-    recursive_directory_iterator end(const recursive_directory_iterator&) noexcept;
+    recursive_directory_iterator end(recursive_directory_iterator) noexcept;
 
     class file_status;
 
@@ -224,14 +224,25 @@
     path weakly_canonical(path const& p);
     path weakly_canonical(path const& p, error_code& ec);
 
+} // namespace std::filesystem
 
-} }  // namespaces std::filesystem
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<std::filesystem::directory_iterator> = true;
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<std::filesystem::recursive_directory_iterator> = true;
+
+template <>
+inline constexpr bool std::ranges::enable_view<std::filesystem::directory_iterator> = true;
+template <>
+inline constexpr bool std::ranges::enable_view<std::filesystem::recursive_directory_iterator> = true;
 
 */
 
 #include <__availability>
 #include <__config>
 #include <__debug>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/enable_view.h>
 #include <__utility/forward.h>
 #include <chrono>
 #include <compare>
@@ -2869,7 +2880,7 @@
 }
 
 inline _LIBCPP_INLINE_VISIBILITY directory_iterator
-end(const directory_iterator&) noexcept {
+end(directory_iterator) noexcept {
   return directory_iterator();
 }
 
@@ -3001,7 +3012,7 @@
 }
 
 inline _LIBCPP_INLINE_VISIBILITY recursive_directory_iterator
-end(const recursive_directory_iterator&) noexcept {
+end(recursive_directory_iterator) noexcept {
   return recursive_directory_iterator();
 }
 
@@ -3009,6 +3020,18 @@
 
 _LIBCPP_END_NAMESPACE_FILESYSTEM
 
+#if !defined(_LIBCPP_HAS_NO_RANGES)
+template <>
+inline constexpr bool _VSTD::ranges::enable_borrowed_range<_VSTD_FS::directory_iterator> = true;
+template <>
+inline constexpr bool _VSTD::ranges::enable_borrowed_range<_VSTD_FS::recursive_directory_iterator> = true;
+
+template <>
+inline constexpr bool _VSTD::ranges::enable_view<_VSTD_FS::directory_iterator> = true;
+template <>
+inline constexpr bool _VSTD::ranges::enable_view<_VSTD_FS::recursive_directory_iterator> = true;
+#endif
+
 #endif // !_LIBCPP_CXX03_LANG
 
 _LIBCPP_POP_MACROS
diff --git a/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp b/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp
index be8a1be..f6f0bbe 100644
--- a/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp
+++ b/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.nonmembers/begin_end.pass.cpp
@@ -30,12 +30,16 @@
 
 TEST_CASE(test_function_signatures)
 {
-    directory_iterator d; ((void)d);
+    directory_iterator d;
 
     ASSERT_SAME_TYPE(decltype(begin(d)), directory_iterator);
+    ASSERT_SAME_TYPE(decltype(begin(std::move(d))), directory_iterator);
+    ASSERT_NOEXCEPT(begin(d));
     ASSERT_NOEXCEPT(begin(std::move(d)));
 
     ASSERT_SAME_TYPE(decltype(end(d)), directory_iterator);
+    ASSERT_SAME_TYPE(decltype(end(std::move(d))), directory_iterator);
+    ASSERT_NOEXCEPT(end(d));
     ASSERT_NOEXCEPT(end(std::move(d)));
 }
 
diff --git a/test/std/input.output/filesystems/class.directory_iterator/range_concept_conformance.compile.pass.cpp b/test/std/input.output/filesystems/class.directory_iterator/range_concept_conformance.compile.pass.cpp
index 3218f20..fd28f16 100644
--- a/test/std/input.output/filesystems/class.directory_iterator/range_concept_conformance.compile.pass.cpp
+++ b/test/std/input.output/filesystems/class.directory_iterator/range_concept_conformance.compile.pass.cpp
@@ -9,45 +9,35 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
-// XFAIL: *
 
-// directory_iterator, recursive_directory_iterator
+// directory_iterator
 
 #include "filesystem_include.h"
 
 #include <concepts>
 #include <ranges>
 
-
-
 static_assert(std::same_as<std::ranges::iterator_t<fs::directory_iterator>, fs::directory_iterator>);
 static_assert(std::ranges::common_range<fs::directory_iterator>);
 static_assert(std::ranges::input_range<fs::directory_iterator>);
-static_assert(!std::ranges::view<fs::directory_iterator>);
+static_assert(std::ranges::view<fs::directory_iterator>);
 static_assert(!std::ranges::sized_range<fs::directory_iterator>);
-static_assert(!std::ranges::borrowed_range<fs::directory_iterator>);
-static_assert(!std::ranges::viewable_range<fs::directory_iterator>);
+static_assert(std::ranges::borrowed_range<fs::directory_iterator>);
+static_assert(std::ranges::viewable_range<fs::directory_iterator>);
 
-static_assert(std::same_as<std::ranges::iterator_t<fs::directory_iterator const>, fs::directory_iterator>);
-static_assert(std::ranges::common_range<fs::directory_iterator const>);
-static_assert(std::ranges::input_range<fs::directory_iterator const>);
-static_assert(!std::ranges::view<fs::directory_iterator const>);
-static_assert(!std::ranges::sized_range<fs::directory_iterator const>);
-static_assert(!std::ranges::borrowed_range<fs::directory_iterator const>);
-static_assert(!std::ranges::viewable_range<fs::directory_iterator const>);
+static_assert(std::same_as<std::ranges::iterator_t<fs::directory_iterator&>, fs::directory_iterator>);
+static_assert(std::ranges::common_range<fs::directory_iterator&>);
+static_assert(std::ranges::input_range<fs::directory_iterator&>);
+static_assert(!std::ranges::view<fs::directory_iterator&>);
+static_assert(!std::ranges::sized_range<fs::directory_iterator&>);
+static_assert(std::ranges::borrowed_range<fs::directory_iterator&>);
+static_assert(std::ranges::viewable_range<fs::directory_iterator&>);
 
-static_assert(std::same_as<std::ranges::iterator_t<fs::recursive_directory_iterator>, fs::recursive_directory_iterator>);
-static_assert(std::ranges::common_range<fs::recursive_directory_iterator>);
-static_assert(std::ranges::input_range<fs::recursive_directory_iterator>);
-static_assert(!std::ranges::view<fs::recursive_directory_iterator>);
-static_assert(!std::ranges::sized_range<fs::recursive_directory_iterator>);
-static_assert(!std::ranges::borrowed_range<fs::recursive_directory_iterator>);
-static_assert(!std::ranges::viewable_range<fs::recursive_directory_iterator>);
+static_assert(std::same_as<std::ranges::iterator_t<const fs::directory_iterator&>, fs::directory_iterator>);
+static_assert(std::ranges::common_range<const fs::directory_iterator&>);
+static_assert(std::ranges::input_range<const fs::directory_iterator&>);
+static_assert(!std::ranges::view<const fs::directory_iterator&>);
+static_assert(!std::ranges::sized_range<const fs::directory_iterator&>);
+static_assert(std::ranges::borrowed_range<const fs::directory_iterator&>);
+static_assert(std::ranges::viewable_range<const fs::directory_iterator&>);
 
-static_assert(std::same_as<std::ranges::iterator_t<fs::recursive_directory_iterator const>, fs::recursive_directory_iterator>);
-static_assert(std::ranges::common_range<fs::recursive_directory_iterator const>);
-static_assert(std::ranges::input_range<fs::recursive_directory_iterator const>);
-static_assert(!std::ranges::view<fs::recursive_directory_iterator const>);
-static_assert(!std::ranges::sized_range<fs::recursive_directory_iterator const>);
-static_assert(!std::ranges::borrowed_range<fs::recursive_directory_iterator const>);
-static_assert(!std::ranges::viewable_range<fs::recursive_directory_iterator const>);
diff --git a/test/std/input.output/filesystems/class.rec.dir.itr/range_concept_conformance.compile.pass.cpp b/test/std/input.output/filesystems/class.rec.dir.itr/range_concept_conformance.compile.pass.cpp
new file mode 100644
index 0000000..834e746
--- /dev/null
+++ b/test/std/input.output/filesystems/class.rec.dir.itr/range_concept_conformance.compile.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// recursive_directory_iterator
+
+#include "filesystem_include.h"
+
+#include <concepts>
+#include <ranges>
+
+static_assert(std::same_as<std::ranges::iterator_t<fs::recursive_directory_iterator>, fs::recursive_directory_iterator>);
+static_assert(std::ranges::common_range<fs::recursive_directory_iterator>);
+static_assert(std::ranges::input_range<fs::recursive_directory_iterator>);
+static_assert(std::ranges::view<fs::recursive_directory_iterator>);
+static_assert(!std::ranges::sized_range<fs::recursive_directory_iterator>);
+static_assert(std::ranges::borrowed_range<fs::recursive_directory_iterator>);
+static_assert(std::ranges::viewable_range<fs::recursive_directory_iterator>);
+
+static_assert(std::same_as<std::ranges::iterator_t<fs::recursive_directory_iterator&>, fs::recursive_directory_iterator>);
+static_assert(std::ranges::common_range<fs::recursive_directory_iterator&>);
+static_assert(std::ranges::input_range<fs::recursive_directory_iterator&>);
+static_assert(!std::ranges::view<fs::recursive_directory_iterator&>);
+static_assert(!std::ranges::sized_range<fs::recursive_directory_iterator&>);
+static_assert(std::ranges::borrowed_range<fs::recursive_directory_iterator&>);
+static_assert(std::ranges::viewable_range<fs::recursive_directory_iterator&>);
+
+static_assert(std::same_as<std::ranges::iterator_t<const fs::recursive_directory_iterator&>, fs::recursive_directory_iterator>);
+static_assert(std::ranges::common_range<const fs::recursive_directory_iterator&>);
+static_assert(std::ranges::input_range<const fs::recursive_directory_iterator&>);
+static_assert(!std::ranges::view<const fs::recursive_directory_iterator&>);
+static_assert(!std::ranges::sized_range<const fs::recursive_directory_iterator&>);
+static_assert(std::ranges::borrowed_range<const fs::recursive_directory_iterator&>);
+static_assert(std::ranges::viewable_range<const fs::recursive_directory_iterator&>);
diff --git a/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp b/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp
index c4bb5a7..104e419 100644
--- a/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp
+++ b/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp
@@ -30,12 +30,16 @@
 
 TEST_CASE(test_function_signatures)
 {
-    recursive_directory_iterator d; ((void)d);
+    recursive_directory_iterator d;
 
     ASSERT_SAME_TYPE(decltype(begin(d)), recursive_directory_iterator);
+    ASSERT_SAME_TYPE(decltype(begin(std::move(d))), recursive_directory_iterator);
+    ASSERT_NOEXCEPT(begin(d));
     ASSERT_NOEXCEPT(begin(std::move(d)));
 
     ASSERT_SAME_TYPE(decltype(end(d)), recursive_directory_iterator);
+    ASSERT_SAME_TYPE(decltype(end(std::move(d))), recursive_directory_iterator);
+    ASSERT_NOEXCEPT(end(d));
     ASSERT_NOEXCEPT(end(std::move(d)));
 }
 
diff --git a/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_borrowed_range.compile.pass.cpp b/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 0000000..e59672a
--- /dev/null
+++ b/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <filesystem>
+
+// template <>
+// inline constexpr bool ranges::enable_borrowed_range<filesystem::directory_iterator> = true;
+// template <>
+// inline constexpr bool ranges::enable_borrowed_range<filesystem::recursive_directory_iterator> = true;
+
+#include <filesystem>
+#include <ranges>
+
+template<class Range>
+void test() {
+  static_assert(std::ranges::enable_borrowed_range<Range>);
+  static_assert(!std::ranges::enable_borrowed_range<Range&>);
+  static_assert(!std::ranges::enable_borrowed_range<const Range>);
+}
+
+void test() {
+  test<std::filesystem::directory_iterator>();
+  test<std::filesystem::recursive_directory_iterator>();
+}
diff --git a/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_view.compile.pass.cpp b/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_view.compile.pass.cpp
new file mode 100644
index 0000000..336d522
--- /dev/null
+++ b/test/std/input.output/filesystems/fs.filesystem.synopsis/enable_view.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <filesystem>
+
+// template <>
+// inline constexpr bool ranges::enable_view<filesystem::directory_iterator> = true;
+// template <>
+// inline constexpr bool ranges::enable_view<filesystem::recursive_directory_iterator> = true;
+
+#include <filesystem>
+#include <ranges>
+
+template<class Range>
+void test() {
+  static_assert(std::ranges::enable_view<Range>);
+  static_assert(!std::ranges::enable_view<Range&>);
+  static_assert(!std::ranges::enable_view<const Range>);
+}
+
+void test() {
+  test<std::filesystem::directory_iterator>();
+  test<std::filesystem::recursive_directory_iterator>();
+}