[libcxx-commits] [libcxx] 9d982c6 - [libcxx][ranges] Add `ranges::reverse_view`.

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 9 15:10:08 PDT 2021


Author: zoecarver
Date: 2021-08-09T15:09:59-07:00
New Revision: 9d982c67ba01ef5fcee1e2ca946a8bd0b6747ac0

URL: https://github.com/llvm/llvm-project/commit/9d982c67ba01ef5fcee1e2ca946a8bd0b6747ac0
DIFF: https://github.com/llvm/llvm-project/commit/9d982c67ba01ef5fcee1e2ca946a8bd0b6747ac0.diff

LOG: [libcxx][ranges] Add `ranges::reverse_view`.

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

Added: 
    libcxx/include/__ranges/reverse_view.h
    libcxx/test/libcxx/diagnostics/detail.headers/ranges/reverse_view.module.verify.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/base.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/borrowing.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/ctad.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.view.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/end.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/range_concept_conformance.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/size.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.reverse/types.h

Modified: 
    libcxx/docs/Status/RangesPaper.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/module.modulemap
    libcxx/include/ranges

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index 29ad8c3ad3ff0..daa7668166096 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -146,4 +146,4 @@ Section,Description,Dependencies,Assignee,Complete
 `[range.split] <http://wg21.link/range.split>`_,split_view,[range.all],Zoe Carver,In Progress
 `[range.counted] <http://wg21.link/range.counted>`_,view::counted,[range.subrange],Zoe Carver,Not started
 `[range.common] <http://wg21.link/range.common>`_,common_view,[range.all],Zoe Carver,✅
-`[range.reverse] <http://wg21.link/range.reverse>`_,reverse_view,[range.all],Unassigned,Not started
+`[range.reverse] <http://wg21.link/range.reverse>`_,reverse_view,[range.all],Zoe Carver,✅

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 1be2df1b5c381..765c193339127 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -199,6 +199,8 @@ set(files
   __ranges/enable_view.h
   __ranges/non_propagating_cache.h
   __ranges/ref_view.h
+  __ranges/reverse_view.h
+  __ranges/take_view.h
   __ranges/single_view.h
   __ranges/size.h
   __ranges/subrange.h

diff  --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h
new file mode 100644
index 0000000000000..25d3d6568cd57
--- /dev/null
+++ b/libcxx/include/__ranges/reverse_view.h
@@ -0,0 +1,112 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+#ifndef _LIBCPP___RANGES_REVERSE_VIEW_H
+#define _LIBCPP___RANGES_REVERSE_VIEW_H
+
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <concepts>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_RANGES)
+
+namespace ranges {
+  template<view _View>
+    requires bidirectional_range<_View>
+  class reverse_view : public view_interface<reverse_view<_View>> {
+    // We cache begin() whenever ranges::next is not guaranteed O(1) to provide an
+    // amortized O(1) begin() method.
+    static constexpr bool _UseCache = !random_access_range<_View> && !common_range<_View>;
+    using _Cache = _If<_UseCache, __non_propagating_cache<reverse_iterator<iterator_t<_View>>>, __empty_cache>;
+    [[no_unique_address]] _Cache __cached_begin_ = _Cache();
+    [[no_unique_address]] _View __base_ = _View();
+
+  public:
+    _LIBCPP_HIDE_FROM_ABI
+    reverse_view() requires default_initializable<_View> = default;
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr explicit reverse_view(_View __view) : __base_(_VSTD::move(__view)) {}
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr _View base() const& requires copy_constructible<_View> { return __base_; }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr _View base() && { return _VSTD::move(__base_); }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr reverse_iterator<iterator_t<_View>> begin() {
+      if constexpr (_UseCache)
+        if (__cached_begin_.__has_value())
+          return *__cached_begin_;
+
+      auto __tmp = _VSTD::make_reverse_iterator(ranges::next(ranges::begin(__base_), ranges::end(__base_)));
+      if constexpr (_UseCache)
+        __cached_begin_.__set(__tmp);
+      return __tmp;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr reverse_iterator<iterator_t<_View>> begin() requires common_range<_View> {
+      return _VSTD::make_reverse_iterator(ranges::end(__base_));
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr auto begin() const requires common_range<const _View> {
+      return _VSTD::make_reverse_iterator(ranges::end(__base_));
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr reverse_iterator<iterator_t<_View>> end() {
+      return _VSTD::make_reverse_iterator(ranges::begin(__base_));
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr auto end() const requires common_range<const _View> {
+      return _VSTD::make_reverse_iterator(ranges::begin(__base_));
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr auto size() requires sized_range<_View> {
+      return ranges::size(__base_);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI
+    constexpr auto size() const requires sized_range<const _View> {
+      return ranges::size(__base_);
+    }
+  };
+
+  template<class _Range>
+  reverse_view(_Range&&) -> reverse_view<views::all_t<_Range>>;
+
+  template<class _Tp>
+  inline constexpr bool enable_borrowed_range<reverse_view<_Tp>> = enable_borrowed_range<_Tp>;
+} // namespace ranges
+
+#endif // !defined(_LIBCPP_HAS_NO_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_REVERSE_VIEW_H

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index ee6f3c3e0a4d4..7f8c3bcc9d707 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -631,6 +631,7 @@ module std [system] {
       module enable_view            { private header "__ranges/enable_view.h"           }
       module non_propagating_cache  { private header "__ranges/non_propagating_cache.h" }
       module ref_view               { private header "__ranges/ref_view.h"              }
+      module reverse_view           { private header "__ranges/reverse_view.h"          }
       module size                   { private header "__ranges/size.h"                  }
       module single_view            { private header "__ranges/single_view.h"           }
       module subrange               { private header "__ranges/subrange.h"              }

diff  --git a/libcxx/include/ranges b/libcxx/include/ranges
index cd29aeee92cc5..a2243138531cc 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -154,6 +154,14 @@ namespace std::ranges {
     requires (!common_range<V> && copyable<iterator_t<V>>)
   class common_view;
 
+ // [range.reverse], reverse view
+  template<view V>
+    requires bidirectional_range<V>
+  class reverse_view;
+
+  template<class T>
+    inline constexpr bool enable_borrowed_range<reverse_view<T>> = enable_borrowed_range<T>;
+
   template<class T>
   inline constexpr bool enable_borrowed_range<common_view<T>> = enable_borrowed_range<T>;
 
@@ -188,6 +196,8 @@ namespace std::ranges {
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/enable_view.h>
 #include <__ranges/ref_view.h>
+#include <__ranges/reverse_view.h>
+#include <__ranges/take_view.h>
 #include <__ranges/single_view.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>

diff  --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/reverse_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/reverse_view.module.verify.cpp
new file mode 100644
index 0000000000000..66c8f84ee44e8
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/reverse_view.module.verify.cpp
@@ -0,0 +1,16 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__ranges/reverse_view.h'}}
+#include <__ranges/reverse_view.h>

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/base.pass.cpp
new file mode 100644
index 0000000000000..3c59ffedb3327
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/base.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr V base() const& requires copy_constructible<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test common ranges.
+  {
+    // Test non-const.
+    {
+      auto rev = std::ranges::reverse_view(BidirRange{buffer});
+      assert(rev.base().ptr_ == buffer);
+      assert(std::move(rev).base().ptr_ == buffer);
+
+      ASSERT_SAME_TYPE(decltype(rev.base()), BidirRange);
+      ASSERT_SAME_TYPE(decltype(std::move(rev).base()), BidirRange);
+    }
+    // Test const.
+    {
+      const auto rev = std::ranges::reverse_view(BidirRange{buffer});
+      assert(rev.base().ptr_ == buffer);
+      assert(std::move(rev).base().ptr_ == buffer);
+
+      ASSERT_SAME_TYPE(decltype(rev.base()), BidirRange);
+      ASSERT_SAME_TYPE(decltype(std::move(rev).base()), BidirRange);
+    }
+  }
+  // Test non-common ranges.
+  {
+    // Test non-const (also move only).
+    {
+      auto rev = std::ranges::reverse_view(BidirSentRange<MoveOnly>{buffer});
+      assert(std::move(rev).base().ptr_ == buffer);
+
+      ASSERT_SAME_TYPE(decltype(std::move(rev).base()), BidirSentRange<MoveOnly>);
+    }
+    // Test const.
+    {
+      const auto rev = std::ranges::reverse_view(BidirSentRange<Copyable>{buffer});
+      assert(rev.base().ptr_ == buffer);
+      assert(std::move(rev).base().ptr_ == buffer);
+
+      ASSERT_SAME_TYPE(decltype(rev.base()), BidirSentRange<Copyable>);
+      ASSERT_SAME_TYPE(decltype(std::move(rev).base()), BidirSentRange<Copyable>);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/begin.pass.cpp
new file mode 100644
index 0000000000000..779bef8386fb1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/begin.pass.cpp
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr reverse_iterator<iterator_t<V>> begin();
+// constexpr reverse_iterator<iterator_t<V>> begin() requires common_range<V>;
+// constexpr auto begin() const requires common_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+static int globalCount = 0;
+
+struct CountedIter {
+    typedef std::bidirectional_iterator_tag iterator_category;
+    typedef int                             value_type;
+    typedef std::ptr
diff _t                  
diff erence_type;
+    typedef int*                            pointer;
+    typedef int&                            reference;
+    typedef CountedIter                     self;
+
+    pointer ptr_;
+    CountedIter(pointer ptr) : ptr_(ptr) {}
+    CountedIter() = default;
+
+    reference operator*() const;
+    pointer operator->() const;
+    auto operator<=>(const self&) const = default;
+
+    self& operator++() { globalCount++; ++ptr_; return *this; }
+    self operator++(int) {
+      auto tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    self& operator--();
+    self operator--(int);
+};
+
+struct CountedView : std::ranges::view_base {
+  int *ptr_;
+
+  CountedView(int *ptr) : ptr_(ptr) {}
+
+  auto begin() { return CountedIter(ptr_); }
+  auto begin() const { return CountedIter(ptr_); }
+  auto end() { return sentinel_wrapper<CountedIter>(CountedIter(ptr_ + 8)); }
+  auto end() const { return sentinel_wrapper<CountedIter>(CountedIter(ptr_ + 8)); }
+};
+
+struct RASentRange : std::ranges::view_base {
+  using sent_t = sentinel_wrapper<random_access_iterator<int*>>;
+  using sent_const_t = sentinel_wrapper<random_access_iterator<const int*>>;
+
+  int *ptr_;
+
+  constexpr RASentRange(int *ptr) : ptr_(ptr) {}
+
+  constexpr random_access_iterator<int*> begin() { return random_access_iterator<int*>{ptr_}; }
+  constexpr random_access_iterator<const int*> begin() const { return random_access_iterator<const int*>{ptr_}; }
+  constexpr sent_t end() { return sent_t{random_access_iterator<int*>{ptr_ + 8}}; }
+  constexpr sent_const_t end() const { return sent_const_t{random_access_iterator<const int*>{ptr_ + 8}}; }
+};
+
+template<class T>
+concept BeginInvocable = requires(T t) { t.begin(); };
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Common bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirRange{buffer});
+    assert(rev.begin().base().base() == buffer + 8);
+    assert(std::move(rev).begin().base().base() == buffer + 8);
+
+    ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+  // Const common bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirRange{buffer});
+    assert(rev.begin().base().base() == buffer + 8);
+    assert(std::move(rev).begin().base().base() == buffer + 8);
+
+    ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<const int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<const int*>>);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSentRange<MoveOnly>{buffer});
+    assert(std::move(rev).begin().base().base() == buffer + 8);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSentRange<Copyable>{buffer});
+    assert(rev.begin().base().base() == buffer + 8);
+    assert(std::move(rev).begin().base().base() == buffer + 8);
+
+    ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+  // Non-common random access range.
+  // Note: const overload invalid for non-common ranges, though it would not be imposible
+  // to implement for random access ranges.
+  {
+    auto rev = std::ranges::reverse_view(RASentRange{buffer});
+    assert(rev.begin().base().base() == buffer + 8);
+    assert(std::move(rev).begin().base().base() == buffer + 8);
+
+    ASSERT_SAME_TYPE(decltype(rev.begin()), std::reverse_iterator<random_access_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).begin()), std::reverse_iterator<random_access_iterator<int*>>);
+  }
+  {
+    static_assert( BeginInvocable<      std::ranges::reverse_view<BidirSentRange<Copyable>>>);
+    static_assert(!BeginInvocable<const std::ranges::reverse_view<BidirSentRange<Copyable>>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  {
+    // Make sure we cache begin.
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    CountedView view{buffer};
+    std::ranges::reverse_view rev(view);
+    assert(rev.begin().base().ptr_ == buffer + 8);
+    assert(globalCount == 8);
+    assert(rev.begin().base().ptr_ == buffer + 8);
+    assert(globalCount == 8);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..ff211ae2ea1ae
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/borrowing.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<class T>
+//   inline constexpr bool enable_borrowed_range<reverse_view<T>> = enable_borrowed_range<T>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  friend int* begin(View&);
+  friend int* begin(View const&);
+  friend sentinel_wrapper<int*> end(View&);
+  friend sentinel_wrapper<int*> end(View const&);
+};
+
+struct BorrowableView : std::ranges::view_base {
+  friend int* begin(BorrowableView&);
+  friend int* begin(BorrowableView const&);
+  friend sentinel_wrapper<int*> end(BorrowableView&);
+  friend sentinel_wrapper<int*> end(BorrowableView const&);
+};
+
+template<>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowableView> = true;
+
+static_assert(!std::ranges::enable_borrowed_range<std::ranges::reverse_view<View>>);
+static_assert( std::ranges::enable_borrowed_range<std::ranges::reverse_view<BorrowableView>>);

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..082867d39b8e8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctad.compile.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<class R>
+//   reverse_view(R&&) -> reverse_view<views::all_t<R>>;
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  friend int* begin(View&);
+  friend int* begin(View const&);
+  friend sentinel_wrapper<int*> end(View&);
+  friend sentinel_wrapper<int*> end(View const&);
+};
+
+struct Range {
+  friend int* begin(Range&);
+  friend int* begin(Range const&);
+  friend sentinel_wrapper<int*> end(Range&);
+  friend sentinel_wrapper<int*> end(Range const&);
+};
+
+struct BorrowedRange {
+  friend int* begin(BorrowedRange&);
+  friend int* begin(BorrowedRange const&);
+  friend sentinel_wrapper<int*> end(BorrowedRange&);
+  friend sentinel_wrapper<int*> end(BorrowedRange const&);
+};
+
+template<>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange> = true;
+
+void testCTAD() {
+    View v;
+    Range r;
+    BorrowedRange br;
+    static_assert(std::same_as<
+        decltype(std::ranges::reverse_view(v)),
+        std::ranges::reverse_view<View>
+    >);
+    static_assert(std::same_as<
+        decltype(std::ranges::reverse_view(r)),
+        std::ranges::reverse_view<std::ranges::ref_view<Range>>
+    >);
+    // std::ranges::reverse_view(std::move(r)) invalid. RValue range must be borrowed.
+    static_assert(std::same_as<
+        decltype(std::ranges::reverse_view(br)),
+        std::ranges::reverse_view<std::ranges::ref_view<BorrowedRange>>
+    >);
+    static_assert(std::same_as<
+        decltype(std::ranges::reverse_view(std::move(br))),
+        std::ranges::reverse_view<std::ranges::subrange<
+          int *, sentinel_wrapper<int *>, std::ranges::subrange_kind::unsized>>
+    >);
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..18974346a86b4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.default.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// reverse_view() requires default_­initializable<V> = default;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+enum CtorKind { DefaultCtor, PtrCtor };
+template<CtorKind CK>
+struct BidirRangeWith : std::ranges::view_base {
+  int *ptr_ = nullptr;
+
+  constexpr BidirRangeWith() requires (CK == DefaultCtor) = default;
+  constexpr BidirRangeWith(int *ptr);
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+};
+
+constexpr bool test() {
+  {
+    static_assert( std::default_initializable<std::ranges::reverse_view<BidirRangeWith<DefaultCtor>>>);
+    static_assert(!std::default_initializable<std::ranges::reverse_view<BidirRangeWith<PtrCtor>>>);
+  }
+
+  {
+    std::ranges::reverse_view<BidirRangeWith<DefaultCtor>> rev;
+    assert(rev.base().ptr_ == nullptr);
+  }
+  {
+    const std::ranges::reverse_view<BidirRangeWith<DefaultCtor>> rev;
+    assert(rev.base().ptr_ == nullptr);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
+

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..647fc3dc5e1d2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/ctor.view.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr explicit reverse_view(V r);
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    BidirRange r{buffer};
+    std::ranges::reverse_view<BidirRange> rev(r);
+    assert(rev.base().ptr_ == buffer);
+  }
+  {
+    const BidirRange r{buffer};
+    const std::ranges::reverse_view<BidirRange> rev(r);
+    assert(rev.base().ptr_ == buffer);
+  }
+  {
+    std::ranges::reverse_view<BidirSentRange<MoveOnly>> rev(BidirSentRange<MoveOnly>{buffer});
+    assert(std::move(rev).base().ptr_ == buffer);
+  }
+  {
+    const std::ranges::reverse_view<BidirSentRange<Copyable>> rev(BidirSentRange<Copyable>{buffer});
+    assert(rev.base().ptr_ == buffer);
+  }
+  {
+    // Make sure this ctor is marked as "explicit".
+    static_assert( std::is_constructible_v<std::ranges::reverse_view<BidirRange>, BidirRange>);
+    static_assert(!std::is_convertible_v<std::ranges::reverse_view<BidirRange>, BidirRange>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/end.pass.cpp
new file mode 100644
index 0000000000000..eb430d16bc100
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/end.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr reverse_iterator<iterator_t<V>> end();
+// constexpr auto end() const requires common_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Common bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirRange{buffer});
+    assert(rev.end().base().base() == buffer);
+    assert(std::move(rev).end().base().base() == buffer);
+
+    ASSERT_SAME_TYPE(decltype(rev.end()), std::reverse_iterator<bidirectional_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).end()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+  // Const common bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirRange{buffer});
+    assert(rev.end().base().base() == buffer);
+    assert(std::move(rev).end().base().base() == buffer);
+
+    ASSERT_SAME_TYPE(decltype(rev.end()), std::reverse_iterator<bidirectional_iterator<const int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).end()), std::reverse_iterator<bidirectional_iterator<const int*>>);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSentRange<MoveOnly>{buffer});
+    assert(std::move(rev).end().base().base() == buffer);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).end()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+  // Non-common, const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSentRange<Copyable>{buffer});
+    assert(rev.end().base().base() == buffer);
+    assert(std::move(rev).end().base().base() == buffer);
+
+    ASSERT_SAME_TYPE(decltype(rev.end()), std::reverse_iterator<bidirectional_iterator<int*>>);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).end()), std::reverse_iterator<bidirectional_iterator<int*>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/range_concept_conformance.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/range_concept_conformance.compile.pass.cpp
new file mode 100644
index 0000000000000..e41c173e07c49
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/range_concept_conformance.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// Test that reverse_view conforms to range and view concepts.
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+static_assert( std::ranges::bidirectional_range<std::ranges::reverse_view<test_view<bidirectional_iterator>>>);
+static_assert( std::ranges::random_access_range<std::ranges::reverse_view<test_view<random_access_iterator>>>);
+static_assert( std::ranges::random_access_range<std::ranges::reverse_view<test_view<contiguous_iterator>>>);
+static_assert(!std::ranges::contiguous_range<std::ranges::reverse_view<test_view<contiguous_iterator>>>);
+
+static_assert(std::ranges::view<std::ranges::reverse_view<test_view<bidirectional_iterator>>>);

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/size.pass.cpp
new file mode 100644
index 0000000000000..6a86e2c283562
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/size.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr auto size() requires sized_range<V>;
+// constexpr auto size() const requires sized_range<const V>;
+
+#include <ranges>
+#include <cassert>
+
+#include "test_macros.h"
+#include "types.h"
+
+// end -  begin = 8, but size may return something else.
+template<CopyCategory CC>
+struct BidirSizedRange : std::ranges::view_base {
+  int *ptr_;
+  size_t size_;
+
+  constexpr BidirSizedRange(int *ptr, size_t size) : ptr_(ptr), size_(size) {}
+  constexpr BidirSizedRange(const BidirSizedRange &) requires (CC == Copyable) = default;
+  constexpr BidirSizedRange(BidirSizedRange &&) requires (CC == MoveOnly) = default;
+  constexpr BidirSizedRange& operator=(const BidirSizedRange &) requires (CC == Copyable) = default;
+  constexpr BidirSizedRange& operator=(BidirSizedRange &&) requires (CC == MoveOnly) = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+
+  constexpr size_t size() const { return size_; }
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Non-common, non-const bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::size(rev) == 4);
+    assert(rev.size() == 4);
+    assert(std::move(rev).size() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.size()), size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).size()), size_t);
+  }
+  // Non-common, const bidirectional range.
+  {
+    const auto rev = std::ranges::reverse_view(BidirSizedRange<Copyable>{buffer, 4});
+    assert(std::ranges::size(rev) == 4);
+    assert(rev.size() == 4);
+    assert(std::move(rev).size() == 4);
+
+    ASSERT_SAME_TYPE(decltype(rev.size()), size_t);
+    ASSERT_SAME_TYPE(decltype(std::move(rev).size()), size_t);
+  }
+  // Non-common, non-const (move only) bidirectional range.
+  {
+    auto rev = std::ranges::reverse_view(BidirSizedRange<MoveOnly>{buffer, 4});
+    assert(std::move(rev).size() == 4);
+
+    ASSERT_SAME_TYPE(decltype(std::move(rev).size()), size_t);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/types.h b/libcxx/test/std/ranges/range.adaptors/range.reverse/types.h
new file mode 100644
index 0000000000000..08454607f0c79
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/types.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_REVERSE_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_REVERSE_TYPES_H
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+struct BidirRange : std::ranges::view_base {
+  int *ptr_;
+
+  constexpr BidirRange(int *ptr) : ptr_(ptr) {}
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{ptr_ + 8}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{ptr_ + 8}; }
+};
+
+enum CopyCategory { MoveOnly, Copyable };
+template<CopyCategory CC>
+struct BidirSentRange : std::ranges::view_base {
+  using sent_t = sentinel_wrapper<bidirectional_iterator<int*>>;
+  using sent_const_t = sentinel_wrapper<bidirectional_iterator<const int*>>;
+
+  int *ptr_;
+
+  constexpr BidirSentRange(int *ptr) : ptr_(ptr) {}
+  constexpr BidirSentRange(const BidirSentRange &) requires (CC == Copyable) = default;
+  constexpr BidirSentRange(BidirSentRange &&) requires (CC == MoveOnly) = default;
+  constexpr BidirSentRange& operator=(const BidirSentRange &) requires (CC == Copyable) = default;
+  constexpr BidirSentRange& operator=(BidirSentRange &&) requires (CC == MoveOnly) = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{ptr_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{ptr_}; }
+  constexpr sent_t end() { return sent_t{bidirectional_iterator<int*>{ptr_ + 8}}; }
+  constexpr sent_const_t end() const { return sent_const_t{bidirectional_iterator<const int*>{ptr_ + 8}}; }
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_REVERSE_TYPES_H


        


More information about the libcxx-commits mailing list