From 0cc54993dc4c21b7594601ba161d4c12aaf2720b Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 18 Aug 2024 20:36:03 +0800 Subject: [PATCH 1/7] Test for LWG-4105 --- tests/std/include/range_algorithm_support.hpp | 228 ++++++++++++++++++ tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 89 +++++++ 4 files changed, 322 insertions(+) create mode 100644 tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/env.lst create mode 100644 tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index b71b654f06..64228bdb9b 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -935,6 +935,234 @@ namespace test { static_assert(false); } }; + + template + concept signed_integer_like = + std::signed_integral || std::same_as>>; + + template + constexpr auto to_unsigned(I n) noexcept { + if constexpr (std::signed_integral) { + return static_cast>(n); + } else { + using huge_iter = std::ranges::iterator_t>; + using unsigned_int_class = decltype(std::ranges::size(std::views::iota(huge_iter{}, huge_iter{}))); + return static_cast(n); + } + } + + template + struct redifference_iterator_category_base {}; + + template + requires std::signed_integral && requires { typename std::iterator_traits::iterator_category; } + struct redifference_iterator_category_base { + using iterator_category = std::iterator_traits::iterator_category; + using iterator_concept = decltype([] { + if constexpr (std::contiguous_iterator) { + return std::contiguous_iterator_tag{}; + } else if constexpr (std::random_access_iterator) { + return std::random_access_iterator_tag{}; + } else if constexpr (std::bidirectional_iterator) { + return std::bidirectional_iterator_tag{}; + } else if constexpr (std::forward_iterator) { + return std::forward_iterator_tag{}; + } else { + return std::input_iterator_tag{}; + } + }()); + }; + + template + class redifference_iterator : public redifference_iterator_category_base { + public: + using value_type = std::iter_value_t; + using difference_type = Diff; + + redifference_iterator() = default; + constexpr explicit redifference_iterator(It it) : i_{std::move(it)} {} + + [[nodiscard]] constexpr decltype(auto) operator*() const { + return *i_; + } + + constexpr decltype(auto) operator->() const + requires std::contiguous_iterator || (requires(const It& i) { i.operator->(); }) + { + if constexpr (std::contiguous_iterator) { + return std::to_address(i_); + } else { + return i_.operator->(); + } + } + + constexpr redifference_iterator& operator++() { + ++i_; + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (std::is_same_v) { + return redifference_iterator{i_++}; + } else { + return i_++; + } + } + + constexpr redifference_iterator& operator--() + requires std::bidirectional_iterator + { + --i_; + return *this; + } + + constexpr redifference_iterator operator--(int) + requires std::bidirectional_iterator + { + return redifference_iterator{--i_}; + } + + constexpr redifference_iterator& operator+=(std::same_as auto n) + requires std::random_access_iterator + { + i_ += static_cast>(n); + return *this; + } + + constexpr redifference_iterator& operator-=(std::same_as auto n) + requires std::random_access_iterator + { + i_ -= static_cast>(n); + return *this; + } + + constexpr decltype(auto) operator[](std::same_as auto n) const + requires std::random_access_iterator + { + return i_[static_cast>(n)]; + } + + [[nodiscard]] friend constexpr bool operator==(const redifference_iterator& i, const redifference_iterator& j) { + return i.i_ == j.i_; + } + + [[nodiscard]] friend constexpr redifference_iterator operator+( + const redifference_iterator& it, std::same_as auto n) + requires std::random_access_iterator + { + return redifference_iterator{it.i_ + static_cast>(n)}; + } + + [[nodiscard]] friend constexpr redifference_iterator operator+( + std::same_as auto n, const redifference_iterator& it) + requires std::random_access_iterator + { + return redifference_iterator{it.i_ + static_cast>(n)}; + } + + [[nodiscard]] friend constexpr redifference_iterator operator-( + const redifference_iterator& it, std::same_as auto n) + requires std::random_access_iterator + { + return redifference_iterator{it.i_ - static_cast>(n)}; + } + + [[nodiscard]] friend constexpr difference_type operator-( + const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + return i.i_ - j.i_; + } + + [[nodiscard]] friend constexpr auto operator<=>(const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + if constexpr (std::three_way_comparable) { + return i.i_ <=> j.i_; + } else { + if (i.i_ < j.i_) { + return std::weak_ordering::less; + } else if (j.i_ < i.i_) { + return std::weak_ordering::greater; + } else { + return std::weak_ordering::equivalent; + } + } + } + + [[nodiscard]] friend constexpr bool operator<(const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + return i.i_ < j.i_; + } + + [[nodiscard]] friend constexpr bool operator>(const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + return j.i_ < i.i_; + } + + [[nodiscard]] friend constexpr bool operator<=(const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + return !(j.i_ < i.i_); + } + + [[nodiscard]] friend constexpr bool operator>=(const redifference_iterator& i, const redifference_iterator& j) + requires std::random_access_iterator + { + return !(i.i_ < j.i_); + } + + constexpr const It& base() const noexcept { + return i_; + } + + private: + It i_; + }; + + template + struct redifference_sentinel { + S se_; + + template + requires std::sentinel_for + [[nodiscard]] friend constexpr bool operator==( + const redifference_iterator& i, const redifference_sentinel& s) { + return i.base() == s.se_; + } + + template + requires std::sized_sentinel_for + [[nodiscard]] friend constexpr Diff operator-( + const redifference_iterator& i, const redifference_sentinel& s) { + return static_cast(i.base() - s.se_); + } + template + requires std::sized_sentinel_for + [[nodiscard]] friend constexpr Diff operator-( + const redifference_sentinel& s, const redifference_iterator& i) { + return static_cast(s.se_ - i.base()); + } + }; + + template + constexpr auto make_redifference_subrange(Rng&& r) { + constexpr bool is_sized = std::ranges::sized_range + || std::sized_sentinel_for, std::ranges::iterator_t>; + using rediff_iter = redifference_iterator>; + using rediff_sent = redifference_sentinel>; + + if constexpr (is_sized) { + const auto sz = to_unsigned(static_cast(std::ranges::distance(r))); + return std::ranges::subrange{ + rediff_iter{r.begin()}, rediff_sent{r.end()}, sz}; + } else { + return std::ranges::subrange{ + rediff_iter{r.begin()}, rediff_sent{r.end()}}; + } + } } // namespace test template +#include +#include +#include +#include +#include + +#include + +using namespace std; + +template +concept testable_range = ranges::input_range && (ranges::forward_range || ranges::sized_range); + +template +concept testable_sentinel = + ranges::input_range + && (ranges::forward_range || sized_sentinel_for, ranges::iterator_t>); + +struct instantiator { + static constexpr pair haystack[] = {{0, 42}, {1, 42}, {2, 42}, {4, 42}}; + static constexpr pair short_haystack[] = {{4, 42}}; + static constexpr pair long_needle[] = {{13, 1}, {13, 2}, {13, 4}}; + static constexpr pair short_needle[] = {{13, 2}, {13, 4}}; + static constexpr pair wrong_needle[] = {{13, 2}, {13, 3}}; + + template + static constexpr void test_range_rediff() { + using ranges::ends_with, ranges::equal_to; + + { + In1 r1{haystack}; + In2 r2{long_needle}; + const same_as auto match = ends_with(test::make_redifference_subrange(r1), + test::make_redifference_subrange(r2), equal_to{}, get_first, get_second); + assert(match); + } + { + In1 r1{haystack}; + In2 r2{short_needle}; + const same_as auto match = ends_with(test::make_redifference_subrange(r1), + test::make_redifference_subrange(r2), equal_to{}, get_first, get_second); + assert(match); + } + { + In1 r1{haystack}; + In2 r2{wrong_needle}; + const same_as auto match = ends_with(test::make_redifference_subrange(r1), + test::make_redifference_subrange(r2), equal_to{}, get_first, get_second); + assert(!match); + } + { + In1 r1{short_haystack}; + In2 r2{short_needle}; + const same_as auto match = ends_with(test::make_redifference_subrange(r1), + test::make_redifference_subrange(r2), equal_to{}, get_first, get_second); + assert(!match); + } + { + In1 r1{haystack}; + In2 r2{span, 0>{}}; + const same_as auto match = ends_with(test::make_redifference_subrange(r1), + test::make_redifference_subrange(r2), equal_to{}, get_first, get_second); + assert(match); + } + } + + template + static void call() { + if constexpr (testable_range && testable_range) { + using int_class = ranges::range_difference_t>; + + test_range_rediff(); + static_assert((test_range_rediff(), true)); + + test_range_rediff(); + static_assert((test_range_rediff(), true)); + } + } +}; + +int main() { +#if !defined(_PREFAST_) && !defined(__EDG__) // TRANSITION, GH-1030 and GH-3567 + test_in_in, const pair>(); +#endif // ^^^ no workaround ^^^ +} From 23b78ba8fe9d63074a1d651f768cd20e3aa5c7d0 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sun, 18 Aug 2024 21:06:28 +0800 Subject: [PATCH 2/7] Switch to use strict_latest_matrix.lst --- .../tests/LWG4105_ranges_ends_with_and_integer_class/env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/env.lst b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/env.lst index 642f530ffa..b2dc2c0f57 100644 --- a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/env.lst +++ b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\strict_latest_matrix.lst From 3dc1d7743cee160162dbe436ef27178a7c6efada Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 05:59:52 -0700 Subject: [PATCH 3/7] Include `` for `to_address`. --- tests/std/include/range_algorithm_support.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 64228bdb9b..9631d9e55c 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include From d483c0baf8c59caa66b748ac6892ec9087f7fa9c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 06:00:24 -0700 Subject: [PATCH 4/7] All shall love `[[nodiscard]]` and despair. --- tests/std/include/range_algorithm_support.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 9631d9e55c..3b6cede38c 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -942,7 +942,7 @@ namespace test { std::signed_integral || std::same_as>>; template - constexpr auto to_unsigned(I n) noexcept { + [[nodiscard]] constexpr auto to_unsigned(I n) noexcept { if constexpr (std::signed_integral) { return static_cast>(n); } else { @@ -1037,7 +1037,7 @@ namespace test { return *this; } - constexpr decltype(auto) operator[](std::same_as auto n) const + [[nodiscard]] constexpr decltype(auto) operator[](std::same_as auto n) const requires std::random_access_iterator { return i_[static_cast>(n)]; @@ -1115,7 +1115,7 @@ namespace test { return !(i.i_ < j.i_); } - constexpr const It& base() const noexcept { + [[nodiscard]] constexpr const It& base() const noexcept { return i_; } @@ -1149,7 +1149,7 @@ namespace test { }; template - constexpr auto make_redifference_subrange(Rng&& r) { + [[nodiscard]] constexpr auto make_redifference_subrange(Rng&& r) { constexpr bool is_sized = std::ranges::sized_range || std::sized_sentinel_for, std::ranges::iterator_t>; using rediff_iter = redifference_iterator>; From c5f40da7dbe063eaf812c8e488f13250058871ba Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 19 Aug 2024 06:02:54 -0700 Subject: [PATCH 5/7] `std::ranges` => `ranges` --- tests/std/include/range_algorithm_support.hpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 3b6cede38c..aa0b032206 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -939,15 +939,15 @@ namespace test { template concept signed_integer_like = - std::signed_integral || std::same_as>>; + std::signed_integral || std::same_as>>; template [[nodiscard]] constexpr auto to_unsigned(I n) noexcept { if constexpr (std::signed_integral) { return static_cast>(n); } else { - using huge_iter = std::ranges::iterator_t>; - using unsigned_int_class = decltype(std::ranges::size(std::views::iota(huge_iter{}, huge_iter{}))); + using huge_iter = ranges::iterator_t>; + using unsigned_int_class = decltype(ranges::size(std::views::iota(huge_iter{}, huge_iter{}))); return static_cast(n); } } @@ -1148,19 +1148,19 @@ namespace test { } }; - template + template [[nodiscard]] constexpr auto make_redifference_subrange(Rng&& r) { - constexpr bool is_sized = std::ranges::sized_range - || std::sized_sentinel_for, std::ranges::iterator_t>; - using rediff_iter = redifference_iterator>; - using rediff_sent = redifference_sentinel>; + constexpr bool is_sized = + ranges::sized_range || std::sized_sentinel_for, ranges::iterator_t>; + using rediff_iter = redifference_iterator>; + using rediff_sent = redifference_sentinel>; if constexpr (is_sized) { - const auto sz = to_unsigned(static_cast(std::ranges::distance(r))); - return std::ranges::subrange{ + const auto sz = to_unsigned(static_cast(ranges::distance(r))); + return ranges::subrange{ rediff_iter{r.begin()}, rediff_sent{r.end()}, sz}; } else { - return std::ranges::subrange{ + return ranges::subrange{ rediff_iter{r.begin()}, rediff_sent{r.end()}}; } } From db4713c72f4aed4c0ca2cf8d20f98557fce046bd Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 21 Aug 2024 00:02:02 +0800 Subject: [PATCH 6/7] Use `_Signed_integer_like`, `input_iterator`, and `TEST_EVERYTHING` --- tests/std/include/range_algorithm_support.hpp | 26 ++++++-------- .../test.cpp | 35 ++++++++++++++++++- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index aa0b032206..eb8e8247a0 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -937,25 +937,19 @@ namespace test { } }; - template - concept signed_integer_like = - std::signed_integral || std::same_as>>; - - template + template [[nodiscard]] constexpr auto to_unsigned(I n) noexcept { if constexpr (std::signed_integral) { return static_cast>(n); } else { - using huge_iter = ranges::iterator_t>; - using unsigned_int_class = decltype(ranges::size(std::views::iota(huge_iter{}, huge_iter{}))); - return static_cast(n); + return static_cast(n); } } - template + template struct redifference_iterator_category_base {}; - template + template requires std::signed_integral && requires { typename std::iterator_traits::iterator_category; } struct redifference_iterator_category_base { using iterator_category = std::iterator_traits::iterator_category; @@ -974,7 +968,7 @@ namespace test { }()); }; - template + template class redifference_iterator : public redifference_iterator_category_base { public: using value_type = std::iter_value_t; @@ -1072,7 +1066,7 @@ namespace test { const redifference_iterator& i, const redifference_iterator& j) requires std::random_access_iterator { - return i.i_ - j.i_; + return static_cast(i.i_ - j.i_); } [[nodiscard]] friend constexpr auto operator<=>(const redifference_iterator& i, const redifference_iterator& j) @@ -1127,20 +1121,20 @@ namespace test { struct redifference_sentinel { S se_; - template + template requires std::sentinel_for [[nodiscard]] friend constexpr bool operator==( const redifference_iterator& i, const redifference_sentinel& s) { return i.base() == s.se_; } - template + template requires std::sized_sentinel_for [[nodiscard]] friend constexpr Diff operator-( const redifference_iterator& i, const redifference_sentinel& s) { return static_cast(i.base() - s.se_); } - template + template requires std::sized_sentinel_for [[nodiscard]] friend constexpr Diff operator-( const redifference_sentinel& s, const redifference_iterator& i) { @@ -1148,7 +1142,7 @@ namespace test { } }; - template + template [[nodiscard]] constexpr auto make_redifference_subrange(Rng&& r) { constexpr bool is_sized = ranges::sized_range || std::sized_sentinel_for, ranges::iterator_t>; diff --git a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp index 9b16d88ec3..ad9379b284 100644 --- a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp +++ b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp @@ -27,7 +27,7 @@ struct instantiator { static constexpr pair short_needle[] = {{13, 2}, {13, 4}}; static constexpr pair wrong_needle[] = {{13, 2}, {13, 3}}; - template + template static constexpr void test_range_rediff() { using ranges::ends_with, ranges::equal_to; @@ -82,8 +82,41 @@ struct instantiator { } }; +#ifdef TEST_EVERYTHING int main() { #if !defined(_PREFAST_) && !defined(__EDG__) // TRANSITION, GH-1030 and GH-3567 test_in_in, const pair>(); #endif // ^^^ no workaround ^^^ } +#else // ^^^ test all permutations of range properties / test only interesting permutations vvv +template +void run_tests_inner() { + instantiator::call, test::Sized::yes>>(); + instantiator::call, test::Sized::no>>(); + instantiator::call, test::Sized::yes>>(); + instantiator::call, test::Sized::no>>(); + instantiator::call, test::Sized::yes>>(); + instantiator::call, test::Sized::no>>(); + instantiator::call, test::Sized::yes>>(); + instantiator::call, test::Sized::no>>(); + instantiator::call, test::Sized::yes>>(); +} + +void run_tests() { + run_tests_inner, test::Sized::yes>>(); + run_tests_inner, test::Sized::no>>(); + run_tests_inner, test::Sized::yes>>(); + run_tests_inner, test::Sized::no>>(); + run_tests_inner, test::Sized::yes>>(); + run_tests_inner, test::Sized::no>>(); + run_tests_inner, test::Sized::yes>>(); + run_tests_inner, test::Sized::no>>(); + run_tests_inner, test::Sized::yes>>(); +} + +int main() { +#if !defined(_PREFAST_) && !defined(__EDG__) // TRANSITION, GH-1030 and GH-3567 + run_tests(); +#endif // ^^^ no workaround ^^^ +} +#endif // TEST_EVERYTHING From 3bed4bf83e99cbc10cf64a59f5a4d6d97006e04c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 20 Aug 2024 10:55:03 -0700 Subject: [PATCH 7/7] Enable /analyze and EDG test coverage. --- .../tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp index ad9379b284..78137bcd5e 100644 --- a/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp +++ b/tests/std/tests/LWG4105_ranges_ends_with_and_integer_class/test.cpp @@ -115,8 +115,6 @@ void run_tests() { } int main() { -#if !defined(_PREFAST_) && !defined(__EDG__) // TRANSITION, GH-1030 and GH-3567 run_tests(); -#endif // ^^^ no workaround ^^^ } #endif // TEST_EVERYTHING