Skip to content
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

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

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.

@@ -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");
Copy link
Member

Choose a reason for hiding this comment

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

This makes the message consistent with what's used in ranges.

Suggested change
"Attempt to advance(it, n) with negative n on a non-bidirectional iterator");
"std::advance: Can only pass a negative `n` with a bidirectional_iterator.");

Copy link
Contributor

Choose a reason for hiding this comment

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

This is an unrelated change though. I'd much rather have this in a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll do this as a follow-up.

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;
 }

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