Skip to content

Commit

Permalink
[libc++] [ranges] ADL-proof the [range.access] CPOs.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Arthur O'Dwyer committed Jan 4, 2022
1 parent 1716c36 commit 8507383
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 6 deletions.
4 changes: 4 additions & 0 deletions libcxx/include/__concepts/class_or_enum.h
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/__ranges/access.h
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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>>;
Expand Down
9 changes: 6 additions & 3 deletions libcxx/include/__ranges/empty.h
Expand Up @@ -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>
Expand All @@ -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 =
Expand Down
10 changes: 7 additions & 3 deletions libcxx/include/__ranges/size.h
Expand Up @@ -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>
Expand Down Expand Up @@ -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 =
Expand Down
6 changes: 6 additions & 0 deletions libcxx/test/std/ranges/range.access/begin.pass.cpp
Expand Up @@ -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());

Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/std/ranges/range.access/data.pass.cpp
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/std/ranges/range.access/empty.pass.cpp
Expand Up @@ -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());
Expand Down
6 changes: 6 additions & 0 deletions libcxx/test/std/ranges/range.access/end.pass.cpp
Expand Up @@ -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());

Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/std/ranges/range.access/size.pass.cpp
Expand Up @@ -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());
Expand Down
5 changes: 5 additions & 0 deletions libcxx/test/std/ranges/range.access/ssize.pass.cpp
Expand Up @@ -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());
Expand Down
Expand Up @@ -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>*>);

0 comments on commit 8507383

Please sign in to comment.