Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[libc++] Remove redundant and somewhat confusing assertions around advance() #133276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 6, 2025

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Mar 27, 2025

The std::advance function has a clear precondition that it can only be called with a negative distance when a bidirectional iterator is used. However, prev() and next() don't have such preconditions explicitly, they inherit it from calling advance().

This patch removes assertions in prev() and next() that were duplicates of similar ones in advance(), and removes a copy-pasted comment that was trying to justify the use of _LIBCPP_ASSERT_PEDANTIC but IMO is creating confusion with little benefit.

…vance()

The std::advance function has a clear precondition that it can only be
called with a negative distance when a bidirectional iterator is used.
However, prev() and next() don't have such preconditions explicitly,
they inherit it from calling advance().

This patch removes assertions in prev() and next() that were duplicates
of similar ones in advance(), and removes a copy-pasted comment that was
trying to justify the use of _LIBCPP_ASSERT_PEDANTIC but IMO is creating
confusion with little benefit.
@ldionne ldionne added the hardening Issues related to the hardening effort label Mar 27, 2025
@ldionne ldionne requested a review from a team as a code owner March 27, 2025 16:52
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 27, 2025

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

The std::advance function has a clear precondition that it can only be called with a negative distance when a bidirectional iterator is used. However, prev() and next() don't have such preconditions explicitly, they inherit it from calling advance().

This patch removes assertions in prev() and next() that were duplicates of similar ones in advance(), and removes a copy-pasted comment that was trying to justify the use of _LIBCPP_ASSERT_PEDANTIC but IMO is creating confusion with little benefit.


Full diff: https://github.com/llvm/llvm-project/pull/133276.diff

3 Files Affected:

  • (modified) libcxx/include/__iterator/advance.h (+6-8)
  • (modified) libcxx/include/__iterator/next.h (-6)
  • (modified) libcxx/include/__iterator/prev.h (-5)
diff --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h
index 57b1b845f1afa..f1a8d28f39aa0 100644
--- a/libcxx/include/__iterator/advance.h
+++ b/libcxx/include/__iterator/advance.h
@@ -65,8 +65,7 @@ template < class _InputIter,
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 void advance(_InputIter& __i, _Distance __orig_n) {
   typedef typename iterator_traits<_InputIter>::difference_type _Difference;
   _Difference __n = static_cast<_Difference>(std::__convert_to_integral(__orig_n));
-  // Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
-  _LIBCPP_ASSERT_PEDANTIC(__n >= 0 || __has_bidirectional_iterator_category<_InputIter>::value,
+  _LIBCPP_ASSERT_PEDANTIC(__has_bidirectional_iterator_category<_InputIter>::value || __n >= 0,
                           "Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
   std::__advance(__i, __n, typename iterator_traits<_InputIter>::iterator_category());
 }
@@ -98,9 +97,8 @@ struct __advance {
   // Preconditions: If `I` does not model `bidirectional_iterator`, `n` is not negative.
   template <input_or_output_iterator _Ip>
   _LIBCPP_HIDE_FROM_ABI constexpr void operator()(_Ip& __i, iter_difference_t<_Ip> __n) const {
-    // Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
-    _LIBCPP_ASSERT_PEDANTIC(
-        __n >= 0 || bidirectional_iterator<_Ip>, "If `n < 0`, then `bidirectional_iterator<I>` must be true.");
+    _LIBCPP_ASSERT_PEDANTIC(bidirectional_iterator<_Ip> || __n >= 0,
+                            "ranges::advance: Can only pass a negative `n` with a bidirectional_iterator.");
 
     // If `I` models `random_access_iterator`, equivalent to `i += n`.
     if constexpr (random_access_iterator<_Ip>) {
@@ -149,9 +147,9 @@ struct __advance {
   template <input_or_output_iterator _Ip, sentinel_for<_Ip> _Sp>
   _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip>
   operator()(_Ip& __i, iter_difference_t<_Ip> __n, _Sp __bound_sentinel) const {
-    // Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
-    _LIBCPP_ASSERT_PEDANTIC((__n >= 0) || (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>),
-                            "If `n < 0`, then `bidirectional_iterator<I> && same_as<I, S>` must be true.");
+    _LIBCPP_ASSERT_PEDANTIC(
+        (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>) || (__n >= 0),
+        "ranges::advance: Can only pass a negative `n` with a bidirectional_iterator coming from a common_range.");
     // If `S` and `I` model `sized_sentinel_for<S, I>`:
     if constexpr (sized_sentinel_for<_Sp, _Ip>) {
       // If |n| >= |bound_sentinel - i|, equivalent to `ranges::advance(i, bound_sentinel)`.
diff --git a/libcxx/include/__iterator/next.h b/libcxx/include/__iterator/next.h
index 1f68a5bec8f39..1143ab31ff7c2 100644
--- a/libcxx/include/__iterator/next.h
+++ b/libcxx/include/__iterator/next.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___ITERATOR_NEXT_H
 #define _LIBCPP___ITERATOR_NEXT_H
 
-#include <__assert>
 #include <__config>
 #include <__iterator/advance.h>
 #include <__iterator/concepts.h>
@@ -27,11 +26,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
 next(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n = 1) {
-  // Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
-  // Note that this check duplicates the similar check in `std::advance`.
-  _LIBCPP_ASSERT_PEDANTIC(__n >= 0 || __has_bidirectional_iterator_category<_InputIter>::value,
-                          "Attempt to next(it, n) with negative n on a non-bidirectional iterator");
-
   std::advance(__x, __n);
   return __x;
 }
diff --git a/libcxx/include/__iterator/prev.h b/libcxx/include/__iterator/prev.h
index bffd5527dc953..97139e067c896 100644
--- a/libcxx/include/__iterator/prev.h
+++ b/libcxx/include/__iterator/prev.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___ITERATOR_PREV_H
 #define _LIBCPP___ITERATOR_PREV_H
 
-#include <__assert>
 #include <__config>
 #include <__iterator/advance.h>
 #include <__iterator/concepts.h>
@@ -31,10 +30,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
 prev(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n) {
-  // Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
-  // Note that this check duplicates the similar check in `std::advance`.
-  _LIBCPP_ASSERT_PEDANTIC(__n <= 0 || __has_bidirectional_iterator_category<_InputIter>::value,
-                          "Attempt to prev(it, n) with a positive n on a non-bidirectional iterator");
   std::advance(__x, -__n);
   return __x;
 }

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit surprised there are no failing tests.

LGTM modulo one comment.

Copy link

github-actions bot commented Apr 3, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- libcxx/include/__iterator/advance.h libcxx/include/__iterator/next.h libcxx/include/__iterator/prev.h libcxx/test/libcxx/iterators/assert.next.pass.cpp libcxx/test/libcxx/iterators/assert.prev.pass.cpp
View the diff from clang-format here.
diff --git a/libcxx/test/libcxx/iterators/assert.next.pass.cpp b/libcxx/test/libcxx/iterators/assert.next.pass.cpp
index 1e8672308..927070075 100644
--- a/libcxx/test/libcxx/iterators/assert.next.pass.cpp
+++ b/libcxx/test/libcxx/iterators/assert.next.pass.cpp
@@ -25,7 +25,8 @@ int main(int, char**) {
     forward_iterator<int *> it(a+1);
     (void)std::next(it, 1); // should work fine
     (void)std::next(it, 0); // should work fine
-    TEST_LIBCPP_ASSERT_FAILURE(std::next(it, -1), "Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        std::next(it, -1), "Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
 
     return 0;
 }
diff --git a/libcxx/test/libcxx/iterators/assert.prev.pass.cpp b/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
index 29b8d6ed5..34b8a7365 100644
--- a/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
+++ b/libcxx/test/libcxx/iterators/assert.prev.pass.cpp
@@ -31,7 +31,8 @@ int main(int, char**) {
     forward_iterator<int *> it(a+1);
     (void)std::prev(it, -1); // should work fine
     (void)std::prev(it, 0);  // should work fine
-    TEST_LIBCPP_ASSERT_FAILURE(std::prev(it, 1), "Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        std::prev(it, 1), "Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
 
     return 0;
 }

@ldionne ldionne merged commit d05ab11 into llvm:main May 6, 2025
81 of 82 checks passed
@ldionne ldionne deleted the review/rework-advance-preconditions branch May 6, 2025 19:44
ldionne added a commit to ldionne/llvm-project that referenced this pull request May 6, 2025
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
…vance() (llvm#133276)

The std::advance function has a clear precondition that it can only be
called with a negative distance when a bidirectional iterator is used.
However, prev() and next() don't have such preconditions explicitly,
they inherit it from calling advance().

This patch removes assertions in prev() and next() that were duplicates
of similar ones in advance(), and removes a copy-pasted comment that was
trying to justify the use of _LIBCPP_ASSERT_PEDANTIC but IMO is creating
confusion with little benefit.
ldionne added a commit that referenced this pull request May 7, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hardening Issues related to the hardening effort libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants