[libc++] [ranges] ADL-proof the [range.access] CPOs.

For example, `std::ranges::range<Holder<Incomplete>*>` should be
well-formed false, not a hard error at compile time.

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

NOKEYCHECK=True
GitOrigin-RevId: 8507383631f2ce2254e35bb81e03319ede056ed1
diff --git a/include/__concepts/class_or_enum.h b/include/__concepts/class_or_enum.h
index 43c7636..aa8606a 100644
--- a/include/__concepts/class_or_enum.h
+++ b/include/__concepts/class_or_enum.h
@@ -25,6 +25,10 @@
 template<class _Tp>
 concept __class_or_enum = is_class_v<_Tp> || is_union_v<_Tp> || is_enum_v<_Tp>;
 
+// Work around Clang bug https://llvm.org/PR52970
+template<class _Tp>
+concept __workaround_52970 = is_class_v<__uncvref_t<_Tp>> || is_union_v<__uncvref_t<_Tp>>;
+
 #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/include/__ranges/access.h b/include/__ranges/access.h
index 4a12421..246f8b2 100644
--- a/include/__ranges/access.h
+++ b/include/__ranges/access.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___RANGES_ACCESS_H
 #define _LIBCPP___RANGES_ACCESS_H
 
+#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/readable_traits.h>
@@ -39,6 +40,7 @@
   template <class _Tp>
   concept __member_begin =
     __can_borrow<_Tp> &&
+    __workaround_52970<_Tp> &&
     requires(_Tp&& __t) {
       { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
     };
@@ -102,6 +104,7 @@
   template <class _Tp>
   concept __member_end =
     __can_borrow<_Tp> &&
+    __workaround_52970<_Tp> &&
     requires(_Tp&& __t) {
       typename iterator_t<_Tp>;
       { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for<iterator_t<_Tp>>;
diff --git a/include/__ranges/empty.h b/include/__ranges/empty.h
index e8a8aab..8da0b12 100644
--- a/include/__ranges/empty.h
+++ b/include/__ranges/empty.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___RANGES_EMPTY_H
 #define _LIBCPP___RANGES_EMPTY_H
 
+#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__ranges/access.h>
@@ -28,9 +29,11 @@
 namespace ranges {
 namespace __empty {
   template <class _Tp>
-  concept __member_empty = requires(_Tp&& __t) {
-    bool(__t.empty());
-  };
+  concept __member_empty =
+    __workaround_52970<_Tp> &&
+    requires(_Tp&& __t) {
+      bool(__t.empty());
+    };
 
   template<class _Tp>
   concept __can_invoke_size =
diff --git a/include/__ranges/size.h b/include/__ranges/size.h
index fc6641c..f3de5a8 100644
--- a/include/__ranges/size.h
+++ b/include/__ranges/size.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___RANGES_SIZE_H
 #define _LIBCPP___RANGES_SIZE_H
 
+#include <__concepts/class_or_enum.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
@@ -41,9 +42,12 @@
   concept __size_enabled = !disable_sized_range<remove_cvref_t<_Tp>>;
 
   template <class _Tp>
-  concept __member_size = __size_enabled<_Tp> && requires(_Tp&& __t) {
-    { _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like;
-  };
+  concept __member_size =
+    __size_enabled<_Tp> &&
+    __workaround_52970<_Tp> &&
+    requires(_Tp&& __t) {
+      { _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like;
+    };
 
   template <class _Tp>
   concept __unqualified_size =
diff --git a/test/std/ranges/range.access/begin.pass.cpp b/test/std/ranges/range.access/begin.pass.cpp
index 1a69519..11170fa 100644
--- a/test/std/ranges/range.access/begin.pass.cpp
+++ b/test/std/ranges/range.access/begin.pass.cpp
@@ -303,6 +303,12 @@
 static_assert(noexcept(std::ranges::begin(brar)));
 static_assert(noexcept(std::ranges::cbegin(brar)));
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);
+
 int main(int, char**) {
   static_assert(testReturnTypes());
 
diff --git a/test/std/ranges/range.access/data.pass.cpp b/test/std/ranges/range.access/data.pass.cpp
index 40d2d3a..6d0b718 100644
--- a/test/std/ranges/range.access/data.pass.cpp
+++ b/test/std/ranges/range.access/data.pass.cpp
@@ -176,6 +176,11 @@
   return true;
 }
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeDataT, Holder<Incomplete>*>);
+
 struct RandomButNotContiguous {
   random_access_iterator<int*> begin() const;
   random_access_iterator<int*> end() const;
diff --git a/test/std/ranges/range.access/empty.pass.cpp b/test/std/ranges/range.access/empty.pass.cpp
index 18cdce0..5724acc 100644
--- a/test/std/ranges/range.access/empty.pass.cpp
+++ b/test/std/ranges/range.access/empty.pass.cpp
@@ -168,6 +168,11 @@
   return true;
 }
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeEmptyT, Holder<Incomplete>*>);
+
 int main(int, char**) {
   testEmptyMember();
   static_assert(testEmptyMember());
diff --git a/test/std/ranges/range.access/end.pass.cpp b/test/std/ranges/range.access/end.pass.cpp
index 27eaf74..4b1d4e3 100644
--- a/test/std/ranges/range.access/end.pass.cpp
+++ b/test/std/ranges/range.access/end.pass.cpp
@@ -350,6 +350,12 @@
 static_assert(noexcept(std::ranges::end(erar)));
 static_assert(noexcept(std::ranges::cend(erar)));
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeEndT, Holder<Incomplete>*>);
+static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*>);
+
 int main(int, char**) {
   static_assert(testReturnTypes());
 
diff --git a/test/std/ranges/range.access/size.pass.cpp b/test/std/ranges/range.access/size.pass.cpp
index 0a45a2d..915e67e 100644
--- a/test/std/ranges/range.access/size.pass.cpp
+++ b/test/std/ranges/range.access/size.pass.cpp
@@ -314,6 +314,11 @@
   return true;
 }
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*>);
+
 int main(int, char**) {
   testArrayType();
   static_assert(testArrayType());
diff --git a/test/std/ranges/range.access/ssize.pass.cpp b/test/std/ranges/range.access/ssize.pass.cpp
index 39e7b80..c351928 100644
--- a/test/std/ranges/range.access/ssize.pass.cpp
+++ b/test/std/ranges/range.access/ssize.pass.cpp
@@ -78,6 +78,11 @@
   return true;
 }
 
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::is_invocable_v<RangeSSizeT, Holder<Incomplete>*>);
+
 int main(int, char**) {
   test();
   static_assert(test());
diff --git a/test/std/ranges/range.req/range.range/range.compile.pass.cpp b/test/std/ranges/range.req/range.range/range.compile.pass.cpp
index ecc8048..adf1caa 100644
--- a/test/std/ranges/range.req/range.range/range.compile.pass.cpp
+++ b/test/std/ranges/range.req/range.range/range.compile.pass.cpp
@@ -46,3 +46,8 @@
   int* end();
 };
 static_assert(!std::ranges::range<int_begin_iterator_end>);
+
+// Test ADL-proofing.
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(!std::ranges::range<Holder<Incomplete>*>);