Index: libcxx/include/__concepts/class_or_enum.h =================================================================== --- libcxx/include/__concepts/class_or_enum.h +++ libcxx/include/__concepts/class_or_enum.h @@ -25,6 +25,10 @@ template 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 +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 Index: libcxx/include/__ranges/access.h =================================================================== --- libcxx/include/__ranges/access.h +++ 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 @@ template 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 concept __member_end = __can_borrow<_Tp> && + __workaround_52970<_Tp> && requires(_Tp&& __t) { typename iterator_t<_Tp>; { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for>; Index: libcxx/include/__ranges/empty.h =================================================================== --- libcxx/include/__ranges/empty.h +++ 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 @@ namespace ranges { namespace __empty { template - concept __member_empty = requires(_Tp&& __t) { - bool(__t.empty()); - }; + concept __member_empty = + __workaround_52970<_Tp> && + requires(_Tp&& __t) { + bool(__t.empty()); + }; template concept __can_invoke_size = Index: libcxx/include/__ranges/size.h =================================================================== --- libcxx/include/__ranges/size.h +++ 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 @@ concept __size_enabled = !disable_sized_range>; template - 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 concept __unqualified_size = Index: libcxx/test/std/ranges/range.access/begin.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/begin.pass.cpp +++ libcxx/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 struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*>); + int main(int, char**) { static_assert(testReturnTypes()); Index: libcxx/test/std/ranges/range.access/data.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/data.pass.cpp +++ libcxx/test/std/ranges/range.access/data.pass.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "test_macros.h" #include "test_iterators.h" @@ -116,9 +117,10 @@ random_access_iterator begin() const; }; -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); struct BeginFriendContiguousIterator { int buff[8]; @@ -135,9 +137,10 @@ struct BeginFriendRandomAccess { friend random_access_iterator begin(const BeginFriendRandomAccess iter); }; -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); struct BeginMemberRvalue { int buff[8]; @@ -173,6 +176,17 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + +struct RandomButNotContiguous { + random_access_iterator begin() const; + random_access_iterator end() const; +}; +static_assert(!std::is_invocable_v); + int main(int, char**) { testDataMember(); static_assert(testDataMember()); Index: libcxx/test/std/ranges/range.access/empty.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/empty.pass.cpp +++ libcxx/test/std/ranges/range.access/empty.pass.cpp @@ -168,6 +168,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { testEmptyMember(); static_assert(testEmptyMember()); Index: libcxx/test/std/ranges/range.access/end.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/end.pass.cpp +++ libcxx/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 struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*>); + int main(int, char**) { static_assert(testReturnTypes()); Index: libcxx/test/std/ranges/range.access/size.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/size.pass.cpp +++ libcxx/test/std/ranges/range.access/size.pass.cpp @@ -314,6 +314,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { testArrayType(); static_assert(testArrayType()); Index: libcxx/test/std/ranges/range.access/ssize.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/ssize.pass.cpp +++ libcxx/test/std/ranges/range.access/ssize.pass.cpp @@ -78,6 +78,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { test(); static_assert(test()); Index: libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp +++ libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp @@ -46,3 +46,8 @@ int* end(); }; static_assert(!std::ranges::range); + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::ranges::range*>);