diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -37,6 +37,7 @@ __nullptr __ranges/access.h __ranges/concepts.h + __ranges/data.h __ranges/empty.h __ranges/enable_borrowed_range.h __ranges/view.h diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/data.h @@ -0,0 +1,83 @@ +// -*- C++ -*- +//===------------------------ __ranges/data.h ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCPP___RANGES_DATA_H +#define _LIBCPP___RANGES_DATA_H + +#include <__config> + +#include +#include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +// clang-format off +namespace ranges { +// [range.prim.data] +namespace __data { + template + concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v>; + + template + concept __member_data = + requires(_Tp&& __t) { + { _VSTD::forward<_Tp>(__t) } -> __can_borrow; + { __t.data() } -> __ptr_to_object; + }; + + template + concept __ranges_begin_invocable = + !__member_data<_Tp> && + requires(_Tp&& __t) { + { _VSTD::forward<_Tp>(__t) } -> __can_borrow; + { ranges::begin(_VSTD::forward<_Tp>(__t)) } -> contiguous_iterator; + }; + + struct __fn { + template <__member_data _Tp> + requires __can_borrow<_Tp> + constexpr __ptr_to_object auto operator()(_Tp&& __t) const + noexcept(noexcept(__t.data())) { + return __t.data(); + } + + template<__ranges_begin_invocable _Tp> + requires __can_borrow<_Tp> + constexpr __ptr_to_object auto operator()(_Tp&& __t) const + noexcept(noexcept(_VSTD::to_address(ranges::begin(_VSTD::forward<_Tp>(__t))))) { + return _VSTD::to_address(ranges::begin(_VSTD::forward<_Tp>(__t))); + } + }; +} // end namespace __data + +inline namespace __cpo { + inline constexpr const auto data = __data::__fn{}; +} // namespace __cpo +} // namespace ranges + +// clang-format off + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_DATA_H diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -85,6 +85,7 @@ #include <__config> #include <__ranges/access.h> #include <__ranges/concepts.h> +#include <__ranges/data.h> #include <__ranges/empty.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/view.h> diff --git a/libcxx/test/std/ranges/range.access/range.prim/data.incomplete.verify.cpp b/libcxx/test/std/ranges/range.access/range.prim/data.incomplete.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/range.prim/data.incomplete.verify.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: clang-10 +// UNSUPPORTED: gcc-10 + +// std::ranges::data + +#include + +struct Incomplete; + +void f(Incomplete arr[]) { + // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f(Incomplete(&arr)[]) { + // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f(Incomplete(&&arr)[]) { + // expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}} + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f2(Incomplete arr[2]) { + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f(Incomplete(&arr)[2]) { + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f(Incomplete(&&arr)[2]) { + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} + +void f(Incomplete(&arr)[2][2]) { + // expected-error@*:* {{no matching function for call}} + std::ranges::data(arr); +} diff --git a/libcxx/test/std/ranges/range.access/range.prim/data.pass.cpp b/libcxx/test/std/ranges/range.access/range.prim/data.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/range.prim/data.pass.cpp @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 + +// std::ranges::data + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +using RangeDataT = decltype(std::ranges::data); + +static int globalBuff[2]; + +struct Incomplete; + +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 DataMember { + int x; + constexpr const int *data() const { return &x; } +}; + +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct VoidDataMember { + void *data() const; +}; +static_assert(!std::is_invocable_v); + +struct Empty { }; +struct EmptyDataMember { + Empty data() const; +}; + +static_assert(!std::is_invocable_v); + +struct EmptyPtrDataMember { + Empty x; + constexpr const Empty *data() const { return &x; } +}; + +struct PtrConvertible { + operator int*() const; +}; +struct PtrConvertibleDataMember { + PtrConvertible data() const; +}; + +static_assert(!std::is_invocable_v); + +struct NonConstDataMember { + int x; + constexpr int *data() { return &x; } +}; + +struct EnabledBorrowingDataMember { + constexpr int *data() { return &globalBuff[0]; } +}; + +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +struct DataMemberAndBegin { + int x; + constexpr const int *data() const { return &x; } + constexpr const int *begin() const { return &x; } +}; + +constexpr bool testDataMember() { + DataMember a; + assert(std::ranges::data(a) == &a.x); + + NonConstDataMember b; + assert(std::ranges::data(b) == &b.x); + + EnabledBorrowingDataMember c; + assert(std::ranges::data(std::move(c)) == &globalBuff[0]); + + DataMemberAndBegin d; + assert(std::ranges::data(d) == &d.x); + + return true; +} + +using ContiguousIter = contiguous_iterator; + +struct BeginMemberContiguousIterator { + int buff[8]; + + constexpr ContiguousIter begin() const { return ContiguousIter(buff); } +}; + +static_assert( std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct BeginMemberRandomAccess { + int buff[8]; + + random_access_iterator begin() const; +}; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct BeginFriendContiguousIterator { + int buff[8]; + + constexpr friend ContiguousIter begin(const BeginFriendContiguousIterator &iter) { + return ContiguousIter(iter.buff); + } +}; + +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); + +struct BeginMemberRvalue { + int buff[8]; + + ContiguousIter begin() &&; +}; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct BeginMemberBorrowingEnabled { + constexpr contiguous_iterator begin() { return contiguous_iterator{&globalBuff[1]}; } +}; + +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +constexpr bool testViaRangesBegin() { + int arr[2]; + assert(std::ranges::data(arr) == arr + 0); + + BeginMemberContiguousIterator a; + assert(std::ranges::data(a) == a.buff); + + const BeginFriendContiguousIterator b {}; + assert(std::ranges::data(b) == b.buff); + + BeginMemberBorrowingEnabled c; + assert(std::ranges::data(std::move(c)) == &globalBuff[1]); + + return true; +} + +int main(int, char**) { + testDataMember(); + static_assert(testDataMember()); + + testViaRangesBegin(); + static_assert(testViaRangesBegin()); + + return 0; +}