[libcxx-commits] [libcxx] bf150e8 - [libc++] [ranges] ADL-proof ranges::iter_{swap, move}.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 31 11:14:53 PST 2022


Author: Arthur O'Dwyer
Date: 2022-01-31T14:14:26-05:00
New Revision: bf150e8dabb1efeffcdd5ba643126475b5286c83

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

LOG: [libc++] [ranges] ADL-proof ranges::iter_{swap,move}.

As discovered in D117817, `std::ranges::input_range<Holder<Incomplete>*[10]>`
hard-errored before this patch. That's because `input_range` requires
`iter_rvalue_reference_t`, which requires `iter_move`, which was
not ADL-proofed.

Add ADL-proofing tests to all the range refinements.
`output_range` and `common_range` shouldn't be affected,
and all the others subsume `input_range` anyway, but we might as
well be thorough.

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

Added: 
    

Modified: 
    libcxx/include/__iterator/iter_move.h
    libcxx/include/__iterator/iter_swap.h
    libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
    libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/common_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/contiguous_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/forward_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/input_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/output_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/random_access_range.compile.pass.cpp
    libcxx/test/std/ranges/range.req/range.refinements/viewable_range.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__iterator/iter_move.h b/libcxx/include/__iterator/iter_move.h
index dc50f30d7b24a..dfcf8e6c8308b 100644
--- a/libcxx/include/__iterator/iter_move.h
+++ b/libcxx/include/__iterator/iter_move.h
@@ -32,10 +32,12 @@ namespace __iter_move {
 
 void iter_move();
 
-template<class _Ip>
-concept __unqualified_iter_move = requires(_Ip&& __i) {
-    iter_move(_VSTD::forward<_Ip>(__i));
-};
+template <class _Tp>
+concept __unqualified_iter_move =
+  __class_or_enum<remove_cvref_t<_Tp>> &&
+  requires (_Tp&& __t) {
+    iter_move(_VSTD::forward<_Tp>(__t));
+  };
 
 // [iterator.cust.move]/1
 // The name ranges::iter_move denotes a customization point object.

diff  --git a/libcxx/include/__iterator/iter_swap.h b/libcxx/include/__iterator/iter_swap.h
index 4d3b455b0d7a2..0179546667b75 100644
--- a/libcxx/include/__iterator/iter_swap.h
+++ b/libcxx/include/__iterator/iter_swap.h
@@ -36,9 +36,11 @@ namespace __iter_swap {
   void iter_swap(_I1, _I2) = delete;
 
   template<class _T1, class _T2>
-  concept __unqualified_iter_swap = requires(_T1&& __x, _T2&& __y) {
-    iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
-  };
+  concept __unqualified_iter_swap =
+    (__class_or_enum<remove_cvref_t<_T1>> || __class_or_enum<remove_cvref_t<_T2>>) &&
+    requires (_T1&& __x, _T2&& __y) {
+      iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
+    };
 
   template<class _T1, class _T2>
   concept __readable_swappable =

diff  --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
index cbd9e13aac55f..39ecab6641e54 100644
--- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
@@ -21,6 +21,8 @@
 
 #include "../unqualified_lookup_wrapper.h"
 
+using IterMoveT = decltype(std::ranges::iter_move);
+
 // Wrapper around an iterator for testing `iter_move` when an unqualified call to `iter_move` isn't
 // possible.
 template <typename I>
@@ -113,7 +115,7 @@ struct WithoutADL {
   constexpr bool operator==(WithoutADL const&) const;
 };
 
-constexpr bool check_iter_move() {
+constexpr bool test() {
   constexpr int full_size = 100;
   constexpr int half_size = full_size / 2;
   constexpr int reset = 0;
@@ -173,18 +175,19 @@ constexpr bool check_iter_move() {
   return true;
 }
 
-template <typename T>
-concept can_iter_move = requires (T t) { std::ranges::iter_move(t); };
+static_assert(!std::is_invocable_v<IterMoveT, int*, int*>); // too many arguments
+static_assert(!std::is_invocable_v<IterMoveT, int>);
 
-int main(int, char**) {
-  static_assert(check_iter_move());
-  check_iter_move();
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::is_invocable_v<IterMoveT, Holder<Incomplete>**>);
+static_assert(std::is_invocable_v<IterMoveT, Holder<Incomplete>**&>);
 
-  // Make sure that `iter_move` SFINAEs away when the type can't be iter_move'd
-  {
-    struct NoIterMove { };
-    static_assert(!can_iter_move<NoIterMove>);
-  }
+int main(int, char**)
+{
+  test();
+  static_assert(test());
 
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp
index 3386e72c9195e..f1bf64a0fa7bd 100644
--- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp
@@ -24,7 +24,7 @@ using IterSwapT = decltype(std::ranges::iter_swap);
 
 struct HasIterSwap {
   int &value_;
-  explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }
+  constexpr explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }
 
   friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
     a.value_ = 1;
@@ -56,7 +56,7 @@ void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { std::ranges::i
 
 struct HasRangesSwap {
   int &value_;
-  explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }
+  constexpr explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }
 
   friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) {
     a.value_ = 1;
@@ -72,9 +72,9 @@ struct HasRangesSwapWrapper {
   using value_type = HasRangesSwap;
 
   HasRangesSwap &value_;
-  explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}
+  constexpr explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}
 
-  HasRangesSwap& operator*() const { return value_; }
+  constexpr HasRangesSwap& operator*() const { return value_; }
 };
 
 static_assert( std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>);
@@ -86,7 +86,7 @@ struct B;
 
 struct A {
   bool value = false;
-  A& operator=(const B&) {
+  constexpr A& operator=(const B&) {
     value = true;
     return *this;
   };
@@ -94,7 +94,7 @@ struct A {
 
 struct B {
   bool value = false;
-  B& operator=(const A&) {
+  constexpr B& operator=(const A&) {
     value = true;
     return *this;
   };
@@ -111,7 +111,7 @@ struct MoveOnly1 {
   MoveOnly1(const MoveOnly1&) = delete;
   MoveOnly1& operator=(const MoveOnly1&) = delete;
 
-  MoveOnly1& operator=(MoveOnly2 &&) {
+  constexpr MoveOnly1& operator=(MoveOnly2 &&) {
     value = true;
     return *this;
   };
@@ -126,13 +126,14 @@ struct MoveOnly2 {
   MoveOnly2(const MoveOnly2&) = delete;
   MoveOnly2& operator=(const MoveOnly2&) = delete;
 
-  MoveOnly2& operator=(MoveOnly1 &&) {
+  constexpr MoveOnly2& operator=(MoveOnly1 &&) {
     value = true;
     return *this;
   };
 };
 
-int main(int, char**) {
+constexpr bool test()
+{
   {
     int value1 = 0;
     int value2 = 0;
@@ -140,7 +141,6 @@ int main(int, char**) {
     std::ranges::iter_swap(a, b);
     assert(value1 == 1 && value2 == 1);
   }
-
   {
     int value1 = 0;
     int value2 = 0;
@@ -149,7 +149,6 @@ int main(int, char**) {
     std::ranges::iter_swap(cWrapper, dWrapper);
     assert(value1 == 1 && value2 == 1);
   }
-
   {
     int value1 = 0;
     int value2 = 0;
@@ -157,7 +156,6 @@ int main(int, char**) {
     std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d));
     assert(value1 == 1 && value2 == 1);
   }
-
   {
     A e; B f;
     A *ePtr = &e;
@@ -165,19 +163,20 @@ int main(int, char**) {
     std::ranges::iter_swap(ePtr, fPtr);
     assert(e.value && f.value);
   }
-
   {
     MoveOnly1 g; MoveOnly2 h;
     std::ranges::iter_swap(&g, &h);
     assert(g.value && h.value);
   }
-
   {
     auto arr = std::array<move_tracker, 2>();
     std::ranges::iter_swap(arr.begin(), arr.begin() + 1);
-    assert(arr[0].moves() == 1 && arr[1].moves() == 2);
+    if (std::is_constant_evaluated()) {
+      assert(arr[0].moves() == 1 && arr[1].moves() == 3);
+    } else {
+      assert(arr[0].moves() == 1 && arr[1].moves() == 2);
+    }
   }
-
   {
     int buff[2] = {1, 2};
     std::ranges::iter_swap(buff + 0, buff + 1);
@@ -201,6 +200,27 @@ int main(int, char**) {
     std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1));
     assert(buff[0] == 2 && buff[1] == 1);
   }
+  return true;
+}
+
+static_assert(!std::is_invocable_v<IterSwapT, int*>); // too few arguments
+static_assert(!std::is_invocable_v<IterSwapT, int*, int*, int*>); // too many arguments
+static_assert(!std::is_invocable_v<IterSwapT, int, int*>);
+static_assert(!std::is_invocable_v<IterSwapT, int*, int>);
+static_assert(!std::is_invocable_v<IterSwapT, void*, void*>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**>);
+static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**&>);
+static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**>);
+static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**&>);
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
 
   return 0;
 }

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp
index d0fb9373b9179..4534c52a53c6c 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/bidirectional_range.compile.pass.cpp
@@ -17,8 +17,6 @@
 
 #include "test_range.h"
 
-
-
 template <template <class...> class I>
 constexpr bool check_bidirectional_range() {
   constexpr bool result = std::ranges::bidirectional_range<test_range<I> >;
@@ -38,3 +36,21 @@ static_assert(!check_bidirectional_range<forward_iterator>());
 static_assert(check_bidirectional_range<bidirectional_iterator>());
 static_assert(check_bidirectional_range<random_access_iterator>());
 static_assert(check_bidirectional_range<contiguous_iterator>());
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::bidirectional_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::bidirectional_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/common_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/common_range.compile.pass.cpp
index 0ee8eed08a0bc..1c0559f44c2a1 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/common_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/common_range.compile.pass.cpp
@@ -64,3 +64,21 @@ struct Range2 {
 };
 static_assert( std::ranges::common_range<Range2>);
 static_assert(!std::ranges::common_range<Range2 const>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::common_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::common_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::common_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::common_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::common_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::common_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::common_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::common_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::common_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::common_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::common_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::common_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/contiguous_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/contiguous_range.compile.pass.cpp
index 1b5aa353d9364..8fe94fe538cdb 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/contiguous_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/contiguous_range.compile.pass.cpp
@@ -75,3 +75,21 @@ struct WrongObjectness {
     void *data() const;
 };
 static_assert(std::ranges::contiguous_range<WrongObjectness>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::contiguous_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::contiguous_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/forward_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/forward_range.compile.pass.cpp
index 9f25e232df6d7..143c5087d2d21 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/forward_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/forward_range.compile.pass.cpp
@@ -18,8 +18,6 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
-
-
 template <template <class...> class I>
 constexpr bool check_forward_range() {
   constexpr bool result = std::ranges::forward_range<test_range<I> >;
@@ -39,3 +37,21 @@ static_assert(check_forward_range<forward_iterator>());
 static_assert(check_forward_range<bidirectional_iterator>());
 static_assert(check_forward_range<random_access_iterator>());
 static_assert(check_forward_range<contiguous_iterator>());
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::forward_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::forward_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::forward_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::forward_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::forward_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::forward_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::forward_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::forward_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::forward_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::forward_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::forward_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::forward_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/input_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/input_range.compile.pass.cpp
index c27c972294e5a..96ba8fc84db4d 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/input_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/input_range.compile.pass.cpp
@@ -18,8 +18,6 @@
 #include "test_iterators.h"
 #include "test_range.h"
 
-
-
 static_assert(std::ranges::input_range<test_range<cpp17_input_iterator> >);
 static_assert(std::ranges::input_range<test_range<cpp17_input_iterator> const>);
 
@@ -43,3 +41,21 @@ static_assert(!std::ranges::input_range<test_non_const_common_range<cpp20_input_
 
 static_assert(!std::ranges::input_range<test_non_const_common_range<forward_iterator> const>);
 static_assert(!std::ranges::input_range<test_non_const_common_range<cpp20_input_iterator> const>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::input_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::input_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::input_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::input_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::input_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::input_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::input_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::input_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::input_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::input_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::input_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::input_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/output_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/output_range.compile.pass.cpp
index 8d0035a46a208..26bfac0e4c302 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/output_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/output_range.compile.pass.cpp
@@ -46,3 +46,21 @@ struct RangeWithBadIterator {
 static_assert( std::ranges::range<RangeWithBadIterator>);
 static_assert(!std::output_iterator<std::ranges::iterator_t<RangeWithBadIterator>, T>);
 static_assert(!std::ranges::output_range<RangeWithBadIterator, T>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::output_range<Holder<Incomplete>*, Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>*&, Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>*&&, Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const, Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const&, Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const&&, Holder<Incomplete>*>);
+
+static_assert( std::ranges::output_range<Holder<Incomplete>*[10], Holder<Incomplete>*>);
+static_assert( std::ranges::output_range<Holder<Incomplete>*(&)[10], Holder<Incomplete>*>);
+static_assert( std::ranges::output_range<Holder<Incomplete>*(&&)[10], Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const[10], Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const(&)[10], Holder<Incomplete>*>);
+static_assert(!std::ranges::output_range<Holder<Incomplete>* const(&&)[10], Holder<Incomplete>*>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/random_access_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/random_access_range.compile.pass.cpp
index f53d21bc34dce..a43c474eb6b62 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/random_access_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/random_access_range.compile.pass.cpp
@@ -38,3 +38,21 @@ static_assert(!check_range<forward_iterator>());
 static_assert(!check_range<bidirectional_iterator>());
 static_assert(check_range<random_access_iterator>());
 static_assert(check_range<contiguous_iterator>());
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::random_access_range<Holder<Incomplete>* const&&>);
+
+static_assert( std::ranges::random_access_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::random_access_range<Holder<Incomplete>*(&)[10]>);
+static_assert( std::ranges::random_access_range<Holder<Incomplete>*(&&)[10]>);
+static_assert( std::ranges::random_access_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::random_access_range<Holder<Incomplete>* const(&)[10]>);
+static_assert( std::ranges::random_access_range<Holder<Incomplete>* const(&&)[10]>);

diff  --git a/libcxx/test/std/ranges/range.req/range.refinements/viewable_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.refinements/viewable_range.compile.pass.cpp
index e0d1c6511d402..103309e6c9c4e 100644
--- a/libcxx/test/std/ranges/range.req/range.refinements/viewable_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.refinements/viewable_range.compile.pass.cpp
@@ -166,3 +166,21 @@ static_assert(!std::ranges::viewable_range<int(&)[]>); // not a range
 static_assert( std::ranges::viewable_range<int(&)[10]>); // OK, lvalue
 static_assert(!std::ranges::viewable_range<int(&&)[]>);
 static_assert(!std::ranges::viewable_range<int(&&)[10]>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>*>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>*&>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>*&&>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>* const>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>* const&>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>* const&&>);
+
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>*[10]>);
+static_assert( std::ranges::viewable_range<Holder<Incomplete>*(&)[10]>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>*(&&)[10]>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>* const[10]>);
+static_assert( std::ranges::viewable_range<Holder<Incomplete>* const(&)[10]>);
+static_assert(!std::ranges::viewable_range<Holder<Incomplete>* const(&&)[10]>);


        


More information about the libcxx-commits mailing list