Improvements to valid range checks in debug mode

Message ID 20200227130636.GV9441@redhat.com
State New
Headers show
Series
  • Improvements to valid range checks in debug mode
Related show

Commit Message

Jonathan Wakely Feb. 27, 2020, 1:06 p.m.
These should wait for stage 1 but I'm posting them now for comment.

With the change to __gnu_debug::__valid_range we now get a debug
assertion for:

   std::string s;
   std::min_element(std::string::iterator{}, s.end());

where previously it would just crash with undefined behaviour.

Patch

commit 77a610b7e88635ee7c63d82cc30fad9c80abebea
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Feb 27 11:17:31 2020 +0000

    libstdc++: Minor optimization for min/max/minmax
    
    By calling the internal __min_element (or __max_element or
    __minmax_element) function directly we avoid a function call and the
    valid range checks that are redundant when the range is defined by an
    initializer_list.
    
            * include/bits/stl_algo.h (min(initializer_list<T>))
            (min(initializer_list<T>, Compare)): Call __min_element directly to
            avoid redundant debug checks for valid ranges.
            (max(initializer_list<T>), max(initializer_list<T>, Compare)):
            Likewise, for __max_element.
            (minmax(initializer_list<T>), minmax(initializer_list<T>, Compare)):
            Likewise, for __minmax_element.

diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
index 6503d1518d3..d5eed9c47f6 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -3543,38 +3543,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				   __gnu_cxx::__ops::__iter_comp_iter(__comp));
     }
 
-  // N2722 + DR 915.
-  template<typename _Tp>
-    _GLIBCXX14_CONSTEXPR
-    inline _Tp
-    min(initializer_list<_Tp> __l)
-    { return *std::min_element(__l.begin(), __l.end()); }
-
-  template<typename _Tp, typename _Compare>
-    _GLIBCXX14_CONSTEXPR
-    inline _Tp
-    min(initializer_list<_Tp> __l, _Compare __comp)
-    { return *std::min_element(__l.begin(), __l.end(), __comp); }
-
-  template<typename _Tp>
-    _GLIBCXX14_CONSTEXPR
-    inline _Tp
-    max(initializer_list<_Tp> __l)
-    { return *std::max_element(__l.begin(), __l.end()); }
-
-  template<typename _Tp, typename _Compare>
-    _GLIBCXX14_CONSTEXPR
-    inline _Tp
-    max(initializer_list<_Tp> __l, _Compare __comp)
-    { return *std::max_element(__l.begin(), __l.end(), __comp); }
-
   template<typename _Tp>
     _GLIBCXX14_CONSTEXPR
     inline pair<_Tp, _Tp>
     minmax(initializer_list<_Tp> __l)
     {
+      __glibcxx_requires_irreflexive(__l.begin(), __l.end);
       pair<const _Tp*, const _Tp*> __p =
-	std::minmax_element(__l.begin(), __l.end());
+	std::__minmax_element(__l.begin(), __l.end(),
+			      __gnu_cxx::__ops::__iter_less_iter());
       return std::make_pair(*__p.first, *__p.second);
     }
 
@@ -3583,8 +3560,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline pair<_Tp, _Tp>
     minmax(initializer_list<_Tp> __l, _Compare __comp)
     {
+      __glibcxx_requires_irreflexive_pred(__l.begin(), __l.end, __comp);
       pair<const _Tp*, const _Tp*> __p =
-	std::minmax_element(__l.begin(), __l.end(), __comp);
+	std::__minmax_element(__l.begin(), __l.end(),
+			      __gnu_cxx::__ops::__iter_comp_iter(__comp));
       return std::make_pair(*__p.first, *__p.second);
     }
 
@@ -3959,7 +3938,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
 	std::iter_swap(__i, __first + __d(__g, __p_type(0, __i - __first)));
     }
-#endif
+#endif // USE C99_STDINT
 
 #endif // C++11
 
@@ -5902,6 +5881,49 @@  _GLIBCXX_BEGIN_NAMESPACE_ALGO
 				__gnu_cxx::__ops::__iter_comp_iter(__comp));
     }
 
+#if __cplusplus >= 201103L
+  // N2722 + DR 915.
+  template<typename _Tp>
+    _GLIBCXX14_CONSTEXPR
+    inline _Tp
+    min(initializer_list<_Tp> __l)
+    {
+      __glibcxx_requires_irreflexive(__l.begin(), __l.end);
+      return *_GLIBCXX_STD_A::__min_element(__l.begin(), __l.end(),
+	  __gnu_cxx::__ops::__iter_less_iter());
+    }
+
+  template<typename _Tp, typename _Compare>
+    _GLIBCXX14_CONSTEXPR
+    inline _Tp
+    min(initializer_list<_Tp> __l, _Compare __comp)
+    {
+      __glibcxx_requires_irreflexive_pred(__l.begin(), __l.end, __comp);
+      return *_GLIBCXX_STD_A::__min_element(__l.begin(), __l.end(),
+	  __gnu_cxx::__ops::__iter_comp_iter(__comp));
+    }
+
+  template<typename _Tp>
+    _GLIBCXX14_CONSTEXPR
+    inline _Tp
+    max(initializer_list<_Tp> __l)
+    {
+      __glibcxx_requires_irreflexive(__l.begin(), __l.end);
+      return *_GLIBCXX_STD_A::__max_element(__l.begin(), __l.end(),
+	  __gnu_cxx::__ops::__iter_less_iter());
+    }
+
+  template<typename _Tp, typename _Compare>
+    _GLIBCXX14_CONSTEXPR
+    inline _Tp
+    max(initializer_list<_Tp> __l, _Compare __comp)
+    {
+      __glibcxx_requires_irreflexive_pred(__l.begin(), __l.end, __comp);
+      return *_GLIBCXX_STD_A::__max_element(__l.begin(), __l.end(),
+	  __gnu_cxx::__ops::__iter_comp_iter(__comp));
+    }
+#endif // C++11
+
 #if __cplusplus >= 201402L
   /// Reservoir sampling algorithm.
   template<typename _InputIterator, typename _RandomAccessIterator,

commit b183bd10b7ac1f20cc4ec60ebe621c3177297d52
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Feb 27 11:20:54 2020 +0000

    libstdc++: Improve check for valid forward iterator range
    
    Since C++14 we can assume that value-initialized forward iterators are
    not part of a valid range (except the special case of an empty range
    defined by two value-initialized iterators).
    
    This covers the case of null pointers which was checked by the overload
    for input iterators, but also handles non-pointers.
    
            * include/debug/helper_functions.h (__valid_range_aux): Add overload
            for forward iterators that checks for value-initialized iterators.

diff --git a/libstdc++-v3/include/debug/helper_functions.h b/libstdc++-v3/include/debug/helper_functions.h
index 251582d5922..3e016c7643e 100644
--- a/libstdc++-v3/include/debug/helper_functions.h
+++ b/libstdc++-v3/include/debug/helper_functions.h
@@ -161,6 +161,28 @@  namespace __gnu_debug
 	|| (!__check_singular(__first) && !__check_singular(__last));
     }
 
+#if __cplusplus >= 201402L
+  template<typename _ForwardIterator>
+    constexpr bool
+    __valid_range_aux(_ForwardIterator __first, _ForwardIterator __last,
+		      std::forward_iterator_tag)
+    {
+      if (__first == __last)
+	return true;
+
+      // Value-initialized forward iterators cannot form a non-empty range.
+      if (__first == _ForwardIterator() || __last == _ForwardIterator())
+	return false;
+
+      // Hook for checking if safe iterators are singular.
+      if (__check_singular_aux(std::__addressof(__first))
+	  || __check_singular_aux(std::__addressof(__last)))
+	return false;
+
+      return true;
+    }
+#endif
+
   template<typename _InputIterator>
     _GLIBCXX_CONSTEXPR
     inline bool
@@ -168,7 +190,7 @@  namespace __gnu_debug
 		      std::random_access_iterator_tag)
     {
       return
-	__valid_range_aux(__first, __last, std::input_iterator_tag())
+	__valid_range_aux(__first, __last, std::forward_iterator_tag())
 	&& __first <= __last;
     }