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

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 4 14:17:54 PST 2022


Author: Arthur O'Dwyer
Date: 2022-01-04T17:15:42-05:00
New Revision: 8507383631f2ce2254e35bb81e03319ede056ed1

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

LOG: [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

Added: 
    

Modified: 
    libcxx/include/__concepts/class_or_enum.h
    libcxx/include/__ranges/access.h
    libcxx/include/__ranges/empty.h
    libcxx/include/__ranges/size.h
    libcxx/test/std/ranges/range.access/begin.pass.cpp
    libcxx/test/std/ranges/range.access/data.pass.cpp
    libcxx/test/std/ranges/range.access/empty.pass.cpp
    libcxx/test/std/ranges/range.access/end.pass.cpp
    libcxx/test/std/ranges/range.access/size.pass.cpp
    libcxx/test/std/ranges/range.access/ssize.pass.cpp
    libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__concepts/class_or_enum.h b/libcxx/include/__concepts/class_or_enum.h
index 43c7636d9c818..aa8606a219290 100644
--- a/libcxx/include/__concepts/class_or_enum.h
+++ b/libcxx/include/__concepts/class_or_enum.h
@@ -25,6 +25,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 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/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 4a1242130ac05..246f8b20caf43 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/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 @@ namespace __begin {
   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 @@ namespace __end {
   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/libcxx/include/__ranges/empty.h b/libcxx/include/__ranges/empty.h
index e8a8aabf4aed6..8da0b120f182c 100644
--- a/libcxx/include/__ranges/empty.h
+++ b/libcxx/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 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 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/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index fc6641cf4887b..f3de5a8b84102 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/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 @@ namespace __size {
   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/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 1a6951967f88a..11170fa4f9943 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -303,6 +303,12 @@ struct BeginReturnsArrayRef {
 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/libcxx/test/std/ranges/range.access/data.pass.cpp b/libcxx/test/std/ranges/range.access/data.pass.cpp
index 40d2d3ab8eca5..6d0b718f6b040 100644
--- a/libcxx/test/std/ranges/range.access/data.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/data.pass.cpp
@@ -176,6 +176,11 @@ constexpr bool testViaRangesBegin() {
   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/libcxx/test/std/ranges/range.access/empty.pass.cpp b/libcxx/test/std/ranges/range.access/empty.pass.cpp
index 18cdce02b5739..5724acc67deea 100644
--- a/libcxx/test/std/ranges/range.access/empty.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/empty.pass.cpp
@@ -168,6 +168,11 @@ constexpr bool testBeginEqualsEnd() {
   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/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 27eaf741a1131..4b1d4e3f488d0 100644
--- a/libcxx/test/std/ranges/range.access/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/end.pass.cpp
@@ -350,6 +350,12 @@ struct EndReturnsArrayRef {
 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/libcxx/test/std/ranges/range.access/size.pass.cpp b/libcxx/test/std/ranges/range.access/size.pass.cpp
index 0a45a2d7c4988..915e67e194755 100644
--- a/libcxx/test/std/ranges/range.access/size.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/size.pass.cpp
@@ -314,6 +314,11 @@ constexpr bool testRanges() {
   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/libcxx/test/std/ranges/range.access/ssize.pass.cpp b/libcxx/test/std/ranges/range.access/ssize.pass.cpp
index 39e7b80e21639..c351928c8fe62 100644
--- a/libcxx/test/std/ranges/range.access/ssize.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/ssize.pass.cpp
@@ -78,6 +78,11 @@ constexpr bool test() {
   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/libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp
index ecc8048a95866..adf1caa200e62 100644
--- a/libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp
@@ -46,3 +46,8 @@ struct int_begin_iterator_end {
   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>*>);


        


More information about the libcxx-commits mailing list