From e87479b00fcc852a54b79d2fd7f8d779e2b75f68 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 16 Apr 2021 17:49:57 -0400 Subject: [PATCH] [libc++] Remove the special logic for "noexcept iterators" in basic_string. This reverts a large chunk of http://reviews.llvm.org/D15862 , and also fixes bugs in `insert`, `append`, and `assign`, which are now regression-tested. (Thanks to Tim Song for pointing out the bug in `append`!) Before this patch, we did a special dance in `append`, `assign`, and `insert` (but not `replace`). All of these require the strong exception guarantee, even when the user-provided InputIterator might have throwing operations. The naive way to accomplish this is to construct a temporary string and then append/assign/insert from the temporary; i.e., finish all the potentially throwing and self-inspecting InputIterator operations *before* starting to modify self. But this is slow, so we'd like to skip it when possible. The old code (D15682) attempted to check that specific iterator operations were nothrow: it assumed that if the iterator operations didn't throw, then it was safe to iterate the input range multiple times and therefore it was safe to use the fast-path non-naive version. This was wrong for two reasons: (1) the old code checked the wrong operations (e.g. checked noexceptness of `==`, but the code that ran used `!=`), and (2) the conversion of value_type to char could still throw, or inspect the contents of self. The new code is much simpler, although still much more complicated than it really could be. We'll likely revisit this codepath at some point, but for now this patch suffices to get it passing all the new regression tests. The added tests all fail before this patch, and succeed afterward. See https://quuxplusone.github.io/blog/2021/04/17/pathological-string-appends/ Differential Revision: https://reviews.llvm.org/D98573 --- libcxx/include/filesystem | 2 +- libcxx/include/iterator | 17 -- libcxx/include/string | 200 ++++++++++----------- .../libcxx/iterators/trivial_iterators.pass.cpp | 195 -------------------- .../libcxx/strings/iterators.exceptions.pass.cpp | 89 --------- .../libcxx/strings/iterators.noexcept.pass.cpp | 81 --------- .../string.modifiers/robust_against_adl.pass.cpp | 35 ++++ .../string_append/iterator.pass.cpp | 22 +++ .../string_assign/iterator.pass.cpp | 14 +- .../string_insert/iter_iter_iter.pass.cpp | 18 ++ .../string_replace/iter_iter_iter_iter.pass.cpp | 5 + 11 files changed, 191 insertions(+), 487 deletions(-) delete mode 100644 libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp delete mode 100644 libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp delete mode 100644 libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp create mode 100644 libcxx/test/std/strings/basic.string/string.modifiers/robust_against_adl.pass.cpp diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem index 3caaca0..50fb353 100644 --- a/libcxx/include/filesystem +++ b/libcxx/include/filesystem @@ -792,7 +792,7 @@ struct _PathCVT<__path_value> { template static typename enable_if<__is_cpp17_forward_iterator<_Iter>::value>::type __append_range(__path_string& __dest, _Iter __b, _Iter __e) { - __dest.__append_forward_unsafe(__b, __e); + __dest.append(__b, __e); } template diff --git a/libcxx/include/iterator b/libcxx/include/iterator index d9a0309..74d04ce 100644 --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -1587,23 +1587,6 @@ operator+(typename __wrap_iter<_Iter>::difference_type __n, return __x; } -template -struct __libcpp_is_trivial_iterator - : public _LIBCPP_BOOL_CONSTANT(is_pointer<_Iter>::value) {}; - -template -struct __libcpp_is_trivial_iterator > - : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {}; - -template -struct __libcpp_is_trivial_iterator > - : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {}; - -template -struct __libcpp_is_trivial_iterator<__wrap_iter<_Iter> > - : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {}; - - template _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 _Tp* diff --git a/libcxx/include/string b/libcxx/include/string index bf3b64c..d233369 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -632,29 +632,16 @@ __basic_string_common<__b>::__throw_out_of_range() const _LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __basic_string_common) -#ifdef _LIBCPP_NO_EXCEPTIONS template -struct __libcpp_string_gets_noexcept_iterator_impl : public true_type {}; -#elif defined(_LIBCPP_HAS_NO_NOEXCEPT) -template -struct __libcpp_string_gets_noexcept_iterator_impl : public false_type {}; -#else -template ::value> -struct __libcpp_string_gets_noexcept_iterator_impl : public _LIBCPP_BOOL_CONSTANT(( - noexcept(++(declval<_Iter&>())) && - is_nothrow_assignable<_Iter&, _Iter>::value && - noexcept(declval<_Iter>() == declval<_Iter>()) && - noexcept(*declval<_Iter>()) -)) {}; - -template -struct __libcpp_string_gets_noexcept_iterator_impl<_Iter, false> : public false_type {}; -#endif +struct __string_is_trivial_iterator : public false_type {}; +template +struct __string_is_trivial_iterator<_Tp*> + : public is_arithmetic<_Tp> {}; template -struct __libcpp_string_gets_noexcept_iterator - : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value || __libcpp_string_gets_noexcept_iterator_impl<_Iter>::value) {}; +struct __string_is_trivial_iterator<__wrap_iter<_Iter> > + : public __string_is_trivial_iterator<_Iter> {}; template struct __can_be_converted_to_string_view : public _BoolConstant< @@ -1048,20 +1035,16 @@ public: _LIBCPP_INLINE_VISIBILITY void __append_default_init(size_type __n); - template - _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS - basic_string& __append_forward_unsafe(_ForwardIterator, _ForwardIterator); template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, basic_string& > _LIBCPP_INLINE_VISIBILITY append(_InputIterator __first, _InputIterator __last) { - const basic_string __temp (__first, __last, __alloc()); + const basic_string __temp(__first, __last, __alloc()); append(__temp.data(), __temp.size()); return *this; } @@ -1069,14 +1052,11 @@ public: _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, basic_string& > _LIBCPP_INLINE_VISIBILITY - append(_ForwardIterator __first, _ForwardIterator __last) { - return __append_forward_unsafe(__first, __last); - } + append(_ForwardIterator __first, _ForwardIterator __last); #ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY @@ -1124,8 +1104,7 @@ public: _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, basic_string& > assign(_InputIterator __first, _InputIterator __last); @@ -1133,8 +1112,7 @@ public: _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, basic_string& > assign(_ForwardIterator __first, _ForwardIterator __last); @@ -1175,8 +1153,7 @@ public: _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, iterator > insert(const_iterator __pos, _InputIterator __first, _InputIterator __last); @@ -1184,8 +1161,7 @@ public: _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, iterator > insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last); @@ -1721,6 +1697,13 @@ private: _LIBCPP_INLINE_VISIBILITY void __invalidate_all_iterators(); _LIBCPP_INLINE_VISIBILITY void __invalidate_iterators_past(size_type); + template + _LIBCPP_INLINE_VISIBILITY + bool __addr_in_range(_Tp&& __t) const { + const volatile void *__p = _VSTD::addressof(__t); + return data() <= __p && __p <= data() + size(); + } + friend basic_string operator+<>(const basic_string&, const basic_string&); friend basic_string operator+<>(const value_type*, const basic_string&); friend basic_string operator+<>(value_type, const basic_string&); @@ -2175,9 +2158,23 @@ basic_string<_CharT, _Traits, _Allocator>::__init(_ForwardIterator __first, _For __set_long_cap(__cap+1); __set_long_size(__sz); } + +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS for (; __first != __last; ++__first, (void) ++__p) traits_type::assign(*__p, *__first); traits_type::assign(*__p, value_type()); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + if (__is_long()) + __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); + throw; + } +#endif // _LIBCPP_NO_EXCEPTIONS } template @@ -2470,8 +2467,7 @@ template template _EnableIf < - __is_exactly_cpp17_input_iterator <_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, basic_string<_CharT, _Traits, _Allocator>& > basic_string<_CharT, _Traits, _Allocator>::assign(_InputIterator __first, _InputIterator __last) @@ -2485,26 +2481,36 @@ template template _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, basic_string<_CharT, _Traits, _Allocator>& > basic_string<_CharT, _Traits, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last) { - size_type __n = static_cast(_VSTD::distance(__first, __last)); size_type __cap = capacity(); - if (__cap < __n) + size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value ? + static_cast(_VSTD::distance(__first, __last)) : 0; + + if (__string_is_trivial_iterator<_ForwardIterator>::value && + (__cap >= __n || !__addr_in_range(*__first))) { - size_type __sz = size(); - __grow_by(__cap, __n - __cap, __sz, 0, __sz); + if (__cap < __n) + { + size_type __sz = size(); + __grow_by(__cap, __n - __cap, __sz, 0, __sz); + } + else + __invalidate_iterators_past(__n); + pointer __p = __get_pointer(); + for (; __first != __last; ++__first, ++__p) + traits_type::assign(*__p, *__first); + traits_type::assign(*__p, value_type()); + __set_size(__n); } else - __invalidate_iterators_past(__n); - pointer __p = __get_pointer(); - for (; __first != __last; ++__first, ++__p) - traits_type::assign(*__p, *__first); - traits_type::assign(*__p, value_type()); - __set_size(__n); + { + const basic_string __temp(__first, __last, __alloc()); + assign(__temp.data(), __temp.size()); + } return *this; } @@ -2651,39 +2657,23 @@ basic_string<_CharT, _Traits, _Allocator>::push_back(value_type __c) traits_type::assign(*++__p, value_type()); } -template -bool __ptr_in_range (const _Tp* __p, const _Tp* __first, const _Tp* __last) -{ - return __first <= __p && __p < __last; -} - -template -bool __ptr_in_range (const _Tp1*, const _Tp2*, const _Tp2*) -{ - return false; -} - template template -basic_string<_CharT, _Traits, _Allocator>& -basic_string<_CharT, _Traits, _Allocator>::__append_forward_unsafe( +_EnableIf +< + __is_cpp17_forward_iterator<_ForwardIterator>::value, + basic_string<_CharT, _Traits, _Allocator>& +> +basic_string<_CharT, _Traits, _Allocator>::append( _ForwardIterator __first, _ForwardIterator __last) { - static_assert(__is_cpp17_forward_iterator<_ForwardIterator>::value, - "function requires a ForwardIterator"); size_type __sz = size(); size_type __cap = capacity(); size_type __n = static_cast(_VSTD::distance(__first, __last)); if (__n) { - typedef typename iterator_traits<_ForwardIterator>::reference _CharRef; - _CharRef __tmp_ref = *__first; - if (__ptr_in_range(_VSTD::addressof(__tmp_ref), data(), data() + size())) - { - const basic_string __temp (__first, __last, __alloc()); - append(__temp.data(), __temp.size()); - } - else + if (__string_is_trivial_iterator<_ForwardIterator>::value && + !__addr_in_range(*__first)) { if (__cap - __sz < __n) __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0); @@ -2693,6 +2683,11 @@ basic_string<_CharT, _Traits, _Allocator>::__append_forward_unsafe( traits_type::assign(*__p, value_type()); __set_size(__sz + __n); } + else + { + const basic_string __temp(__first, __last, __alloc()); + append(__temp.data(), __temp.size()); + } } return *this; } @@ -2808,8 +2803,7 @@ template template _EnableIf < - __is_exactly_cpp17_input_iterator<_InputIterator>::value - || !__libcpp_string_gets_noexcept_iterator<_InputIterator>::value, + __is_exactly_cpp17_input_iterator<_InputIterator>::value, typename basic_string<_CharT, _Traits, _Allocator>::iterator > basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _InputIterator __first, _InputIterator __last) @@ -2827,8 +2821,7 @@ template template _EnableIf < - __is_cpp17_forward_iterator<_ForwardIterator>::value - && __libcpp_string_gets_noexcept_iterator<_ForwardIterator>::value, + __is_cpp17_forward_iterator<_ForwardIterator>::value, typename basic_string<_CharT, _Traits, _Allocator>::iterator > basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _ForwardIterator __first, _ForwardIterator __last) @@ -2842,34 +2835,35 @@ basic_string<_CharT, _Traits, _Allocator>::insert(const_iterator __pos, _Forward size_type __n = static_cast(_VSTD::distance(__first, __last)); if (__n) { - typedef typename iterator_traits<_ForwardIterator>::reference _CharRef; - _CharRef __tmp_char = *__first; - if (__ptr_in_range(_VSTD::addressof(__tmp_char), data(), data() + size())) + if (__string_is_trivial_iterator<_ForwardIterator>::value && + !__addr_in_range(*__first)) { - const basic_string __temp(__first, __last, __alloc()); - return insert(__pos, __temp.data(), __temp.data() + __temp.size()); - } - - size_type __sz = size(); - size_type __cap = capacity(); - value_type* __p; - if (__cap - __sz >= __n) - { - __p = _VSTD::__to_address(__get_pointer()); - size_type __n_move = __sz - __ip; - if (__n_move != 0) - traits_type::move(__p + __ip + __n, __p + __ip, __n_move); + size_type __sz = size(); + size_type __cap = capacity(); + value_type* __p; + if (__cap - __sz >= __n) + { + __p = _VSTD::__to_address(__get_pointer()); + size_type __n_move = __sz - __ip; + if (__n_move != 0) + traits_type::move(__p + __ip + __n, __p + __ip, __n_move); + } + else + { + __grow_by(__cap, __sz + __n - __cap, __sz, __ip, 0, __n); + __p = _VSTD::__to_address(__get_long_pointer()); + } + __sz += __n; + __set_size(__sz); + traits_type::assign(__p[__sz], value_type()); + for (__p += __ip; __first != __last; ++__p, ++__first) + traits_type::assign(*__p, *__first); } else { - __grow_by(__cap, __sz + __n - __cap, __sz, __ip, 0, __n); - __p = _VSTD::__to_address(__get_long_pointer()); + const basic_string __temp(__first, __last, __alloc()); + return insert(__pos, __temp.data(), __temp.data() + __temp.size()); } - __sz += __n; - __set_size(__sz); - traits_type::assign(__p[__sz], value_type()); - for (__p += __ip; __first != __last; ++__p, ++__first) - traits_type::assign(*__p, *__first); } return begin() + __ip; } diff --git a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp b/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp deleted file mode 100644 index f4cd359..0000000 --- a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp +++ /dev/null @@ -1,195 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// - -// - -// __libcpp_is_trivial_iterator - -// __libcpp_is_trivial_iterator determines if an iterator is a "trivial" one, -// that can be used w/o worrying about its operations throwing exceptions. -// Pointers are trivial iterators. Libc++ has three "iterator wrappers": -// reverse_iterator, move_iterator, and __wrap_iter. If the underlying iterator -// is trivial, then those are as well. -// - -#include -#include -#include -#include -#include - -#include "test_macros.h" -#include "test_iterators.h" - -#if TEST_STD_VER >= 11 -#define DELETE_FUNCTION = delete -#else -#define DELETE_FUNCTION -#endif - -class T; // incomplete - -class my_input_iterator_tag : public std::input_iterator_tag {}; - -template -class my_input_iterator -{ - It it_; - - template friend class my_input_iterator; -public: - typedef my_input_iterator_tag iterator_category; - typedef typename std::iterator_traits::value_type value_type; - typedef typename std::iterator_traits::difference_type difference_type; - typedef It pointer; - typedef typename std::iterator_traits::reference reference; - - It base() const {return it_;} - - my_input_iterator() : it_() {} - explicit my_input_iterator(It it) : it_(it) {} - template - my_input_iterator(const my_input_iterator& u) :it_(u.it_) {} - - reference operator*() const {return *it_;} - pointer operator->() const {return it_;} - - my_input_iterator& operator++() {++it_; return *this;} - my_input_iterator operator++(int) - {my_input_iterator tmp(*this); ++(*this); return tmp;} - - friend bool operator==(const my_input_iterator& x, const my_input_iterator& y) - {return x.it_ == y.it_;} - friend bool operator!=(const my_input_iterator& x, const my_input_iterator& y) - {return !(x == y);} - - template - void operator,(T const &) DELETE_FUNCTION; -}; - -template -inline -bool -operator==(const my_input_iterator& x, const my_input_iterator& y) -{ - return x.base() == y.base(); -} - -template -inline -bool -operator!=(const my_input_iterator& x, const my_input_iterator& y) -{ - return !(x == y); -} - - -int main(int, char**) -{ -// basic tests - static_assert(( std::__libcpp_is_trivial_iterator::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::value), ""); - - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator >::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator >::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator >::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator > ::value), ""); - - static_assert(( std::__libcpp_is_trivial_iterator > > ::value), ""); - -// iterators in the libc++ test suite - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - static_assert((!std::__libcpp_is_trivial_iterator >::value), ""); - - -// Iterator classification - static_assert(( std::__is_cpp17_input_iterator ::value), "" ); - static_assert(( std::__is_cpp17_forward_iterator ::value), "" ); - static_assert(( std::__is_cpp17_bidirectional_iterator::value), "" ); - static_assert(( std::__is_cpp17_random_access_iterator::value), "" ); - static_assert(( std::__is_cpp17_contiguous_iterator ::value), "" ); - static_assert((!std::__is_exactly_cpp17_input_iterator::value), "" ); - - static_assert(( std::__is_cpp17_input_iterator >::value), "" ); - static_assert((!std::__is_cpp17_forward_iterator >::value), "" ); - static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); - static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); - static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); - static_assert(( std::__is_exactly_cpp17_input_iterator >::value), "" ); - - static_assert(( std::__is_cpp17_input_iterator >::value), "" ); - static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); - static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); - static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); - static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); - static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); - - static_assert(( std::__is_cpp17_input_iterator >::value), "" ); - static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); - static_assert(( std::__is_cpp17_bidirectional_iterator >::value), "" ); - static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); - static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); - static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); - - static_assert(( std::__is_cpp17_input_iterator >::value), "" ); - static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); - static_assert(( std::__is_cpp17_bidirectional_iterator >::value), "" ); - static_assert(( std::__is_cpp17_random_access_iterator >::value), "" ); - static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); - static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); - - static_assert(( std::__is_cpp17_input_iterator >::value), "" ); - static_assert((!std::__is_cpp17_forward_iterator >::value), "" ); - static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); - static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); - static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); - static_assert(( std::__is_exactly_cpp17_input_iterator >::value), "" ); - -// -// iterators from libc++'s containers -// - -// vector - static_assert(( std::__libcpp_is_trivial_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::const_reverse_iterator>::value), ""); - -// string - static_assert(( std::__libcpp_is_trivial_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::const_reverse_iterator>::value), ""); - -#if TEST_STD_VER >= 11 -// Initializer list (which has no reverse iterators) - static_assert(( std::__libcpp_is_trivial_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_is_trivial_iterator::const_iterator> ::value), ""); -#endif - - - return 0; -} diff --git a/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp b/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp deleted file mode 100644 index d281cb2..0000000 --- a/libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// - -// __libcpp_is_trivial_iterator - -// __libcpp_string_gets_noexcept_iterator determines if an iterator can be used -// w/o worrying about whether or not certain operations can throw. -// This gives us a "fast path for string operations" -// - -#include -#include -#include -#include -#include - -#include "test_macros.h" -#include "test_iterators.h" - -#ifndef TEST_HAS_NO_EXCEPTIONS -static const bool expected = false; -#else -// Under -fno-exceptions all noexcept expressions are trivially true, so -// any check for a noexcept returning false must actually check for it being -// true. -static const bool expected = true; -#endif - -int main(int, char**) -{ -// basic tests - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > > ::value), ""); - -// iterators in the libc++ test suite - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); - -#if TEST_STD_VER >= 11 - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); -#else - static_assert(std::__libcpp_string_gets_noexcept_iterator >::value == expected, ""); -#endif - -// -// iterators from libc++'s containers -// - -// string - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -// vector - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -#if TEST_STD_VER >= 11 -// Initializer list (which has no reverse iterators) - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); -#endif - - return 0; -} diff --git a/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp b/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp deleted file mode 100644 index 78f8429..0000000 --- a/libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// - -// - -// __libcpp_is_trivial_iterator - -// __libcpp_string_gets_noexcept_iterator determines if an iterator can be used -// w/o worrying about whether or not certain operations can throw. -// This gives us a "fast path for string operations". -// -// When exceptions are disabled, all iterators should get this "fast path" -// - -// ADDITIONAL_COMPILE_FLAGS: -fno-exceptions - -#include -#include -#include -#include -#include - -#include "test_macros.h" -#include "test_iterators.h" - -int main(int, char**) -{ -// basic tests - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator > > ::value), ""); - -// iterators in the libc++ test suite - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - - static_assert(( std::__libcpp_string_gets_noexcept_iterator >::value), ""); - -// -// iterators from libc++'s containers -// - -// string - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -// vector - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::reverse_iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_reverse_iterator>::value), ""); - -#if TEST_STD_VER >= 11 -// Initializer list (which has no reverse iterators) - static_assert(( std::__libcpp_string_gets_noexcept_iterator::iterator> ::value), ""); - static_assert(( std::__libcpp_string_gets_noexcept_iterator::const_iterator> ::value), ""); -#endif - - return 0; -} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/robust_against_adl.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/robust_against_adl.pass.cpp new file mode 100644 index 0000000..120f533 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.modifiers/robust_against_adl.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +#include +#include + +struct Incomplete; +template struct Holder { T t; }; + +template +struct Charlike { + char ch_; + Charlike(char ch) : ch_(ch) {} + operator char() const { return ch_; } +}; + +int main(int, char**) +{ + std::string s; + Charlike > a[] = {'m', 'a', 'h', 'i'}; + s.append(a, a+4); + s.assign(a, a+4); + s.insert(s.begin(), a, a+4); + s.replace(s.begin(), s.begin()+4, a, a+4); + assert(s == "mahimahi"); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp index d611260..8c0d3d7 100644 --- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp @@ -28,6 +28,8 @@ test(S s, It first, It last, S expected) } #ifndef TEST_HAS_NO_EXCEPTIONS +struct Widget { operator char() const { throw 42; } }; + template void test_exceptions(S s, It first, It last) @@ -176,6 +178,9 @@ int main(int, char**) test_exceptions(S(), TIter(s, s+10, 4, TIter::TAIncrement), TIter()); test_exceptions(S(), TIter(s, s+10, 5, TIter::TADereference), TIter()); test_exceptions(S(), TIter(s, s+10, 6, TIter::TAComparison), TIter()); + + Widget w[100]; + test_exceptions(S(), w, w+100); } #endif @@ -204,6 +209,23 @@ int main(int, char**) assert(s == "ABCD"); } + { // regression-test appending to self in sneaky ways + std::string s_short = "hello"; + std::string s_long = "Lorem ipsum dolor sit amet, consectetur/"; + std::string s_othertype = "hello"; + const unsigned char *first = reinterpret_cast(s_othertype.data()); + std::string s_sneaky = "hello"; + + test(s_short, s_short.data() + s_short.size(), s_short.data() + s_short.size() + 1, + std::string("hello\0", 6)); + test(s_long, s_long.data() + s_long.size(), s_long.data() + s_long.size() + 1, + std::string("Lorem ipsum dolor sit amet, consectetur/\0", 41)); + test(s_othertype, first + 2, first + 5, std::string("hellollo")); + + s_sneaky.reserve(12); + test(s_sneaky, s_sneaky.data(), s_sneaky.data() + 6, std::string("hellohello\0", 11)); + } + { // test with a move iterator that returns char&& typedef forward_iterator It; typedef std::move_iterator MoveIt; diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp index c03b5ef..a4bad33 100644 --- a/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp @@ -28,6 +28,8 @@ test(S s, It first, It last, S expected) } #ifndef TEST_HAS_NO_EXCEPTIONS +struct Widget { operator char() const { throw 42; } }; + template void test_exceptions(S s, It first, It last) @@ -176,6 +178,9 @@ int main(int, char**) test_exceptions(S(), TIter(s, s+10, 4, TIter::TAIncrement), TIter()); test_exceptions(S(), TIter(s, s+10, 5, TIter::TADereference), TIter()); test_exceptions(S(), TIter(s, s+10, 6, TIter::TAComparison), TIter()); + + Widget w[100]; + test_exceptions(S(), w, w+100); } #endif @@ -205,5 +210,12 @@ int main(int, char**) assert(s == "ABCD"); } - return 0; + { // regression-test assigning to self in sneaky ways + std::string sneaky = "hello"; + sneaky.resize(sneaky.capacity(), 'x'); + std::string expected = sneaky + std::string(1, '\0'); + test(sneaky, sneaky.data(), sneaky.data() + sneaky.size() + 1, expected); + } + + return 0; } diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp index 67ba33d..471e301 100644 --- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp @@ -30,6 +30,8 @@ test(S s, typename S::difference_type pos, It first, It last, S expected) } #ifndef TEST_HAS_NO_EXCEPTIONS +struct Widget { operator char() const { throw 42; } }; + template void test_exceptions(S s, typename S::difference_type pos, It first, It last) @@ -153,6 +155,9 @@ int main(int, char**) test_exceptions(S(), 0, TIter(s, s+10, 4, TIter::TAIncrement), TIter()); test_exceptions(S(), 0, TIter(s, s+10, 5, TIter::TADereference), TIter()); test_exceptions(S(), 0, TIter(s, s+10, 6, TIter::TAComparison), TIter()); + + Widget w[100]; + test_exceptions(S(), 0, w, w+100); } #endif @@ -181,6 +186,19 @@ int main(int, char**) assert(s == "ABCD"); } + { // regression-test inserting into self in sneaky ways + std::string s_short = "hello"; + std::string s_long = "Lorem ipsum dolor sit amet, consectetur/"; + std::string s_othertype = "hello"; + const unsigned char *first = reinterpret_cast(s_othertype.data()); + + test(s_short, 0, s_short.data() + s_short.size(), s_short.data() + s_short.size() + 1, + std::string("\0hello", 6)); + test(s_long, 0, s_long.data() + s_long.size(), s_long.data() + s_long.size() + 1, + std::string("\0Lorem ipsum dolor sit amet, consectetur/", 41)); + test(s_othertype, 1, first + 2, first + 5, std::string("hlloello")); + } + { // test with a move iterator that returns char&& typedef input_iterator It; typedef std::move_iterator MoveIt; diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_iter_iter.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_iter_iter.pass.cpp index 1d4a46b..3e6907a 100644 --- a/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_iter_iter.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_iter_iter.pass.cpp @@ -36,6 +36,8 @@ test(S s, typename S::size_type pos1, typename S::size_type n1, It f, It l, S ex } #ifndef TEST_HAS_NO_EXCEPTIONS +struct Widget { operator char() const { throw 42; } }; + template void test_exceptions(S s, typename S::size_type pos1, typename S::size_type n1, It f, It l) @@ -1005,6 +1007,9 @@ int main(int, char**) test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 4, TIter::TAIncrement), TIter()); test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 5, TIter::TADereference), TIter()); test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, TIter(s, s+10, 6, TIter::TAComparison), TIter()); + + Widget w[100]; + test_exceptions(S("abcdefghijklmnopqrst"), 10, 5, w, w+100); } #endif -- 2.7.4