9 changes: 9 additions & 0 deletions libcxx/include/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ namespace std::ranges {
template<class T>
inline constexpr bool enable_borrowed_range<drop_view<T>> = enable_borrowed_range<T>;
// [range.transform], transform view
template<input_range V, copy_constructible F>
requires view<V> && is_object_v<F> &&
regular_invocable<F&, range_reference_t<V>> &&
can-reference<invoke_result_t<F&, range_reference_t<V>>>
class transform_view;
}
*/
Expand All @@ -118,6 +126,7 @@ namespace std::ranges {
#include <__ranges/ref_view.h>
#include <__ranges/size.h>
#include <__ranges/subrange.h>
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <compare> // Required by the standard.
#include <initializer_list> // Required by the standard.
Expand Down
5 changes: 5 additions & 0 deletions libcxx/include/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -4474,6 +4474,11 @@ using _IsCharLikeType = _And<is_standard_layout<_CharT>, is_trivial<_CharT> >;
template<class _Tp>
using __make_const_lvalue_ref = const typename remove_reference<_Tp>::type&;

#if _LIBCPP_STD_VER > 17
template<bool _Const, class _Tp>
using __maybe_const = conditional_t<_Const, const _Tp, _Tp>;
#endif // _LIBCPP_STD_VER > 17

_LIBCPP_END_NAMESPACE_STD

#if _LIBCPP_STD_VER > 14
Expand Down
18 changes: 15 additions & 3 deletions libcxx/include/variant
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ namespace std {
#include <__utility/forward.h>
#include <__variant/monostate.h>
#include <__tuple>
#include <array>
#include <compare>
#include <exception>
#include <functional>
Expand Down Expand Up @@ -239,6 +238,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// Remove this once we drop support for GCC 5.
#if _LIBCPP_STD_VER > 14 && !(defined(_LIBCPP_COMPILER_GCC) && _GNUC_VER_NEW < 6000)

// Light N-dimensional array of function pointers. Used in place of std::array to avoid
// adding a dependency.
template<class _Tp, size_t _Size>
struct __farray {
static_assert(_Size > 0, "N-dimensional array should never be empty in std::visit");
_Tp __buf_[_Size] = {};

_LIBCPP_INLINE_VISIBILITY constexpr
const _Tp &operator[](size_t __n) const noexcept {
return __buf_[__n];
}
};

_LIBCPP_NORETURN
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS
Expand Down Expand Up @@ -499,7 +511,7 @@ private:

template <class _Tp, size_t _Np, typename... _Indices>
inline _LIBCPP_INLINE_VISIBILITY
static constexpr auto&& __at(const array<_Tp, _Np>& __elems,
static constexpr auto&& __at(const __farray<_Tp, _Np>& __elems,
size_t __index, _Indices... __indices) {
return __at(__elems[__index], __indices...);
}
Expand All @@ -515,7 +527,7 @@ private:
inline _LIBCPP_INLINE_VISIBILITY
static constexpr auto __make_farray(_Fs&&... __fs) {
__std_visit_visitor_return_type_check<__uncvref_t<_Fs>...>();
using __result = array<common_type_t<__uncvref_t<_Fs>...>, sizeof...(_Fs)>;
using __result = __farray<common_type_t<__uncvref_t<_Fs>...>, sizeof...(_Fs)>;
return __result{{_VSTD::forward<_Fs>(__fs)...}};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===----------------------------------------------------------------------===//
//
// 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

// constexpr V base() const& requires copy_constructible<V>
// constexpr V base() &&

#include <ranges>

#include "test_macros.h"
#include "types.h"

constexpr bool test() {
{
std::ranges::transform_view<ContiguousView, Increment> transformView;
ContiguousView base = std::move(transformView).base();
ASSERT_SAME_TYPE(ContiguousView, decltype(std::move(transformView).base()));
assert(std::ranges::begin(base) == globalBuff);
}

{
std::ranges::transform_view<CopyableView, Increment> transformView;
CopyableView base1 = transformView.base();
ASSERT_SAME_TYPE(CopyableView, decltype(transformView.base()));
assert(std::ranges::begin(base1) == globalBuff);

CopyableView base2 = std::move(transformView).base();
ASSERT_SAME_TYPE(CopyableView, decltype(std::move(transformView).base()));
assert(std::ranges::begin(base2) == globalBuff);
}

{
const std::ranges::transform_view<CopyableView, Increment> transformView;
const CopyableView base1 = transformView.base();
ASSERT_SAME_TYPE(CopyableView, decltype(transformView.base()));
assert(std::ranges::begin(base1) == globalBuff);

const CopyableView base2 = std::move(transformView).base();
ASSERT_SAME_TYPE(CopyableView, decltype(std::move(transformView).base()));
assert(std::ranges::begin(base2) == globalBuff);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// 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

// constexpr iterator<false> begin();
// constexpr iterator<true> begin() const
// requires range<const V> &&
// regular_invocable<const F&, range_reference_t<const V>>;

#include <ranges>

#include "test_macros.h"
#include "types.h"

template<class T>
concept BeginInvocable = requires(T t) { t.begin(); };

constexpr bool test() {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};

{
std::ranges::transform_view transformView(ContiguousView{buff}, Increment{});
assert(transformView.begin().base() == buff);
assert(*transformView.begin() == 1);
}

{
std::ranges::transform_view transformView(ForwardView{buff}, Increment{});
assert(transformView.begin().base().base() == buff);
assert(*transformView.begin() == 1);
}

{
std::ranges::transform_view transformView(InputView{buff}, Increment{});
assert(transformView.begin().base().base() == buff);
assert(*transformView.begin() == 1);
}

{
const std::ranges::transform_view transformView(ContiguousView{buff}, IncrementConst{});
assert(*transformView.begin() == 1);
}

static_assert(!BeginInvocable<const std::ranges::transform_view<ContiguousView, Increment>>);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===----------------------------------------------------------------------===//
//
// 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

// CTAD tests.

#include <ranges>

#include "test_macros.h"
#include "types.h"

static_assert(std::same_as<decltype(std::ranges::transform_view(InputView(), Increment())),
std::ranges::transform_view<InputView, Increment>>);
static_assert(std::same_as<decltype(std::ranges::transform_view(std::declval<ForwardRange&>(), Increment())),
std::ranges::transform_view<std::ranges::ref_view<ForwardRange>, Increment>>);
static_assert(std::same_as<decltype(std::ranges::transform_view(BorrowableRange(), Increment())),
std::ranges::transform_view<std::ranges::subrange<int*>, Increment>>);
74 changes: 74 additions & 0 deletions libcxx/test/std/ranges/range.adaptors/range.transform/end.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// 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

// constexpr sentinel<false> end();
// constexpr iterator<false> end() requires common_range<V>;
// constexpr sentinel<true> end() const
// requires range<const V> &&
// regular_invocable<const F&, range_reference_t<const V>>;
// constexpr iterator<true> end() const
// requires common_range<const V> &&
// regular_invocable<const F&, range_reference_t<const V>>;

#include <ranges>

#include "test_macros.h"
#include "types.h"

template<class T>
concept EndInvocable = requires(T t) { t.end(); };

template<class T>
concept EndIsIter = requires(T t) { ++t.end(); };

constexpr bool test() {
{
std::ranges::transform_view transformView(ContiguousView{}, Increment{});
assert(transformView.end().base() == globalBuff + 8);
}

{
std::ranges::transform_view transformView(ForwardView{}, Increment{});
assert(transformView.end().base().base() == globalBuff + 8);
}

{
std::ranges::transform_view transformView(InputView{}, Increment{});
assert(transformView.end().base() == globalBuff + 8);
}

{
const std::ranges::transform_view transformView(ContiguousView{}, IncrementConst{});
assert(transformView.end().base() == globalBuff + 8);
}

static_assert(!EndInvocable<const std::ranges::transform_view<ContiguousView, Increment>>);
static_assert( EndInvocable< std::ranges::transform_view<ContiguousView, Increment>>);
static_assert( EndInvocable<const std::ranges::transform_view<ContiguousView, IncrementConst>>);
static_assert(!EndInvocable<const std::ranges::transform_view<InputView, Increment>>);
static_assert( EndInvocable< std::ranges::transform_view<InputView, Increment>>);
static_assert( EndInvocable<const std::ranges::transform_view<InputView, IncrementConst>>);

static_assert(!EndIsIter<const std::ranges::transform_view<InputView, IncrementConst>>);
static_assert(!EndIsIter< std::ranges::transform_view<InputView, Increment>>);
static_assert( EndIsIter<const std::ranges::transform_view<ContiguousView, IncrementConst>>);
static_assert( EndIsIter< std::ranges::transform_view<ContiguousView, Increment>>);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===----------------------------------------------------------------------===//
//
// 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

// Some basic examples of how transform_view might be used in the wild. This is a general
// collection of sample algorithms and functions that try to mock general usage of
// this view.

#include <ranges>

#include <cctype>
#include <functional>
#include <list>
#include <numeric>
#include <string>
#include <vector>

#include <cassert>
#include "test_macros.h"
#include "test_iterators.h"
#include "types.h"

template<std::ranges::range R>
auto toUpper(R range) {
return std::ranges::transform_view(range, [](char c) { return std::toupper(c); });
}

unsigned badRandom() { return 42; }

template<std::ranges::range R, class Fn = std::plus<std::iter_value_t<R>>>
auto withRandom(R&& range, Fn func = Fn()) {
return std::ranges::transform_view(range, std::bind_front(func, badRandom()));
}

template<class E1, class E2, size_t N, class Join = std::plus<E1>>
auto joinArrays(E1 (&a)[N], E2 (&b)[N], Join join = Join()) {
return std::ranges::transform_view(a, [&a, &b, join](auto& x) {
auto idx = (&x) - a;
return join(x, b[idx]);
});
}

int main(int, char**) {
{
std::vector vec = {1, 2, 3, 4};
auto sortOfRandom = withRandom(vec);
std::vector check = {43, 44, 45, 46};
assert(std::equal(sortOfRandom.begin(), sortOfRandom.end(), check.begin(), check.end()));
}

{
int a[4] = {1, 2, 3, 4};
int b[4] = {4, 3, 2, 1};
auto out = joinArrays(a, b);
int check[4] = {5, 5, 5, 5};
assert(std::equal(out.begin(), out.end(), check));
}

{
std::string_view str = "Hello, World.";
auto upp = toUpper(str);
std::string_view check = "HELLO, WORLD.";
assert(std::equal(upp.begin(), upp.end(), check.begin(), check.end()));
}

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator{++,--,+=,-=}

#include <ranges>

#include "test_macros.h"
#include "../types.h"

constexpr bool test() {
std::ranges::transform_view<ContiguousView, Increment> transformView;
auto iter = std::move(transformView).begin();
assert((++iter).base() == globalBuff + 1);

assert((iter++).base() == globalBuff + 1);
assert(iter.base() == globalBuff + 2);

assert((--iter).base() == globalBuff + 1);
assert((iter--).base() == globalBuff + 1);
assert(iter.base() == globalBuff);

// Check that decltype(InputIter++) == void.
ASSERT_SAME_TYPE(decltype(
std::declval<std::ranges::iterator_t<std::ranges::transform_view<InputView, Increment>>>()++),
void);

assert((iter += 4).base() == globalBuff + 4);
assert((iter -= 3).base() == globalBuff + 1);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::base

#include <ranges>

#include "test_macros.h"
#include "../types.h"

template<class V, class F>
concept BaseInvocable = requires(std::ranges::iterator_t<std::ranges::transform_view<V, F>> iter) {
iter.base();
};

constexpr bool test() {
{
std::ranges::transform_view<ContiguousView, Increment> transformView;
auto iter = std::move(transformView).begin();
ASSERT_SAME_TYPE(int*, decltype(iter.base()));
assert(iter.base() == globalBuff);
ASSERT_SAME_TYPE(int*, decltype(std::move(iter).base()));
assert(std::move(iter).base() == globalBuff);
}

{
std::ranges::transform_view<InputView, Increment> transformView;
auto iter = transformView.begin();
assert(std::move(iter).base() == globalBuff);
ASSERT_SAME_TYPE(cpp20_input_iterator<int *>, decltype(std::move(iter).base()));
}

static_assert(!BaseInvocable<InputView, Increment>);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator{<,>,<=,>=}

#include <ranges>
#include <compare>

#include "test_macros.h"
#include "../types.h"

constexpr bool test() {
{
std::ranges::transform_view<ContiguousView, Increment> transformView1;
auto iter1 = std::move(transformView1).begin();
std::ranges::transform_view<ContiguousView, Increment> transformView2;
auto iter2 = std::move(transformView2).begin();
assert(iter1 == iter2);
assert(iter1 + 1 != iter2);
assert(iter1 + 1 == iter2 + 1);

assert(iter1 < iter1 + 1);
assert(iter1 + 1 > iter1);
assert(iter1 <= iter1 + 1);
assert(iter1 <= iter2);
assert(iter1 + 1 >= iter2);
assert(iter1 >= iter2);
}

// TODO: when three_way_comparable is implemented and std::is_eq is implemented,
// uncomment this.
// {
// std::ranges::transform_view<ThreeWayCompView, Increment> transformView1;
// auto iter1 = transformView1.begin();
// std::ranges::transform_view<ThreeWayCompView, Increment> transformView2;
// auto iter2 = transformView2.begin();
//
// assert(std::is_eq(iter1 <=> iter2));
// assert(std::is_lteq(iter1 <=> iter2));
// ++iter2;
// assert(std::is_neq(iter1 <=> iter2));
// assert(std::is_lt(iter1 <=> iter2));
// assert(std::is_gt(iter2 <=> iter1));
// assert(std::is_gteq(iter2 <=> iter1));
//
// static_assert( std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ThreeWayCompView, Increment>>>);
// static_assert(!std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ContiguousView, Increment>>>);
// }

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::transform_view::<iterator>();

#include <ranges>

#include "test_macros.h"
#include "../types.h"

struct NoDefaultInit {
typedef std::random_access_iterator_tag iterator_category;
typedef int value_type;
typedef std::ptrdiff_t difference_type;
typedef int* pointer;
typedef int& reference;
typedef NoDefaultInit self;

NoDefaultInit(int*);

reference operator*() const;
pointer operator->() const;
auto operator<=>(const self&) const = default;
bool operator==(int *) const;

self& operator++();
self operator++(int);

self& operator--();
self operator--(int);

self& operator+=(difference_type n);
self operator+(difference_type n) const;
friend self operator+(difference_type n, self x);

self& operator-=(difference_type n);
self operator-(difference_type n) const;
difference_type operator-(const self&) const;

reference operator[](difference_type n) const;
};

struct IterNoDefaultInitView : std::ranges::view_base {
NoDefaultInit begin() const;
int *end() const;
NoDefaultInit begin();
int *end();
};

constexpr bool test() {
std::ranges::transform_view<ContiguousView, IncrementConst> transformView;
auto iter = std::move(transformView).begin();
std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, IncrementConst>> i2(iter);
(void)i2;
std::ranges::iterator_t<const std::ranges::transform_view<ContiguousView, IncrementConst>> constIter(iter);
(void)constIter;


static_assert( std::default_initializable<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, IncrementConst>>>);
static_assert(!std::default_initializable<std::ranges::iterator_t<std::ranges::transform_view<IterNoDefaultInitView, IncrementConst>>>);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator*

#include <ranges>

#include "test_macros.h"
#include "../types.h"

int main(int, char**) {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};

{
std::ranges::transform_view transformView(ContiguousView{buff}, Increment{});
assert(*transformView.begin() == 1);
}

static_assert(!noexcept(
*std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, Increment>>>()));
static_assert( noexcept(
*std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, IncrementNoexcept>>>()));

ASSERT_SAME_TYPE(
int,
decltype(*std::declval<std::ranges::transform_view<RandomAccessView, Increment>>().begin()));
ASSERT_SAME_TYPE(
int&,
decltype(*std::declval<std::ranges::transform_view<RandomAccessView, IncrementRef>>().begin()));
ASSERT_SAME_TYPE(
int&&,
decltype(*std::declval<std::ranges::transform_view<RandomAccessView, IncrementRvalueRef>>().begin()));

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator[]

#include <ranges>

#include "test_macros.h"
#include "../types.h"

constexpr bool test() {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};

{
std::ranges::transform_view transformView(ContiguousView{buff}, Increment{});
auto iter = transformView.begin();
static_assert(!noexcept(std::ranges::iter_move(iter)));

assert(std::ranges::iter_move(iter) == 1);
assert(std::ranges::iter_move(iter + 2) == 3);

ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(iter)));
ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(std::move(iter))));
}

{
static_assert( noexcept(std::ranges::iter_move(
std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, IncrementNoexcept>>&>())));
static_assert(!noexcept(std::ranges::iter_move(
std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, Increment>>&>())));
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator{+,-}

#include <ranges>

#include "test_macros.h"
#include "../types.h"

constexpr bool test() {
std::ranges::transform_view<ContiguousView, Increment> transformView1;
auto iter1 = std::move(transformView1).begin();
std::ranges::transform_view<ContiguousView, Increment> transformView2;
auto iter2 = std::move(transformView2).begin();
iter1 += 4;
assert((iter1 + 1).base() == globalBuff + 5);
assert((1 + iter1).base() == globalBuff + 5);
assert((iter1 - 1).base() == globalBuff + 3);
assert(iter1 - iter2 == 4);
assert((iter1 + 2) - 2 == iter1);
assert((iter1 - 2) + 2 == iter1);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// 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

// The requirements for transform_view::<iterator>'s members.

#include <ranges>

#include "test_macros.h"
#include "../types.h"

static_assert(std::ranges::bidirectional_range<std::ranges::transform_view<BidirectionalView, IncrementConst>>);
static_assert(!std::ranges::bidirectional_range<std::ranges::transform_view<ForwardView, IncrementConst>>);

static_assert(std::ranges::random_access_range<std::ranges::transform_view<RandomAccessView, IncrementConst>>);
static_assert(!std::ranges::random_access_range<std::ranges::transform_view<BidirectionalView, IncrementConst>>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===----------------------------------------------------------------------===//
//
// 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

// class transform_view::<sentinel>;

#include <ranges>

#include "test_macros.h"
#include "../types.h"

template<class T>
concept EndIsIter = requires(T t) { ++t.end(); };

constexpr bool test() {
std::ranges::transform_view<SizedSentinelView, IncrementConst> transformView1;
// Going to const and back.
auto sent1 = transformView1.end();
std::ranges::sentinel_t<const std::ranges::transform_view<SizedSentinelView, IncrementConst>> sent2{sent1};
std::ranges::sentinel_t<const std::ranges::transform_view<SizedSentinelView, IncrementConst>> sent3{sent2};
(void)sent3;

static_assert(!EndIsIter<decltype(sent1)>);
static_assert(!EndIsIter<decltype(sent2)>);
assert(sent1.base() == globalBuff + 8);

std::ranges::transform_view transformView2(SizedSentinelView{4}, IncrementConst());
auto sent4 = transformView2.end();
auto iter = transformView1.begin();
{
assert(iter != sent1);
assert(iter != sent2);
assert(iter != sent4);
}

{
assert(iter + 8 == sent1);
assert(iter + 8 == sent2);
assert(iter + 4 == sent4);
}

{
assert(sent1 - iter == 8);
assert(sent4 - iter == 4);
assert(iter - sent1 == -8);
assert(iter - sent4 == -4);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::operator[]

#include <ranges>

#include "test_macros.h"
#include "../types.h"

constexpr bool test() {
int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
std::ranges::transform_view transformView1(ContiguousView{buff}, Increment{});
auto iter1 = std::move(transformView1).begin() + 1;
assert(iter1[0] == 2);
assert(iter1[4] == 6);

static_assert(!noexcept(
std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, Increment>>>()[0]));
static_assert( noexcept(
std::declval<std::ranges::iterator_t<std::ranges::transform_view<ContiguousView, IncrementNoexcept>>>()[0]));

ASSERT_SAME_TYPE(
int,
decltype(std::declval<std::ranges::transform_view<RandomAccessView, Increment>>().begin()[0]));
ASSERT_SAME_TYPE(
int&,
decltype(std::declval<std::ranges::transform_view<RandomAccessView, IncrementRef>>().begin()[0]));
ASSERT_SAME_TYPE(
int&&,
decltype(std::declval<std::ranges::transform_view<RandomAccessView, IncrementRvalueRef>>().begin()[0]));

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//===----------------------------------------------------------------------===//
//
// 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

// transform_view::<iterator>::difference_type
// transform_view::<iterator>::value_type
// transform_view::<iterator>::iterator_category
// transform_view::<iterator>::iterator_concept

#include <ranges>

#include "test_macros.h"
#include "../types.h"

template<class V, class F>
concept HasIterCategory = requires { typename std::ranges::transform_view<V, F>::iterator_category; };

constexpr bool test() {
{
// Member typedefs for contiguous iterator.
static_assert(std::same_as<std::iterator_traits<int*>::iterator_concept, std::contiguous_iterator_tag>);
static_assert(std::same_as<std::iterator_traits<int*>::iterator_category, std::random_access_iterator_tag>);

using TView = std::ranges::transform_view<ContiguousView, IncrementRef>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}
{
// Member typedefs for random access iterator.
using TView = std::ranges::transform_view<RandomAccessView, IncrementRef>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::same_as<typename TIter::iterator_category, std::random_access_iterator_tag>);
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}
{
// Member typedefs for random access iterator/not-lvalue-ref.
using TView = std::ranges::transform_view<RandomAccessView, Increment>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::same_as<typename TIter::iterator_category, std::input_iterator_tag>); // Note: this is now input_iterator_tag.
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}
{
// Member typedefs for bidirectional iterator.
using TView = std::ranges::transform_view<BidirectionalView, IncrementRef>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::same_as<typename TIter::iterator_category, std::bidirectional_iterator_tag>);
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}
{
// Member typedefs for forward iterator.
using TView = std::ranges::transform_view<ForwardView, IncrementRef>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::forward_iterator_tag>);
static_assert(std::same_as<typename TIter::iterator_category, std::forward_iterator_tag>);
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}
{
// Member typedefs for input iterator.
using TView = std::ranges::transform_view<InputView, IncrementRef>;
using TIter = std::ranges::iterator_t<TView>;
static_assert(std::same_as<typename TIter::iterator_concept, std::input_iterator_tag>);
static_assert(!HasIterCategory<InputView, IncrementRef>);
static_assert(std::same_as<typename TIter::value_type, int>);
static_assert(std::same_as<typename TIter::difference_type, std::ptrdiff_t>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// 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

// constexpr auto size() requires sized_range<V>
// constexpr auto size() const requires sized_range<const V>

#include <ranges>

#include "test_macros.h"
#include "types.h"

template<class T>
concept SizeInvocable = requires(T t) { t.size(); };

constexpr bool test() {
{
std::ranges::transform_view transformView(ContiguousView{}, Increment{});
assert(transformView.size() == 8);
}

{
const std::ranges::transform_view transformView(ContiguousView{globalBuff, 4}, Increment{});
assert(transformView.size() == 4);
}

static_assert(!SizeInvocable<std::ranges::transform_view<ForwardView, Increment>>);

static_assert(SizeInvocable<std::ranges::transform_view<SizedSentinelNotConstView, Increment>>);
static_assert(!SizeInvocable<const std::ranges::transform_view<SizedSentinelNotConstView, Increment>>);

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
155 changes: 155 additions & 0 deletions libcxx/test/std/ranges/range.adaptors/range.transform/types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TRANSFORM_TYPES_H
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TRANSFORM_TYPES_H

#include "test_macros.h"
#include "test_iterators.h"
#include "test_range.h"

int globalBuff[8] = {0,1,2,3,4,5,6,7};

template<class T, class F>
concept ValidDropView = requires { typename std::ranges::transform_view<T, F>; };

struct ContiguousView : std::ranges::view_base {
int start_;
int *ptr_;
constexpr ContiguousView(int* ptr = globalBuff, int start = 0) : start_(start), ptr_(ptr) {}
constexpr ContiguousView(ContiguousView&&) = default;
constexpr ContiguousView& operator=(ContiguousView&&) = default;
constexpr friend int* begin(ContiguousView& view) { return view.ptr_ + view.start_; }
constexpr friend int* begin(ContiguousView const& view) { return view.ptr_ + view.start_; }
constexpr friend int* end(ContiguousView& view) { return view.ptr_ + 8; }
constexpr friend int* end(ContiguousView const& view) { return view.ptr_ + 8; }
};

struct CopyableView : std::ranges::view_base {
int start_;
constexpr CopyableView(int start = 0) : start_(start) {}
constexpr CopyableView(CopyableView const&) = default;
constexpr CopyableView& operator=(CopyableView const&) = default;
constexpr friend int* begin(CopyableView& view) { return globalBuff + view.start_; }
constexpr friend int* begin(CopyableView const& view) { return globalBuff + view.start_; }
constexpr friend int* end(CopyableView&) { return globalBuff + 8; }
constexpr friend int* end(CopyableView const&) { return globalBuff + 8; }
};

using ForwardIter = forward_iterator<int*>;
struct ForwardView : std::ranges::view_base {
int *ptr_;
constexpr ForwardView(int* ptr = globalBuff) : ptr_(ptr) {}
constexpr ForwardView(ForwardView&&) = default;
constexpr ForwardView& operator=(ForwardView&&) = default;
constexpr friend ForwardIter begin(ForwardView& view) { return ForwardIter(view.ptr_); }
constexpr friend ForwardIter begin(ForwardView const& view) { return ForwardIter(view.ptr_); }
constexpr friend ForwardIter end(ForwardView& view) { return ForwardIter(view.ptr_ + 8); }
constexpr friend ForwardIter end(ForwardView const& view) { return ForwardIter(view.ptr_ + 8); }
};

using ForwardRange = test_common_range<forward_iterator>;

using RandomAccessIter = random_access_iterator<int*>;
struct RandomAccessView : std::ranges::view_base {
RandomAccessIter begin() const noexcept;
RandomAccessIter end() const noexcept;
RandomAccessIter begin() noexcept;
RandomAccessIter end() noexcept;
};

using BidirectionalIter = bidirectional_iterator<int*>;
struct BidirectionalView : std::ranges::view_base {
BidirectionalIter begin() const;
BidirectionalIter end() const;
BidirectionalIter begin();
BidirectionalIter end();
};

struct BorrowableRange {
friend int* begin(BorrowableRange const& range);
friend int* end(BorrowableRange const&);
friend int* begin(BorrowableRange& range);
friend int* end(BorrowableRange&);
};

template<>
inline constexpr bool std::ranges::enable_borrowed_range<BorrowableRange> = true;

struct InputView : std::ranges::view_base {
int *ptr_;
constexpr InputView(int* ptr = globalBuff) : ptr_(ptr) {}
constexpr cpp20_input_iterator<int*> begin() const { return cpp20_input_iterator<int*>(ptr_); }
constexpr int* end() const { return ptr_ + 8; }
constexpr cpp20_input_iterator<int*> begin() { return cpp20_input_iterator<int*>(ptr_); }
constexpr int* end() { return ptr_ + 8; }
};

constexpr bool operator==(const cpp20_input_iterator<int*> &lhs, int* rhs) { return lhs.base() == rhs; }
constexpr bool operator==(int* lhs, const cpp20_input_iterator<int*> &rhs) { return rhs.base() == lhs; }

struct SizedSentinelView : std::ranges::view_base {
int count_;
constexpr SizedSentinelView(int count = 8) : count_(count) {}
constexpr RandomAccessIter begin() const { return RandomAccessIter(globalBuff); }
constexpr int* end() const { return globalBuff + count_; }
constexpr RandomAccessIter begin() { return RandomAccessIter(globalBuff); }
constexpr int* end() { return globalBuff + count_; }
};

constexpr auto operator- (const RandomAccessIter &lhs, int* rhs) { return lhs.base() - rhs; }
constexpr auto operator- (int* lhs, const RandomAccessIter &rhs) { return lhs - rhs.base(); }
constexpr bool operator==(const RandomAccessIter &lhs, int* rhs) { return lhs.base() == rhs; }
constexpr bool operator==(int* lhs, const RandomAccessIter &rhs) { return rhs.base() == lhs; }

struct SizedSentinelNotConstView : std::ranges::view_base {
ForwardIter begin() const;
int *end() const;
ForwardIter begin();
int *end();
size_t size();
};
bool operator==(const ForwardIter &lhs, int* rhs);
bool operator==(int* lhs, const ForwardIter &rhs);

struct Range {
friend int* begin(Range const&);
friend int* end(Range const&);
friend int* begin(Range&);
friend int* end(Range&);
};

using CountedIter = stride_counting_iterator<forward_iterator<int*>>;
struct CountedView : std::ranges::view_base {
constexpr CountedIter begin() { return CountedIter(ForwardIter(globalBuff)); }
constexpr CountedIter begin() const { return CountedIter(ForwardIter(globalBuff)); }
constexpr CountedIter end() { return CountedIter(ForwardIter(globalBuff + 8)); }
constexpr CountedIter end() const { return CountedIter(ForwardIter(globalBuff + 8)); }
};

using ThreeWayCompIter = three_way_contiguous_iterator<int*>;
struct ThreeWayCompView : std::ranges::view_base {
constexpr ThreeWayCompIter begin() { return ThreeWayCompIter(globalBuff); }
constexpr ThreeWayCompIter begin() const { return ThreeWayCompIter(globalBuff); }
constexpr ThreeWayCompIter end() { return ThreeWayCompIter(globalBuff + 8); }
constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); }
};

struct Increment {
constexpr int operator()(int x) { return x + 1; }
};

struct IncrementConst {
constexpr int operator()(int x) const { return x + 1; }
};

struct IncrementRef {
constexpr int& operator()(int& x) { return ++x; }
};

struct IncrementRvalueRef {
constexpr int&& operator()(int& x) { return std::move(++x); }
};

struct IncrementNoexcept {
constexpr int operator()(int x) noexcept { return x + 1; }
};

#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TRANSFORM_TYPES_H
56 changes: 56 additions & 0 deletions libcxx/test/support/test_iterators.h
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,62 @@ class sentinel_wrapper {
I base_ = I();
};

template <class It>
class three_way_contiguous_iterator
{
static_assert(std::is_pointer_v<It>, "Things probably break in this case");

It it_;

template <class U> friend class three_way_contiguous_iterator;
public:
typedef std::contiguous_iterator_tag iterator_category;
typedef typename std::iterator_traits<It>::value_type value_type;
typedef typename std::iterator_traits<It>::difference_type difference_type;
typedef It pointer;
typedef typename std::iterator_traits<It>::reference reference;
typedef typename std::remove_pointer<It>::type element_type;

TEST_CONSTEXPR_CXX14 It base() const {return it_;}

TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator() : it_() {}
explicit TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator(It it) : it_(it) {}
template <class U>
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator(const three_way_contiguous_iterator<U>& u) : it_(u.it_) {}

TEST_CONSTEXPR_CXX14 reference operator*() const {return *it_;}
TEST_CONSTEXPR_CXX14 pointer operator->() const {return it_;}

TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator& operator++() {++it_; return *this;}
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator operator++(int)
{three_way_contiguous_iterator tmp(*this); ++(*this); return tmp;}

TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator& operator--() {--it_; return *this;}
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator operator--(int)
{three_way_contiguous_iterator tmp(*this); --(*this); return tmp;}

TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator& operator+=(difference_type n) {it_ += n; return *this;}
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator operator+(difference_type n) const
{three_way_contiguous_iterator tmp(*this); tmp += n; return tmp;}
friend TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator operator+(difference_type n, three_way_contiguous_iterator x)
{x += n; return x;}
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator& operator-=(difference_type n) {return *this += -n;}
TEST_CONSTEXPR_CXX14 three_way_contiguous_iterator operator-(difference_type n) const
{three_way_contiguous_iterator tmp(*this); tmp -= n; return tmp;}

TEST_CONSTEXPR_CXX14 reference operator[](difference_type n) const {return it_[n];}

template <class T>
void operator,(T const &) DELETE_FUNCTION;

friend TEST_CONSTEXPR_CXX14
difference_type operator-(const three_way_contiguous_iterator& x, const three_way_contiguous_iterator& y) {
return x.base() - y.base();
}

friend auto operator<=>(const three_way_contiguous_iterator&, const three_way_contiguous_iterator&) = default;
};

// clang-format on

#endif // TEST_STD_VER > 17 && defined(__cpp_lib_concepts)
Expand Down