llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.05k stars 11.58k forks source link

[libc++] std::prev(it) should not compile for a non-bidi iterator #109456

Open ldionne opened 3 hours ago

ldionne commented 3 hours ago

std::prev(it, n) could conceivably be well-formed for a non-bidirectional it if n is negative, however std::prev(it) is always UB, and we can easily diagnose it at compile-time. Right now std::prev(non_bidi) is a no-op, which is absolutely vexing.

https://godbolt.org/z/zaG3jTEqP

Potential patch:

diff --git a/libcxx/include/__iterator/prev.h b/libcxx/include/__iterator/prev.h
index e950d8dc4147..98883188312a 100644
--- a/libcxx/include/__iterator/prev.h
+++ b/libcxx/include/__iterator/prev.h
@@ -26,7 +26,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD

 template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
-prev(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n = 1) {
+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,
@@ -35,6 +35,12 @@ prev(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n =
   return __x;
 }

+template <class _BidirectionalIterator,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirectionalIterator>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _BidirectionalIterator prev(_BidirectionalIterator __it) {
+  return std::prev(std::move(__it), 1);
+}
+
 #if _LIBCPP_STD_VER >= 20

 // [range.iter.op.prev]
philnik777 commented 3 hours ago

I think it would be better to static_assert here, since this isn't a constraint.