[libcxx-commits] [libcxx] 502e5df - [libc++] Implement `ranges::{cbegin, cend}` per the spec.
Arthur O'Dwyer via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jan 4 13:19:15 PST 2022
Author: Arthur O'Dwyer
Date: 2022-01-04T16:18:41-05:00
New Revision: 502e5df0e08e0ea625b528e45fc92257273b6d89
URL: https://github.com/llvm/llvm-project/commit/502e5df0e08e0ea625b528e45fc92257273b6d89
DIFF: https://github.com/llvm/llvm-project/commit/502e5df0e08e0ea625b528e45fc92257273b6d89.diff
LOG: [libc++] Implement `ranges::{cbegin,cend}` per the spec.
The big change here is that they now work as intended for rvalues,
e.g. `ranges::cbegin(std::string_view("hello"))`.
Also, add tests verifying their return types.
Differential Revision: https://reviews.llvm.org/D116199
Added:
Modified:
libcxx/include/__ranges/access.h
libcxx/test/std/ranges/range.access/begin.pass.cpp
libcxx/test/std/ranges/range.access/end.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 91dc3055c86da..4a1242130ac05 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -160,20 +160,19 @@ namespace ranges {
namespace __cbegin {
struct __fn {
template <class _Tp>
- requires invocable<decltype(ranges::begin), _Tp const&>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const
- noexcept(noexcept(ranges::begin(_VSTD::as_const(__t))))
- {
- return ranges::begin(_VSTD::as_const(__t));
- }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp& __t) const
+ noexcept(noexcept(ranges::begin(static_cast<const _Tp&>(__t))))
+ -> decltype( ranges::begin(static_cast<const _Tp&>(__t)))
+ { return ranges::begin(static_cast<const _Tp&>(__t)); }
template <class _Tp>
- requires is_rvalue_reference_v<_Tp> && invocable<decltype(ranges::begin), _Tp const&&>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
- noexcept(noexcept(ranges::begin(static_cast<_Tp const&&>(__t))))
- {
- return ranges::begin(static_cast<_Tp const&&>(__t));
- }
+ requires is_rvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::begin(static_cast<const _Tp&&>(__t))))
+ -> decltype( ranges::begin(static_cast<const _Tp&&>(__t)))
+ { return ranges::begin(static_cast<const _Tp&&>(__t)); }
};
}
@@ -188,20 +187,19 @@ namespace ranges {
namespace __cend {
struct __fn {
template <class _Tp>
- requires invocable<decltype(ranges::end), _Tp const&>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const
- noexcept(noexcept(ranges::end(_VSTD::as_const(__t))))
- {
- return ranges::end(_VSTD::as_const(__t));
- }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp& __t) const
+ noexcept(noexcept(ranges::end(static_cast<const _Tp&>(__t))))
+ -> decltype( ranges::end(static_cast<const _Tp&>(__t)))
+ { return ranges::end(static_cast<const _Tp&>(__t)); }
template <class _Tp>
- requires is_rvalue_reference_v<_Tp> && invocable<decltype(ranges::end), _Tp const&&>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
- noexcept(noexcept(ranges::end(static_cast<_Tp const&&>(__t))))
- {
- return ranges::end(static_cast<_Tp const&&>(__t));
- }
+ requires is_rvalue_reference_v<_Tp&&>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Tp&& __t) const
+ noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+ -> decltype( ranges::end(static_cast<const _Tp&&>(__t)))
+ { return ranges::end(static_cast<const _Tp&&>(__t)); }
};
}
diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 95d2196803b28..1a6951967f88a 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -11,6 +11,7 @@
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::ranges::begin
+// std::ranges::cbegin
#include <ranges>
@@ -18,8 +19,8 @@
#include "test_macros.h"
#include "test_iterators.h"
-using RangeBeginT = decltype(std::ranges::begin)&;
-using RangeCBeginT = decltype(std::ranges::cbegin)&;
+using RangeBeginT = decltype(std::ranges::begin);
+using RangeCBeginT = decltype(std::ranges::cbegin);
static int globalBuff[8];
@@ -49,6 +50,28 @@ static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>);
static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>);
static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&&>);
+constexpr bool testReturnTypes() {
+ {
+ int *x[2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*);
+ }
+ {
+ int x[2][2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]);
+ }
+ {
+ struct Different {
+ char*& begin();
+ short*& begin() const;
+ } x;
+ ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*);
+ }
+ return true;
+}
+
constexpr bool testArray() {
int a[2];
assert(std::ranges::begin(a) == a);
@@ -118,12 +141,18 @@ constexpr bool testBeginMember() {
BeginMember a;
assert(std::ranges::begin(a) == &a.x);
assert(std::ranges::cbegin(a) == &a.x);
+ static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>);
+ static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>);
NonConstBeginMember b;
assert(std::ranges::begin(b) == &b.x);
+ static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>);
EnabledBorrowingBeginMember c;
+ assert(std::ranges::begin(c) == &globalBuff[0]);
+ assert(std::ranges::cbegin(c) == &globalBuff[0]);
assert(std::ranges::begin(std::move(c)) == &globalBuff[0]);
+ assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]);
BeginMemberFunction d;
assert(std::ranges::begin(d) == &d.x);
@@ -202,44 +231,44 @@ static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertib
constexpr bool testBeginFunction() {
BeginFunction a{};
const BeginFunction aa{};
- static_assert(!std::invocable<decltype(std::ranges::begin), decltype((a))>);
- assert(std::ranges::begin(aa) == &aa.x);
+ static_assert(!std::invocable<RangeBeginT, decltype((a))>);
assert(std::ranges::cbegin(a) == &a.x);
+ assert(std::ranges::begin(aa) == &aa.x);
assert(std::ranges::cbegin(aa) == &aa.x);
BeginFunctionByValue b{};
const BeginFunctionByValue bb{};
assert(std::ranges::begin(b) == &globalBuff[1]);
- assert(std::ranges::begin(bb) == &globalBuff[1]);
assert(std::ranges::cbegin(b) == &globalBuff[1]);
+ assert(std::ranges::begin(bb) == &globalBuff[1]);
assert(std::ranges::cbegin(bb) == &globalBuff[1]);
BeginFunctionEnabledBorrowing c{};
const BeginFunctionEnabledBorrowing cc{};
assert(std::ranges::begin(std::move(c)) == &globalBuff[2]);
- static_assert(!std::invocable<decltype(std::ranges::cbegin), decltype(std::move(c))>);
+ assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]);
assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]);
assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]);
BeginFunctionReturnsEmptyPtr d{};
const BeginFunctionReturnsEmptyPtr dd{};
- static_assert(!std::invocable<decltype(std::ranges::begin), decltype((d))>);
- assert(std::ranges::begin(dd) == &dd.x);
+ static_assert(!std::invocable<RangeBeginT, decltype((d))>);
assert(std::ranges::cbegin(d) == &d.x);
+ assert(std::ranges::begin(dd) == &dd.x);
assert(std::ranges::cbegin(dd) == &dd.x);
BeginFunctionWithDataMember e{};
const BeginFunctionWithDataMember ee{};
- static_assert(!std::invocable<decltype(std::ranges::begin), decltype((e))>);
+ static_assert(!std::invocable<RangeBeginT, decltype((e))>);
assert(std::ranges::begin(ee) == &ee.x);
assert(std::ranges::cbegin(e) == &e.x);
assert(std::ranges::cbegin(ee) == &ee.x);
BeginFunctionWithPrivateBeginMember f{};
const BeginFunctionWithPrivateBeginMember ff{};
- static_assert(!std::invocable<decltype(std::ranges::begin), decltype((f))>);
- assert(std::ranges::begin(ff) == &ff.y);
+ static_assert(!std::invocable<RangeBeginT, decltype((f))>);
assert(std::ranges::cbegin(f) == &f.y);
+ assert(std::ranges::begin(ff) == &ff.y);
assert(std::ranges::cbegin(ff) == &ff.y);
return true;
@@ -274,8 +303,9 @@ struct BeginReturnsArrayRef {
static_assert(noexcept(std::ranges::begin(brar)));
static_assert(noexcept(std::ranges::cbegin(brar)));
-
int main(int, char**) {
+ static_assert(testReturnTypes());
+
testArray();
static_assert(testArray());
diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 84b4904d8f963..27eaf741a1131 100644
--- a/libcxx/test/std/ranges/range.access/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/end.pass.cpp
@@ -11,6 +11,7 @@
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::ranges::end
+// std::ranges::cend
#include <ranges>
@@ -18,8 +19,8 @@
#include "test_macros.h"
#include "test_iterators.h"
-using RangeEndT = decltype(std::ranges::end)&;
-using RangeCEndT = decltype(std::ranges::cend)&;
+using RangeEndT = decltype(std::ranges::end);
+using RangeCEndT = decltype(std::ranges::cend);
static int globalBuff[8];
@@ -47,6 +48,30 @@ static_assert(!std::is_invocable_v<RangeEndT, EndMember &&>);
static_assert( std::is_invocable_v<RangeCEndT, EndMember &>);
static_assert( std::is_invocable_v<RangeCEndT, EndMember const&>);
+constexpr bool testReturnTypes() {
+ {
+ int *x[2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int**);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*);
+ }
+ {
+ int x[2][2];
+ ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]);
+ }
+ {
+ struct Different {
+ char *begin();
+ sentinel_wrapper<char*>& end();
+ short *begin() const;
+ sentinel_wrapper<short*>& end() const;
+ } x;
+ ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper<char*>);
+ ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper<short*>);
+ }
+ return true;
+}
+
constexpr bool testArray() {
int a[2];
assert(std::ranges::end(a) == a + 2);
@@ -139,9 +164,11 @@ constexpr bool testEndMember() {
NonConstEndMember b;
assert(std::ranges::end(b) == &b.x);
+ static_assert(!std::is_invocable_v<RangeCEndT, decltype((b))>);
EnabledBorrowingEndMember c;
assert(std::ranges::end(std::move(c)) == &globalBuff[0]);
+ assert(std::ranges::cend(std::move(c)) == &globalBuff[0]);
EndMemberFunction d;
assert(std::ranges::end(d) == &d.x);
@@ -246,7 +273,9 @@ struct BeginMemberEndFunction {
constexpr bool testEndFunction() {
const EndFunction a{};
assert(std::ranges::end(a) == &a.x);
+ assert(std::ranges::cend(a) == &a.x);
EndFunction aa{};
+ static_assert(!std::is_invocable_v<RangeEndT, decltype((aa))>);
assert(std::ranges::cend(aa) == &aa.x);
EndFunctionByValue b;
@@ -255,25 +284,34 @@ constexpr bool testEndFunction() {
EndFunctionEnabledBorrowing c;
assert(std::ranges::end(std::move(c)) == &globalBuff[2]);
+ assert(std::ranges::cend(std::move(c)) == &globalBuff[2]);
const EndFunctionReturnsEmptyPtr d{};
assert(std::ranges::end(d) == &d.x);
+ assert(std::ranges::cend(d) == &d.x);
EndFunctionReturnsEmptyPtr dd{};
+ static_assert(!std::is_invocable_v<RangeEndT, decltype((dd))>);
assert(std::ranges::cend(dd) == &dd.x);
const EndFunctionWithDataMember e{};
assert(std::ranges::end(e) == &e.x);
+ assert(std::ranges::cend(e) == &e.x);
EndFunctionWithDataMember ee{};
+ static_assert(!std::is_invocable_v<RangeEndT, decltype((ee))>);
assert(std::ranges::cend(ee) == &ee.x);
const EndFunctionWithPrivateEndMember f{};
assert(std::ranges::end(f) == &f.y);
+ assert(std::ranges::cend(f) == &f.y);
EndFunctionWithPrivateEndMember ff{};
+ static_assert(!std::is_invocable_v<RangeEndT, decltype((ff))>);
assert(std::ranges::cend(ff) == &ff.y);
const BeginMemberEndFunction g{};
assert(std::ranges::end(g) == &g.x);
+ assert(std::ranges::cend(g) == &g.x);
BeginMemberEndFunction gg{};
+ static_assert(!std::is_invocable_v<RangeEndT, decltype((gg))>);
assert(std::ranges::cend(gg) == &gg.x);
return true;
@@ -313,6 +351,8 @@ static_assert(noexcept(std::ranges::end(erar)));
static_assert(noexcept(std::ranges::cend(erar)));
int main(int, char**) {
+ static_assert(testReturnTypes());
+
testArray();
static_assert(testArray());
More information about the libcxx-commits
mailing list