diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp index b6c558b37f9..2ff78c29bcb 100644 --- a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp @@ -23,55 +23,62 @@ #include #include + #include "test_macros.h" #include "test_iterators.h" -template -__host__ __device__ -void -test(It i, typename cuda::std::iterator_traits::difference_type n, It x) +template +__host__ __device__ TEST_CONSTEXPR_CXX14 +void check_advance(It it, Distance n, It result) { - cuda::std::advance(i, n); - assert(i == x); + static_assert(cuda::std::is_same::value, ""); + cuda::std::advance(it, n); + assert(it == result); } -#if TEST_STD_VER > 14 -template -__host__ __device__ -constexpr bool -constepxr_test(It i, typename cuda::std::iterator_traits::difference_type n, It x) +__host__ __device__ TEST_CONSTEXPR_CXX14 bool tests() { - cuda::std::advance(i, n); - return i == x; -} -#endif + const char* s = "1234567890"; -int main(int, char**) -{ + // Check with iterator_traits::difference_type { - const char* s = "1234567890"; - test(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); - test(forward_iterator(s), 10, forward_iterator(s+10)); - test(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)); - test(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)); - test(random_access_iterator(s+5), 5, random_access_iterator(s+10)); - test(random_access_iterator(s+5), -5, random_access_iterator(s)); - test(s+5, 5, s+10); - test(s+5, -5, s); + typedef cuda::std::iterator_traits::difference_type Distance; + check_advance(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)); + check_advance(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)); + check_advance(random_access_iterator(s+5), 5, random_access_iterator(s+10)); + check_advance(random_access_iterator(s+5), -5, random_access_iterator(s)); + check_advance(s+5, 5, s+10); + check_advance(s+5, -5, s); } -#if TEST_STD_VER > 14 + + // Also check with other distance types { - constexpr const char* s = "1234567890"; - static_assert( constepxr_test(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)), "" ); - static_assert( constepxr_test(forward_iterator(s), 10, forward_iterator(s+10)), "" ); - static_assert( constepxr_test(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)), "" ); - static_assert( constepxr_test(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)), "" ); - static_assert( constepxr_test(random_access_iterator(s+5), 5, random_access_iterator(s+10)), "" ); - static_assert( constepxr_test(random_access_iterator(s+5), -5, random_access_iterator(s)), "" ); - static_assert( constepxr_test(s+5, 5, s+10), "" ); - static_assert( constepxr_test(s+5, -5, s), "" ); + typedef int Distance; + check_advance(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s), 10, bidirectional_iterator(s+10)); + check_advance(random_access_iterator(s), 10, random_access_iterator(s+10)); } -#endif - return 0; + // Check with unsigned distance types to catch signedness-change issues + { + typedef cuda::std::size_t Distance; + check_advance(cpp17_input_iterator(s), 10u, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10u, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s), 10u, bidirectional_iterator(s+10)); + check_advance(random_access_iterator(s), 10u, random_access_iterator(s+10)); + } + + return true; +} + +int main(int, char**) +{ + tests(); +#if TEST_STD_VER > 14 + static_assert(tests(), ""); +#endif + return 0; } diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp new file mode 100644 index 00000000000..94f1f6b691e --- /dev/null +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: nvrtc +// UNSUPPORTED: msvc-19.16 + +// ranges::advance +// Make sure we're SFINAE-friendly when the template argument constraints are not met. + +#include + +class not_incrementable{}; + +__host__ __device__ void proper_constraints() { + not_incrementable p{}; + cuda::std::ranges::advance(p, 5); // expected-error {{no matching function for call}} + cuda::std::ranges::advance(p, p); // expected-error {{no matching function for call}} + cuda::std::ranges::advance(p, 5, p); // expected-error {{no matching function for call}} +} + +int main(int, char**) { + return 0; +} \ No newline at end of file diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp new file mode 100644 index 00000000000..670463c10c6 --- /dev/null +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: msvc-19.16 + +// ranges::advance(it, n) + +#include + +#include + +#include "test_iterators.h" +#include "test_macros.h" + +struct Abs { + template + __host__ __device__ constexpr T operator()(T x) const noexcept { return x < 0 ? -x : x; }; +}; + +template +__host__ __device__ constexpr void check(int* first, cuda::std::iter_difference_t n, int* expected) { + using Difference = cuda::std::iter_difference_t; + Difference const M = (expected - first); // expected travel distance (which may be negative) + Abs abs{}; + unused(abs); + unused(M); + + { + It it(first); + cuda::std::ranges::advance(it, n); + assert(base(it) == expected); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::advance(it, n)), void); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + cuda::std::ranges::advance(it, n); + if constexpr (cuda::std::random_access_iterator) { + assert(it.stride_count() <= 1); + } else { + assert(it.stride_count() == abs(M)); + } + } +} + +__host__ __device__ constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Check advancing forward + for (int n = 0; n != 10; ++n) { + check>( range, n, range+n); + check>( range, n, range+n); + check>( range, n, range+n); + check>(range, n, range+n); + check>(range, n, range+n); + check>( range, n, range+n); + check( range, n, range+n); + check >(range, n, range+n); + } + + // Check advancing backward + for (int n = 0; n != 10; ++n) { + check>(range+9, -n, range+9 - n); + check>(range+9, -n, range+9 - n); + check>( range+9, -n, range+9 - n); + check( range+9, -n, range+9 - n); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp new file mode 100644 index 00000000000..0927caf56db --- /dev/null +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp @@ -0,0 +1,234 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: msvc-19.16 + +// ranges::advance(it, n, sent) + +#include + +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "../types.h" + +template +__host__ __device__ constexpr void check_forward(int* first, int* last, cuda::std::iter_difference_t n, int* expected) { + using Difference = cuda::std::iter_difference_t; + Difference const M = (expected - first); // expected travel distance + + { + It it(first); + auto sent = sentinel_wrapper(It(last)); + decltype(auto) result = cuda::std::ranges::advance(it, n, sent); + static_assert(cuda::std::same_as); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = sentinel_wrapper(stride_counting_iterator(It(last))); + (void)cuda::std::ranges::advance(it, n, sent); + // We don't have a sized sentinel, so we have to increment one-by-one + // regardless of the iterator category. + assert(it.stride_count() == M); + assert(it.stride_displacement() == M); + } +} + +template +__host__ __device__ constexpr void check_forward_sized_sentinel(int* first, int* last, cuda::std::iter_difference_t n, int* expected) { + using Difference = cuda::std::iter_difference_t; + Difference const size = (last - first); + Difference const M = (expected - first); // expected travel distance + + { + It it(first); + auto sent = distance_apriori_sentinel(size); + decltype(auto) result = cuda::std::ranges::advance(it, n, sent); + static_assert(cuda::std::same_as); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + { + auto it = stride_counting_iterator(It(first)); + auto sent = distance_apriori_sentinel(size); + (void)cuda::std::ranges::advance(it, n, sent); + if constexpr (cuda::std::random_access_iterator) { + assert(it.stride_count() <= 1); + assert(it.stride_displacement() <= 1); + } else { + assert(it.stride_count() == M); + assert(it.stride_displacement() == M); + } + } +} + +template +__host__ __device__ constexpr void check_backward(int* first, int* last, cuda::std::iter_difference_t n, int* expected) { + static_assert(cuda::std::random_access_iterator, "This test doesn't support non random access iterators"); + using Difference = cuda::std::iter_difference_t; + Difference const M = (expected - last); // expected travel distance (which is negative) + + { + It it(last); + It sent(first); + decltype(auto) result = cuda::std::ranges::advance(it, n, sent); + static_assert(cuda::std::same_as); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + { + auto it = stride_counting_iterator(It(last)); + auto sent = stride_counting_iterator(It(first)); + (void)cuda::std::ranges::advance(it, n, sent); + assert(it.stride_count() <= 1); + assert(it.stride_displacement() <= 1); + } +} + +struct iota_iterator { + using difference_type = int; + using value_type = int; + + __host__ __device__ constexpr int operator*() const { return x; } + __host__ __device__ constexpr iota_iterator& operator++() { ++x; return *this; } + __host__ __device__ constexpr iota_iterator operator++(int) { ++x; return iota_iterator{x - 1}; } +#if TEST_STD_VER > 17 + constexpr bool operator==(const iota_iterator&) const = default; +#else + __host__ __device__ constexpr bool operator==(const iota_iterator& other) const { return x == other.x; } + __host__ __device__ constexpr bool operator!=(const iota_iterator& other) const { return x != other.x; } +#endif + __host__ __device__ constexpr int operator-(const iota_iterator& that) const { return x - that.x; } + __host__ __device__ constexpr iota_iterator& operator--() { --x; return *this; } + __host__ __device__ constexpr iota_iterator operator--(int) { --x; return iota_iterator{x + 1}; } + + int x; +}; +static_assert(cuda::std::bidirectional_iterator); +static_assert(cuda::std::sized_sentinel_for); + +__host__ __device__ constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Basic functionality test: advance forward, bound has the same type + { + int *p = nullptr; + p = range+5; assert(cuda::std::ranges::advance(p, 0, range+7) == 0); assert(p == range+5); + p = range+5; assert(cuda::std::ranges::advance(p, 1, range+7) == 0); assert(p == range+6); + p = range+5; assert(cuda::std::ranges::advance(p, 2, range+7) == 0); assert(p == range+7); + p = range+5; assert(cuda::std::ranges::advance(p, 3, range+7) == 1); assert(p == range+7); + } + + // Basic functionality test: advance forward, bound is not the same type and not assignable + { + int *p = nullptr; + using ConstPtr = const int*; + p = range+5; assert(cuda::std::ranges::advance(p, 0, ConstPtr(range+7)) == 0); assert(p == range+5); + p = range+5; assert(cuda::std::ranges::advance(p, 1, ConstPtr(range+7)) == 0); assert(p == range+6); + p = range+5; assert(cuda::std::ranges::advance(p, 2, ConstPtr(range+7)) == 0); assert(p == range+7); + p = range+5; assert(cuda::std::ranges::advance(p, 3, ConstPtr(range+7)) == 1); assert(p == range+7); + } + + // Basic functionality test: advance forward, bound has different type but assignable + { + const int *pc = nullptr; + pc = range+5; assert(cuda::std::ranges::advance(pc, 0, range+7) == 0); assert(pc == range+5); + pc = range+5; assert(cuda::std::ranges::advance(pc, 1, range+7) == 0); assert(pc == range+6); + pc = range+5; assert(cuda::std::ranges::advance(pc, 2, range+7) == 0); assert(pc == range+7); + pc = range+5; assert(cuda::std::ranges::advance(pc, 3, range+7) == 1); assert(pc == range+7); + } + + // Basic functionality test: advance backward, bound has the same type + // Note that we don't test advancing backward with a bound of a different type because that's UB + { + int *p = nullptr; + p = range+5; assert(cuda::std::ranges::advance(p, 0, range+3) == 0); assert(p == range+5); + p = range+5; assert(cuda::std::ranges::advance(p, -1, range+3) == 0); assert(p == range+4); + p = range+5; assert(cuda::std::ranges::advance(p, -2, range+3) == 0); assert(p == range+3); + p = range+5; assert(cuda::std::ranges::advance(p, -3, range+3) == -1); assert(p == range+3); + } + + // Basic functionality test: advance backward with an array as a sentinel + { + int* p = nullptr; + p = range+5; assert(cuda::std::ranges::advance(p, 0, range) == 0); assert(p == range+5); + p = range+5; assert(cuda::std::ranges::advance(p, -1, range) == 0); assert(p == range+4); + p = range+5; assert(cuda::std::ranges::advance(p, -5, range) == 0); assert(p == range); + p = range+5; assert(cuda::std::ranges::advance(p, -6, range) == -1); assert(p == range); + } + + // Exhaustive checks for n and range sizes + for (int size = 0; size != 10; ++size) { + for (int n = 0; n != 20; ++n) { + + { + int* expected = n > size ? range + size : range + n; + check_forward>( range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward>(range, range+size, n, expected); + check_forward>(range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward( range, range+size, n, expected); + + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>(range, range+size, n, expected); + check_forward_sized_sentinel>(range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel( range, range+size, n, expected); + } + + { + // Note that we can only test ranges::advance with a negative n for iterators that + // are sized sentinels for themselves, because ranges::advance is UB otherwise. + // In particular, that excludes bidirectional_iterators since those are not sized sentinels. + int* expected = n > size ? range : range + size - n; + check_backward>(range, range+size, -n, expected); + check_backward>( range, range+size, -n, expected); + check_backward( range, range+size, -n, expected); + } + } + } + + // Regression-test that INT_MIN doesn't cause any undefined behavior + { + auto i = iota_iterator{+1}; + assert(cuda::std::ranges::advance(i, INT_MIN, iota_iterator{-2}) == INT_MIN+3); + assert(i == iota_iterator{-2}); + i = iota_iterator{+1}; + assert(cuda::std::ranges::advance(i, -2, iota_iterator{INT_MIN+1}) == 0); + assert(i == iota_iterator{-1}); + i = iota_iterator{+1}; + assert(cuda::std::ranges::advance(i, INT_MIN, iota_iterator{INT_MIN+1}) == 0); + assert(i == iota_iterator{INT_MIN+1}); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp new file mode 100644 index 00000000000..ed5bcf2bb85 --- /dev/null +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: msvc-19.16 +// ranges::advance(it, sent) + +#include + +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +template +__host__ __device__ constexpr void check_assignable(int* first, int* last, int* expected) { + { + It it(first); + auto sent = assignable_sentinel(It(last)); + cuda::std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = assignable_sentinel(stride_counting_iterator(It(last))); + cuda::std::ranges::advance(it, sent); + assert(base(base(it)) == expected); + assert(it.stride_count() == 0); // because we got here by assigning from last, not by incrementing + } +} + +template +__host__ __device__ constexpr void check_sized_sentinel(int* first, int* last, int* expected) { + auto size = (last - first); + + { + It it(first); + auto sent = distance_apriori_sentinel(size); + cuda::std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = distance_apriori_sentinel(size); + cuda::std::ranges::advance(it, sent); + if constexpr (cuda::std::random_access_iterator) { + assert(it.stride_count() == 1); + } else { + assert(it.stride_count() == size); + } + } +} + +template +__host__ __device__ constexpr void check_sentinel(int* first, int* last, int* expected) { + auto size = (last - first); + unused(size); + + { + It it(first); + auto sent = sentinel_wrapper(It(last)); + cuda::std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(cuda::std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = sentinel_wrapper(stride_counting_iterator(It(last))); + cuda::std::ranges::advance(it, sent); + assert(it.stride_count() == size); + } +} + +__host__ __device__ constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + for (int n = 0; n != 10; ++n) { + check_assignable>( range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable>(range, range+n, range+n); + check_assignable>(range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable( range, range+n, range+n); + + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>(range, range+n, range+n); + check_sized_sentinel>(range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel( range, range+n, range+n); + + check_sentinel>( range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel>(range, range+n, range+n); + check_sentinel>(range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel( range, range+n, range+n); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/types.h b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/types.h new file mode 100644 index 00000000000..931af1d70ee --- /dev/null +++ b/libcudacxx/.upstream-tests/test/std/iterators/iterator.primitives/range.iter.ops/types.h @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H +#define TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H + +#include +#include +#include +#include + +#include "test_iterators.h" // for the fallthrough base() function + +class distance_apriori_sentinel { +public: + distance_apriori_sentinel() = default; + __host__ __device__ constexpr explicit distance_apriori_sentinel(cuda::std::ptrdiff_t const count) : count_(count) {} + + template, int> = 0> + __host__ __device__ friend constexpr bool operator==(distance_apriori_sentinel const, It const&) { + assert(false && "difference op should take precedence"); + return false; + } + +#if TEST_STD_VER < 20 + template, int> = 0> + __host__ __device__ friend constexpr bool operator==(It const&, distance_apriori_sentinel const) { + assert(false && "difference op should take precedence"); + return false; + } + + template, int> = 0> + __host__ __device__ friend constexpr bool operator!=(distance_apriori_sentinel const, It const&) { + assert(false && "difference op should take precedence"); + return true; + } + + template, int> = 0> + __host__ __device__ friend constexpr bool operator!=(It const&, distance_apriori_sentinel const) { + assert(false && "difference op should take precedence"); + return true; + } +#endif + + template, int> = 0> + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(It const&, distance_apriori_sentinel const y) { + return -y.count_; + } + + template, int> = 0> + __host__ __device__ friend constexpr cuda::std::ptrdiff_t operator-(distance_apriori_sentinel const x, It const&) { + return x.count_; + } + +private: + cuda::std::ptrdiff_t count_ = 0; +}; + +// Sentinel type that can be assigned to an iterator. This is to test the cases where the +// various iterator operations use assignment instead of successive increments/decrements. +template +class assignable_sentinel { +public: + explicit assignable_sentinel() = default; + __host__ __device__ constexpr explicit assignable_sentinel(const It& it) : base_(base(it)) {} + __host__ __device__ constexpr operator It() const { return It(base_); } + __host__ __device__ friend constexpr bool operator==(const assignable_sentinel& s, const It& other) { return s.base_ == base(other); } +#if TEST_STD_VER < 20 + __host__ __device__ friend constexpr bool operator==(const It& other, const assignable_sentinel& s) { return s.base_ == base(other); } + __host__ __device__ friend constexpr bool operator!=(const assignable_sentinel& s, const It& other) { return s.base_ != base(other); } + __host__ __device__ friend constexpr bool operator!=(const It& other, const assignable_sentinel& s) { return s.base_ != base(other); } +#endif + __host__ __device__ friend constexpr It base(const assignable_sentinel& s) { return It(s.base_); } +private: + decltype(base(cuda::std::declval())) base_; +}; + +template +__host__ __device__ assignable_sentinel(const It&) -> assignable_sentinel; + +#endif // TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H diff --git a/libcudacxx/.upstream-tests/test/support/test_iterators.h b/libcudacxx/.upstream-tests/test/support/test_iterators.h index e1d0e580aea..27d9171b386 100644 --- a/libcudacxx/.upstream-tests/test/support/test_iterators.h +++ b/libcudacxx/.upstream-tests/test/support/test_iterators.h @@ -378,6 +378,7 @@ class contiguous_iterator template void operator,(T const &) = delete; }; +static_assert(cuda::std::random_access_iterator>, ""); #ifndef TEST_HAS_NO_SPACESHIP_OPERATOR diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/__iterator/advance.h b/libcudacxx/include/cuda/std/detail/libcxx/include/__iterator/advance.h index a9257b833a9..196aedf6a54 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/__iterator/advance.h +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/__iterator/advance.h @@ -12,15 +12,21 @@ #define _LIBCUDACXX___ITERATOR_ADVANCE_H #ifndef __cuda_std__ -#include <__config> +# include <__config> #endif // __cuda_std__ #include "../__assert" +#include "../__concepts/assignable.h" +#include "../__concepts/same_as.h" +#include "../__iterator/concepts.h" +#include "../__iterator/incrementable_traits.h" #include "../__iterator/iterator_traits.h" +#include "../__utility/convert_to_integral.h" +#include "../__utility/move.h" #include "../cstdlib" #if defined(_CCCL_COMPILER_NVHPC) && defined(_CCCL_USE_IMPLICIT_SYSTEM_DEADER) -#pragma GCC system_header +# pragma GCC system_header #else // ^^^ _CCCL_COMPILER_NVHPC ^^^ / vvv !_CCCL_COMPILER_NVHPC vvv _CCCL_IMPLICIT_SYSTEM_HEADER #endif // !_CCCL_COMPILER_NVHPC @@ -28,45 +34,215 @@ _CCCL_IMPLICIT_SYSTEM_HEADER _LIBCUDACXX_BEGIN_NAMESPACE_STD template -inline _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 -void __advance(_InputIter& __i, - typename iterator_traits<_InputIter>::difference_type __n, input_iterator_tag) +_LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 void +__advance(_InputIter& __i, typename iterator_traits<_InputIter>::difference_type __n, input_iterator_tag) { - for (; __n > 0; --__n) - ++__i; + for (; __n > 0; --__n) + { + ++__i; + } } template -inline _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 -void __advance(_BiDirIter& __i, - typename iterator_traits<_BiDirIter>::difference_type __n, bidirectional_iterator_tag) +_LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 void +__advance(_BiDirIter& __i, typename iterator_traits<_BiDirIter>::difference_type __n, bidirectional_iterator_tag) { - if (__n >= 0) - for (; __n > 0; --__n) - ++__i; - else - for (; __n < 0; ++__n) - --__i; + if (__n >= 0) + { + for (; __n > 0; --__n) + { + ++__i; + } + } + else + { + for (; __n < 0; ++__n) + { + --__i; + } + } } template -inline _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 -void __advance(_RandIter& __i, - typename iterator_traits<_RandIter>::difference_type __n, random_access_iterator_tag) +_LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 void +__advance(_RandIter& __i, typename iterator_traits<_RandIter>::difference_type __n, random_access_iterator_tag) { - __i += __n; + __i += __n; } -template -inline _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 -void advance(_InputIter& __i, - typename iterator_traits<_InputIter>::difference_type __n) +template < class _InputIter, + class _Distance, + class _IntegralDistance = decltype(_CUDA_VSTD::__convert_to_integral(_CUDA_VSTD::declval<_Distance>())), + class = __enable_if_t::value> > +_LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_CONSTEXPR_AFTER_CXX14 void advance(_InputIter& __i, _Distance __orig_n) { - _LIBCUDACXX_ASSERT(__n >= 0 || __is_cpp17_bidirectional_iterator<_InputIter>::value, - "Attempt to advance(it, -n) on a non-bidi iterator"); - __advance(__i, __n, typename iterator_traits<_InputIter>::iterator_category()); + typedef typename iterator_traits<_InputIter>::difference_type _Difference; + _Difference __n = static_cast<_Difference>(_CUDA_VSTD::__convert_to_integral(__orig_n)); + _LIBCUDACXX_ASSERT(__n >= 0 || __is_cpp17_bidirectional_iterator<_InputIter>::value, + "Attempt to advance(it, n) with negative n on a non-bidirectional iterator"); + _CUDA_VSTD::__advance(__i, __n, typename iterator_traits<_InputIter>::iterator_category()); } _LIBCUDACXX_END_NAMESPACE_STD +#if _LIBCUDACXX_STD_VER > 14 && !defined(_LICBUDACXX_COMPILER_MSVC_2017) + +// [range.iter.op.advance] + +_LIBCUDACXX_BEGIN_NAMESPACE_RANGES +_LIBCUDACXX_BEGIN_NAMESPACE_CPO(__advance) +struct __fn +{ +private: + template + _LIBCUDACXX_INLINE_VISIBILITY static constexpr void __advance_forward(_Ip& __i, iter_difference_t<_Ip> __n) + { + while (__n > 0) + { + --__n; + ++__i; + } + } + + template + _LIBCUDACXX_INLINE_VISIBILITY static constexpr void __advance_backward(_Ip& __i, iter_difference_t<_Ip> __n) + { + while (__n < 0) + { + ++__n; + --__i; + } + } + + template + _LIBCUDACXX_INLINE_VISIBILITY static constexpr auto + __magnitude_geq(_Iter_difference __a, _Iter_difference __b) noexcept + { + return __a == 0 ? __b == 0 : // + __a > 0 ? __a >= __b + : __a <= __b; + }; + +public: + // Preconditions: If `I` does not model `bidirectional_iterator`, `n` is not negative. + + _LIBCUDACXX_TEMPLATE(class _Ip) + _LIBCUDACXX_REQUIRES(input_or_output_iterator<_Ip>) + _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr void + operator()(_Ip& __i, iter_difference_t<_Ip> __n) const + { + _LIBCUDACXX_ASSERT(__n >= 0 || bidirectional_iterator<_Ip>, + "If `n < 0`, then `bidirectional_iterator` must be true."); + + // If `I` models `random_access_iterator`, equivalent to `i += n`. + if constexpr (random_access_iterator<_Ip>) + { + __i += __n; + return; + } + else if constexpr (bidirectional_iterator<_Ip>) + { + // Otherwise, if `n` is non-negative, increments `i` by `n`. + __advance_forward(__i, __n); + // Otherwise, decrements `i` by `-n`. + __advance_backward(__i, __n); + return; + } + else + { + // Otherwise, if `n` is non-negative, increments `i` by `n`. + __advance_forward(__i, __n); + return; + } + } + + _LIBCUDACXX_TEMPLATE(class _Ip, class _Sp) + _LIBCUDACXX_REQUIRES(input_or_output_iterator<_Ip> _LIBCUDACXX_AND sentinel_for<_Sp, _Ip>) + _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr void operator()(_Ip& __i, _Sp __bound_sentinel) const + { + // If `I` and `S` model `assignable_from`, equivalent to `i = std::move(bound_sentinel)`. + if constexpr (assignable_from<_Ip&, _Sp>) + { + __i = _CUDA_VSTD::move(__bound_sentinel); + } + // Otherwise, if `S` and `I` model `sized_sentinel_for`, + // equivalent to `ranges::advance(i, bound_sentinel - i)`. + else if constexpr (sized_sentinel_for<_Sp, _Ip>) + { + (*this)(__i, __bound_sentinel - __i); + } + // Otherwise, while `bool(i != bound_sentinel)` is true, increments `i`. + else + { + while (__i != __bound_sentinel) + { + ++__i; + } + } + } + + // Preconditions: + // * If `n > 0`, [i, bound_sentinel) denotes a range. + // * If `n == 0`, [i, bound_sentinel) or [bound_sentinel, i) denotes a range. + // * If `n < 0`, [bound_sentinel, i) denotes a range, `I` models `bidirectional_iterator`, + // and `I` and `S` model `same_as`. + // Returns: `n - M`, where `M` is the difference between the ending and starting position. + _LIBCUDACXX_TEMPLATE(class _Ip, class _Sp) + _LIBCUDACXX_REQUIRES(input_or_output_iterator<_Ip> _LIBCUDACXX_AND sentinel_for<_Sp, _Ip>) + _LIBCUDACXX_INLINE_VISIBILITY _LIBCUDACXX_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> + operator()(_Ip& __i, iter_difference_t<_Ip> __n, _Sp __bound_sentinel) const + { + _LIBCUDACXX_ASSERT((__n >= 0) || (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>), + "If `n < 0`, then `bidirectional_iterator && same_as` must be true."); + // If `S` and `I` model `sized_sentinel_for`: + if constexpr (sized_sentinel_for<_Sp, _Ip>) + { + // If |n| >= |bound_sentinel - i|, equivalent to `ranges::advance(i, bound_sentinel)`. + // __magnitude_geq(a, b) returns |a| >= |b|, assuming they have the same sign. + const auto __M = __bound_sentinel - __i; + if (__magnitude_geq(__n, __M)) + { + (*this)(__i, __bound_sentinel); + return __n - __M; + } + + // Otherwise, equivalent to `ranges::advance(i, n)`. + (*this)(__i, __n); + return 0; + } + else + { + // Otherwise, if `n` is non-negative, while `bool(i != bound_sentinel)` is true, increments `i` but at + // most `n` times. + while (__i != __bound_sentinel && __n > 0) + { + ++__i; + --__n; + } + + // Otherwise, while `bool(i != bound_sentinel)` is true, decrements `i` but at most `-n` times. + if constexpr (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>) + { + while (__i != __bound_sentinel && __n < 0) + { + --__i; + ++__n; + } + } + return __n; + } + _LIBCUDACXX_UNREACHABLE(); + } +}; +_LIBCUDACXX_END_NAMESPACE_CPO + +inline namespace __cpo +{ +_LIBCUDACXX_CPO_ACCESSIBILITY auto advance = __advance::__fn{}; +} // namespace __cpo + +_LIBCUDACXX_END_NAMESPACE_RANGES + +#endif // _LIBCUDACXX_STD_VER > 14 && !_LICBUDACXX_COMPILER_MSVC_2017 + #endif // _LIBCUDACXX___ITERATOR_ADVANCE_H diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/cstdlib b/libcudacxx/include/cuda/std/detail/libcxx/include/cstdlib index 306c319ce64..a0bf255c15a 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/cstdlib +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/cstdlib @@ -84,7 +84,7 @@ void *aligned_alloc(size_t alignment, size_t size); // C11 #ifndef __cuda_std__ #include <__config> #include -#endif //__cuda_std__ +#endif // __cuda_std__ #ifndef _LIBCUDACXX_COMPILER_NVRTC # include @@ -92,7 +92,7 @@ void *aligned_alloc(size_t alignment, size_t size); // C11 #ifndef __cuda_std__ #include <__pragma_push> -#endif //__cuda_std__ +#endif // __cuda_std__ #if defined(_CCCL_COMPILER_NVHPC) && defined(_CCCL_USE_IMPLICIT_SYSTEM_DEADER) #pragma GCC system_header diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp index 9f96e62c429..8cf86407d7a 100644 --- a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp @@ -10,65 +10,85 @@ // All of these became constexpr in C++17 // -// template -// constexpr void advance(Iter& i, Iter::difference_type n); +// template +// constexpr void advance(Iter& i, Distance n); // -// template -// constexpr void advance(Iter& i, Iter::difference_type n); +// template +// constexpr void advance(Iter& i, Distance n); // -// template -// constexpr void advance(Iter& i, Iter::difference_type n); +// template +// constexpr void advance(Iter& i, Distance n); + + +// TODO: test_iterators.h includes , and includes and . +// Lots of implementation headers under <__chrono/> and has signed to unsigned conversion, +// which will trigger the -Wsign-conversion warning. +// Once those headers are fixed, enable the -Wsign-conversion for this test by removing +// below + +// Make sure we catch forced conversions to the difference_type if they happen. +// ADDITIONAL_COMPILE_FLAGS: -Wsign-conversion #include #include +#include +#include #include "test_macros.h" #include "test_iterators.h" -template -void -test(It i, typename std::iterator_traits::difference_type n, It x) +template +TEST_CONSTEXPR_CXX17 +void check_advance(It it, Distance n, It result) { - std::advance(i, n); - assert(i == x); + static_assert(std::is_same::value, ""); + std::advance(it, n); + assert(it == result); } -#if TEST_STD_VER > 14 -template -constexpr bool -constepxr_test(It i, typename std::iterator_traits::difference_type n, It x) +TEST_CONSTEXPR_CXX17 bool tests() { - std::advance(i, n); - return i == x; -} -#endif + const char* s = "1234567890"; -int main(int, char**) -{ + // Check with iterator_traits::difference_type { - const char* s = "1234567890"; - test(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); - test(forward_iterator(s), 10, forward_iterator(s+10)); - test(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)); - test(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)); - test(random_access_iterator(s+5), 5, random_access_iterator(s+10)); - test(random_access_iterator(s+5), -5, random_access_iterator(s)); - test(s+5, 5, s+10); - test(s+5, -5, s); + typedef std::iterator_traits::difference_type Distance; + check_advance(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)); + check_advance(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)); + check_advance(random_access_iterator(s+5), 5, random_access_iterator(s+10)); + check_advance(random_access_iterator(s+5), -5, random_access_iterator(s)); + check_advance(s+5, 5, s+10); + check_advance(s+5, -5, s); } -#if TEST_STD_VER > 14 + + // Also check with other distance types { - constexpr const char* s = "1234567890"; - static_assert( constepxr_test(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)), "" ); - static_assert( constepxr_test(forward_iterator(s), 10, forward_iterator(s+10)), "" ); - static_assert( constepxr_test(bidirectional_iterator(s+5), 5, bidirectional_iterator(s+10)), "" ); - static_assert( constepxr_test(bidirectional_iterator(s+5), -5, bidirectional_iterator(s)), "" ); - static_assert( constepxr_test(random_access_iterator(s+5), 5, random_access_iterator(s+10)), "" ); - static_assert( constepxr_test(random_access_iterator(s+5), -5, random_access_iterator(s)), "" ); - static_assert( constepxr_test(s+5, 5, s+10), "" ); - static_assert( constepxr_test(s+5, -5, s), "" ); + typedef int Distance; + check_advance(cpp17_input_iterator(s), 10, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s), 10, bidirectional_iterator(s+10)); + check_advance(random_access_iterator(s), 10, random_access_iterator(s+10)); } -#endif - return 0; + // Check with unsigned distance types to catch signedness-change issues + { + typedef std::size_t Distance; + check_advance(cpp17_input_iterator(s), 10u, cpp17_input_iterator(s+10)); + check_advance(forward_iterator(s), 10u, forward_iterator(s+10)); + check_advance(bidirectional_iterator(s), 10u, bidirectional_iterator(s+10)); + check_advance(random_access_iterator(s), 10u, random_access_iterator(s+10)); + } + + return true; +} + +int main(int, char**) +{ + tests(); +#if TEST_STD_VER >= 17 + static_assert(tests(), ""); +#endif + return 0; } diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp new file mode 100644 index 00000000000..4fa80abff9a --- /dev/null +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/constraints.verify.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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: msvc-19.16 + +// ranges::advance +// Make sure we're SFINAE-friendly when the template argument constraints are not met. + +#include + +#include + +#include "test_iterators.h" + +void proper_constraints() { + auto p = std::unique_ptr(); + std::ranges::advance(p, 5); // expected-error {{no matching function for call}} + std::ranges::advance(p, p); // expected-error {{no matching function for call}} + std::ranges::advance(p, 5, p); // expected-error {{no matching function for call}} +} diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp new file mode 100644 index 00000000000..d5ec11ac6a3 --- /dev/null +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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: msvc-19.16 + +// ranges::advance(it, n) + +#include + +#include + +#include "test_iterators.h" +#include "test_macros.h" + +template +constexpr void check(int* first, std::iter_difference_t n, int* expected) { + using Difference = std::iter_difference_t; + Difference const M = (expected - first); // expected travel distance (which may be negative) + auto abs = [](auto x) { return x < 0 ? -x : x; }; + + { + It it(first); + std::ranges::advance(it, n); + assert(base(it) == expected); + ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, n)), void); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + std::ranges::advance(it, n); + if constexpr (std::random_access_iterator) { + assert(it.stride_count() <= 1); + } else { + assert(it.stride_count() == abs(M)); + } + } +} + +constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Check advancing forward + for (int n = 0; n != 10; ++n) { + check>( range, n, range+n); + check>( range, n, range+n); + check>( range, n, range+n); + check>(range, n, range+n); + check>(range, n, range+n); + check>( range, n, range+n); + check( range, n, range+n); + check >(range, n, range+n); + } + + // Check advancing backward + for (int n = 0; n != 10; ++n) { + check>(range+9, -n, range+9 - n); + check>(range+9, -n, range+9 - n); + check>( range+9, -n, range+9 - n); + check( range+9, -n, range+9 - n); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp new file mode 100644 index 00000000000..73b95bd3367 --- /dev/null +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp @@ -0,0 +1,224 @@ +//===----------------------------------------------------------------------===// +// +// 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: msvc-19.16 + +// ranges::advance(it, n, sent) + +#include + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "../types.h" + +template +constexpr void check_forward(int* first, int* last, std::iter_difference_t n, int* expected) { + using Difference = std::iter_difference_t; + Difference const M = (expected - first); // expected travel distance + + { + It it(first); + auto sent = sentinel_wrapper(It(last)); + std::same_as auto result = std::ranges::advance(it, n, sent); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = sentinel_wrapper(stride_counting_iterator(It(last))); + (void)std::ranges::advance(it, n, sent); + // We don't have a sized sentinel, so we have to increment one-by-one + // regardless of the iterator category. + assert(it.stride_count() == M); + assert(it.stride_displacement() == M); + } +} + +template +constexpr void check_forward_sized_sentinel(int* first, int* last, std::iter_difference_t n, int* expected) { + using Difference = std::iter_difference_t; + Difference const size = (last - first); + Difference const M = (expected - first); // expected travel distance + + { + It it(first); + auto sent = distance_apriori_sentinel(size); + std::same_as auto result = std::ranges::advance(it, n, sent); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + { + auto it = stride_counting_iterator(It(first)); + auto sent = distance_apriori_sentinel(size); + (void)std::ranges::advance(it, n, sent); + if constexpr (std::random_access_iterator) { + assert(it.stride_count() <= 1); + assert(it.stride_displacement() <= 1); + } else { + assert(it.stride_count() == M); + assert(it.stride_displacement() == M); + } + } +} + +template +constexpr void check_backward(int* first, int* last, std::iter_difference_t n, int* expected) { + static_assert(std::random_access_iterator, "This test doesn't support non random access iterators"); + using Difference = std::iter_difference_t; + Difference const M = (expected - last); // expected travel distance (which is negative) + + { + It it(last); + It sent(first); + std::same_as auto result = std::ranges::advance(it, n, sent); + assert(result == n - M); + assert(base(it) == expected); + } + + // Count operations + { + auto it = stride_counting_iterator(It(last)); + auto sent = stride_counting_iterator(It(first)); + (void)std::ranges::advance(it, n, sent); + assert(it.stride_count() <= 1); + assert(it.stride_displacement() <= 1); + } +} + +struct iota_iterator { + using difference_type = int; + using value_type = int; + + constexpr int operator*() const { return x; } + constexpr iota_iterator& operator++() { ++x; return *this; } + constexpr iota_iterator operator++(int) { ++x; return iota_iterator{x - 1}; } + constexpr bool operator==(const iota_iterator&) const = default; + constexpr int operator-(const iota_iterator& that) const { return x - that.x; } + constexpr iota_iterator& operator--() { --x; return *this; } + constexpr iota_iterator operator--(int) { --x; return iota_iterator{x + 1}; } + + int x; +}; +static_assert(std::bidirectional_iterator); +static_assert(std::sized_sentinel_for); + +constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Basic functionality test: advance forward, bound has the same type + { + int *p; + p = range+5; assert(std::ranges::advance(p, 0, range+7) == 0); assert(p == range+5); + p = range+5; assert(std::ranges::advance(p, 1, range+7) == 0); assert(p == range+6); + p = range+5; assert(std::ranges::advance(p, 2, range+7) == 0); assert(p == range+7); + p = range+5; assert(std::ranges::advance(p, 3, range+7) == 1); assert(p == range+7); + } + + // Basic functionality test: advance forward, bound is not the same type and not assignable + { + int *p; + using ConstPtr = const int*; + p = range+5; assert(std::ranges::advance(p, 0, ConstPtr(range+7)) == 0); assert(p == range+5); + p = range+5; assert(std::ranges::advance(p, 1, ConstPtr(range+7)) == 0); assert(p == range+6); + p = range+5; assert(std::ranges::advance(p, 2, ConstPtr(range+7)) == 0); assert(p == range+7); + p = range+5; assert(std::ranges::advance(p, 3, ConstPtr(range+7)) == 1); assert(p == range+7); + } + + // Basic functionality test: advance forward, bound has different type but assignable + { + const int *pc; + pc = range+5; assert(std::ranges::advance(pc, 0, range+7) == 0); assert(pc == range+5); + pc = range+5; assert(std::ranges::advance(pc, 1, range+7) == 0); assert(pc == range+6); + pc = range+5; assert(std::ranges::advance(pc, 2, range+7) == 0); assert(pc == range+7); + pc = range+5; assert(std::ranges::advance(pc, 3, range+7) == 1); assert(pc == range+7); + } + + // Basic functionality test: advance backward, bound has the same type + // Note that we don't test advancing backward with a bound of a different type because that's UB + { + int *p; + p = range+5; assert(std::ranges::advance(p, 0, range+3) == 0); assert(p == range+5); + p = range+5; assert(std::ranges::advance(p, -1, range+3) == 0); assert(p == range+4); + p = range+5; assert(std::ranges::advance(p, -2, range+3) == 0); assert(p == range+3); + p = range+5; assert(std::ranges::advance(p, -3, range+3) == -1); assert(p == range+3); + } + + // Basic functionality test: advance backward with an array as a sentinel + { + int* p; + p = range+5; assert(std::ranges::advance(p, 0, range) == 0); assert(p == range+5); + p = range+5; assert(std::ranges::advance(p, -1, range) == 0); assert(p == range+4); + p = range+5; assert(std::ranges::advance(p, -5, range) == 0); assert(p == range); + p = range+5; assert(std::ranges::advance(p, -6, range) == -1); assert(p == range); + } + + // Exhaustive checks for n and range sizes + for (int size = 0; size != 10; ++size) { + for (int n = 0; n != 20; ++n) { + + { + int* expected = n > size ? range + size : range + n; + check_forward>( range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward>(range, range+size, n, expected); + check_forward>(range, range+size, n, expected); + check_forward>( range, range+size, n, expected); + check_forward( range, range+size, n, expected); + + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel>(range, range+size, n, expected); + check_forward_sized_sentinel>(range, range+size, n, expected); + check_forward_sized_sentinel>( range, range+size, n, expected); + check_forward_sized_sentinel( range, range+size, n, expected); + } + + { + // Note that we can only test ranges::advance with a negative n for iterators that + // are sized sentinels for themselves, because ranges::advance is UB otherwise. + // In particular, that excludes bidirectional_iterators since those are not sized sentinels. + int* expected = n > size ? range : range + size - n; + check_backward>(range, range+size, -n, expected); + check_backward>( range, range+size, -n, expected); + check_backward( range, range+size, -n, expected); + } + } + } + + // Regression-test that INT_MIN doesn't cause any undefined behavior + { + auto i = iota_iterator{+1}; + assert(std::ranges::advance(i, INT_MIN, iota_iterator{-2}) == INT_MIN+3); + assert(i == iota_iterator{-2}); + i = iota_iterator{+1}; + assert(std::ranges::advance(i, -2, iota_iterator{INT_MIN+1}) == 0); + assert(i == iota_iterator{-1}); + i = iota_iterator{+1}; + assert(std::ranges::advance(i, INT_MIN, iota_iterator{INT_MIN+1}) == 0); + assert(i == iota_iterator{INT_MIN+1}); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp new file mode 100644 index 00000000000..52e539450a3 --- /dev/null +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_sentinel.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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: msvc-19.16 + +// ranges::advance(it, sent) + +#include + +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +template +constexpr void check_assignable(int* first, int* last, int* expected) { + { + It it(first); + auto sent = assignable_sentinel(It(last)); + std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = assignable_sentinel(stride_counting_iterator(It(last))); + std::ranges::advance(it, sent); + assert(base(base(it)) == expected); + assert(it.stride_count() == 0); // because we got here by assigning from last, not by incrementing + } +} + +template +constexpr void check_sized_sentinel(int* first, int* last, int* expected) { + auto size = (last - first); + + { + It it(first); + auto sent = distance_apriori_sentinel(size); + std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = distance_apriori_sentinel(size); + std::ranges::advance(it, sent); + if constexpr (std::random_access_iterator) { + assert(it.stride_count() == 1); + } else { + assert(it.stride_count() == size); + } + } +} + +template +constexpr void check_sentinel(int* first, int* last, int* expected) { + auto size = (last - first); + + { + It it(first); + auto sent = sentinel_wrapper(It(last)); + std::ranges::advance(it, sent); + ASSERT_SAME_TYPE(decltype(std::ranges::advance(it, sent)), void); + assert(base(it) == expected); + } + + // Count operations + if constexpr (Count) { + auto it = stride_counting_iterator(It(first)); + auto sent = sentinel_wrapper(stride_counting_iterator(It(last))); + std::ranges::advance(it, sent); + assert(it.stride_count() == size); + } +} + +constexpr bool test() { + int range[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + for (int n = 0; n != 10; ++n) { + check_assignable>( range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable>(range, range+n, range+n); + check_assignable>(range, range+n, range+n); + check_assignable>( range, range+n, range+n); + check_assignable( range, range+n, range+n); + + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel>(range, range+n, range+n); + check_sized_sentinel>(range, range+n, range+n); + check_sized_sentinel>( range, range+n, range+n); + check_sized_sentinel( range, range+n, range+n); + + check_sentinel>( range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel>(range, range+n, range+n); + check_sentinel>(range, range+n, range+n); + check_sentinel>( range, range+n, range+n); + check_sentinel( range, range+n, range+n); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + return 0; +} diff --git a/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/types.h b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/types.h new file mode 100644 index 00000000000..3ab4924e241 --- /dev/null +++ b/libcudacxx/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/types.h @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H +#define TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H + +#include +#include +#include +#include + +#include "test_iterators.h" // for the fallthrough base() function + +class distance_apriori_sentinel { +public: + distance_apriori_sentinel() = default; + constexpr explicit distance_apriori_sentinel(std::ptrdiff_t const count) : count_(count) {} + + constexpr bool operator==(std::input_or_output_iterator auto const&) const { + assert(false && "difference op should take precedence"); + return false; + } + + friend constexpr std::ptrdiff_t operator-(std::input_or_output_iterator auto const&, + distance_apriori_sentinel const y) { + return -y.count_; + } + + friend constexpr std::ptrdiff_t operator-(distance_apriori_sentinel const x, + std::input_or_output_iterator auto const&) { + return x.count_; + } + +private: + std::ptrdiff_t count_ = 0; +}; + +// Sentinel type that can be assigned to an iterator. This is to test the cases where the +// various iterator operations use assignment instead of successive increments/decrements. +template +class assignable_sentinel { +public: + explicit assignable_sentinel() = default; + constexpr explicit assignable_sentinel(const It& it) : base_(base(it)) {} + constexpr operator It() const { return It(base_); } + constexpr bool operator==(const It& other) const { return base_ == base(other); } + friend constexpr It base(const assignable_sentinel& s) { return It(s.base_); } +private: + decltype(base(std::declval())) base_; +}; + +template +assignable_sentinel(const It&) -> assignable_sentinel; + +#endif // TEST_STD_ITERATORS_ITERATOR_PRIMITIVES_RANGE_ITER_OPS_TYPES_H