[libcxx-commits] [libcxx] e87479b - [libc++] Remove the special logic for "noexcept iterators" in basic_string.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 26 13:23:29 PDT 2021


Author: Arthur O'Dwyer
Date: 2021-04-26T16:22:43-04:00
New Revision: e87479b00fcc852a54b79d2fd7f8d779e2b75f68

URL: https://github.com/llvm/llvm-project/commit/e87479b00fcc852a54b79d2fd7f8d779e2b75f68
DIFF: https://github.com/llvm/llvm-project/commit/e87479b00fcc852a54b79d2fd7f8d779e2b75f68.diff

LOG: [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

Added: 
    libcxx/test/std/strings/basic.string/string.modifiers/robust_against_adl.pass.cpp

Modified: 
    libcxx/include/filesystem
    libcxx/include/iterator
    libcxx/include/string
    libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
    libcxx/test/std/strings/basic.string/string.modifiers/string_assign/iterator.pass.cpp
    libcxx/test/std/strings/basic.string/string.modifiers/string_insert/iter_iter_iter.pass.cpp
    libcxx/test/std/strings/basic.string/string.modifiers/string_replace/iter_iter_iter_iter.pass.cpp

Removed: 
    libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp
    libcxx/test/libcxx/strings/iterators.exceptions.pass.cpp
    libcxx/test/libcxx/strings/iterators.noexcept.pass.cpp


################################################################################
diff  --git a/libcxx/include/filesystem b/libcxx/include/filesystem
index 3caaca0a3ef8f..50fb353017396 100644
--- a/libcxx/include/filesystem
+++ b/libcxx/include/filesystem
@@ -792,7 +792,7 @@ struct _PathCVT<__path_value> {
   template <class _Iter>
   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 <class _Iter>

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index d9a0309e0b93a..74d04cecf2a99 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -1587,23 +1587,6 @@ operator+(typename __wrap_iter<_Iter>::
diff erence_type __n,
     return __x;
 }
 
-template <class _Iter>
-struct __libcpp_is_trivial_iterator
-    : public _LIBCPP_BOOL_CONSTANT(is_pointer<_Iter>::value) {};
-
-template <class _Iter>
-struct __libcpp_is_trivial_iterator<move_iterator<_Iter> >
-    : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {};
-
-template <class _Iter>
-struct __libcpp_is_trivial_iterator<reverse_iterator<_Iter> >
-    : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {};
-
-template <class _Iter>
-struct __libcpp_is_trivial_iterator<__wrap_iter<_Iter> >
-    : public _LIBCPP_BOOL_CONSTANT(__libcpp_is_trivial_iterator<_Iter>::value) {};
-
-
 template <class _Tp, size_t _Np>
 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
 _Tp*

diff  --git a/libcxx/include/string b/libcxx/include/string
index bf3b64c0de927..d233369df1ab9 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<true>)
 
-#ifdef _LIBCPP_NO_EXCEPTIONS
 template <class _Iter>
-struct __libcpp_string_gets_noexcept_iterator_impl : public true_type {};
-#elif defined(_LIBCPP_HAS_NO_NOEXCEPT)
-template <class _Iter>
-struct __libcpp_string_gets_noexcept_iterator_impl : public false_type {};
-#else
-template <class _Iter, bool = __is_cpp17_forward_iterator<_Iter>::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 <class _Iter>
-struct __libcpp_string_gets_noexcept_iterator_impl<_Iter, false> : public false_type {};
-#endif
+struct __string_is_trivial_iterator : public false_type {};
 
+template <class _Tp>
+struct __string_is_trivial_iterator<_Tp*>
+    : public is_arithmetic<_Tp> {};
 
 template <class _Iter>
-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 <class _CharT, class _Traits, class _Tp>
 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 <class _ForwardIterator>
-    _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
-    basic_string& __append_forward_unsafe(_ForwardIterator, _ForwardIterator);
     template<class _InputIterator>
     _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<class _Tp>
+    _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 <class _CharT, class _Traits, class _Allocator>
@@ -2470,8 +2467,7 @@ template <class _CharT, class _Traits, class _Allocator>
 template<class _InputIterator>
 _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 <class _CharT, class _Traits, class _Allocator>
 template<class _ForwardIterator>
 _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<size_type>(_VSTD::distance(__first, __last));
     size_type __cap = capacity();
-    if (__cap < __n)
+    size_type __n = __string_is_trivial_iterator<_ForwardIterator>::value ?
+        static_cast<size_type>(_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 <class _Tp>
-bool __ptr_in_range (const _Tp* __p, const _Tp* __first, const _Tp* __last)
-{
-    return __first <= __p && __p < __last;
-}
-
-template <class _Tp1, class _Tp2>
-bool __ptr_in_range (const _Tp1*, const _Tp2*, const _Tp2*)
-{
-    return false;
-}
-
 template <class _CharT, class _Traits, class _Allocator>
 template<class _ForwardIterator>
-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<size_type>(_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 <class _CharT, class _Traits, class _Allocator>
 template<class _InputIterator>
 _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 <class _CharT, class _Traits, class _Allocator>
 template<class _ForwardIterator>
 _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<size_type>(_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 f4cd35911643d..0000000000000
--- 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
-//
-//===----------------------------------------------------------------------===//
-//
-
-// <iterator>
-
-// __libcpp_is_trivial_iterator<Tp>
-
-// __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 <iterator>
-#include <cassert>
-#include <string>
-#include <vector>
-#include <initializer_list>
-
-#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 It>
-class my_input_iterator
-{
-    It it_;
-
-    template <class U> friend class my_input_iterator;
-public:
-    typedef          my_input_iterator_tag                     iterator_category;
-    typedef typename std::iterator_traits<It>::value_type      value_type;
-    typedef typename std::iterator_traits<It>::
diff erence_type 
diff erence_type;
-    typedef It                                                 pointer;
-    typedef typename std::iterator_traits<It>::reference       reference;
-
-    It base() const {return it_;}
-
-    my_input_iterator() : it_() {}
-    explicit my_input_iterator(It it) : it_(it) {}
-    template <class U>
-        my_input_iterator(const my_input_iterator<U>& 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 <class T>
-    void operator,(T const &) DELETE_FUNCTION;
-};
-
-template <class T, class U>
-inline
-bool
-operator==(const my_input_iterator<T>& x, const my_input_iterator<U>& y)
-{
-    return x.base() == y.base();
-}
-
-template <class T, class U>
-inline
-bool
-operator!=(const my_input_iterator<T>& x, const my_input_iterator<U>& y)
-{
-    return !(x == y);
-}
-
-
-int main(int, char**)
-{
-//  basic tests
-    static_assert(( std::__libcpp_is_trivial_iterator<char *>::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<const char *>::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<int *>::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<T *>::value), "");
-
-    static_assert(( std::__libcpp_is_trivial_iterator<std::move_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::move_iterator<const char *> >::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::move_iterator<int *> >       ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::move_iterator<T *> >         ::value), "");
-
-    static_assert(( std::__libcpp_is_trivial_iterator<std::reverse_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::reverse_iterator<const char *> >::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::reverse_iterator<int *> >       ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::reverse_iterator<T *> >         ::value), "");
-
-    static_assert(( std::__libcpp_is_trivial_iterator<std::__wrap_iter<char *> >      ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::__wrap_iter<const char *> >::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::__wrap_iter<int *> >       ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::__wrap_iter<T *> >         ::value), "");
-
-    static_assert(( std::__libcpp_is_trivial_iterator<std::reverse_iterator<std::__wrap_iter<char *> > > ::value), "");
-
-//  iterators in the libc++ test suite
-    static_assert((!std::__libcpp_is_trivial_iterator<output_iterator       <char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<input_iterator        <char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<forward_iterator      <char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<bidirectional_iterator<char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<random_access_iterator<char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<ThrowingIterator      <char *> >::value), "");
-    static_assert((!std::__libcpp_is_trivial_iterator<NonThrowingIterator   <char *> >::value), "");
-
-
-//  Iterator classification
-    static_assert(( std::__is_cpp17_input_iterator        <char *>::value), "" );
-    static_assert(( std::__is_cpp17_forward_iterator      <char *>::value), "" );
-    static_assert(( std::__is_cpp17_bidirectional_iterator<char *>::value), "" );
-    static_assert(( std::__is_cpp17_random_access_iterator<char *>::value), "" );
-    static_assert(( std::__is_cpp17_contiguous_iterator   <char *>::value), "" );
-    static_assert((!std::__is_exactly_cpp17_input_iterator<char *>::value), "" );
-
-    static_assert(( std::__is_cpp17_input_iterator        <input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_forward_iterator      <input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_bidirectional_iterator<input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_random_access_iterator<input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_contiguous_iterator   <input_iterator<char *> >::value), "" );
-    static_assert(( std::__is_exactly_cpp17_input_iterator<input_iterator<char *> >::value), "" );
-
-    static_assert(( std::__is_cpp17_input_iterator        <forward_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_forward_iterator      <forward_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_bidirectional_iterator<forward_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_random_access_iterator<forward_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_contiguous_iterator   <forward_iterator<char *> >::value), "" );
-    static_assert((!std::__is_exactly_cpp17_input_iterator<forward_iterator<char *> >::value), "" );
-
-    static_assert(( std::__is_cpp17_input_iterator        <bidirectional_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_forward_iterator      <bidirectional_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_bidirectional_iterator<bidirectional_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_random_access_iterator<bidirectional_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_contiguous_iterator   <bidirectional_iterator<char *> >::value), "" );
-    static_assert((!std::__is_exactly_cpp17_input_iterator<bidirectional_iterator<char *> >::value), "" );
-
-    static_assert(( std::__is_cpp17_input_iterator        <random_access_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_forward_iterator      <random_access_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_bidirectional_iterator<random_access_iterator<char *> >::value), "" );
-    static_assert(( std::__is_cpp17_random_access_iterator<random_access_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_contiguous_iterator   <random_access_iterator<char *> >::value), "" );
-    static_assert((!std::__is_exactly_cpp17_input_iterator<random_access_iterator<char *> >::value), "" );
-
-    static_assert(( std::__is_cpp17_input_iterator        <my_input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_forward_iterator      <my_input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_bidirectional_iterator<my_input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_random_access_iterator<my_input_iterator<char *> >::value), "" );
-    static_assert((!std::__is_cpp17_contiguous_iterator   <my_input_iterator<char *> >::value), "" );
-    static_assert(( std::__is_exactly_cpp17_input_iterator<my_input_iterator<char *> >::value), "" );
-
-//
-//  iterators from libc++'s containers
-//
-
-//  vector
-    static_assert(( std::__libcpp_is_trivial_iterator<std::vector<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::vector<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::vector<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::vector<char>::const_reverse_iterator>::value), "");
-
-//  string
-    static_assert(( std::__libcpp_is_trivial_iterator<std::basic_string<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::basic_string<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::basic_string<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::basic_string<char>::const_reverse_iterator>::value), "");
-
-#if TEST_STD_VER >= 11
-//  Initializer list  (which has no reverse iterators)
-    static_assert(( std::__libcpp_is_trivial_iterator<std::initializer_list<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_is_trivial_iterator<std::initializer_list<char>::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 d281cb2065542..0000000000000
--- 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
-//
-//===----------------------------------------------------------------------===//
-//
-// <iterator>
-
-// __libcpp_is_trivial_iterator<Tp>
-
-// __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 <iterator>
-#include <cassert>
-#include <string>
-#include <vector>
-#include <initializer_list>
-
-#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<char *>::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<const char *>::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::move_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::move_iterator<const char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<const char *> >::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::__wrap_iter<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::__wrap_iter<const char *> >::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<std::__wrap_iter<char *> > > ::value), "");
-
-//  iterators in the libc++ test suite
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<output_iterator       <char *> >::value == expected, "");
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<input_iterator        <char *> >::value == expected, "");
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<forward_iterator      <char *> >::value == expected, "");
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<bidirectional_iterator<char *> >::value == expected, "");
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<random_access_iterator<char *> >::value == expected, "");
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<ThrowingIterator      <char *> >::value == expected, "");
-
-#if TEST_STD_VER >= 11
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<NonThrowingIterator   <char *> >::value), "");
-#else
-    static_assert(std::__libcpp_string_gets_noexcept_iterator<NonThrowingIterator   <char *> >::value == expected, "");
-#endif
-
-//
-//  iterators from libc++'s containers
-//
-
-//  string
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::const_reverse_iterator>::value), "");
-
-//  vector
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::const_reverse_iterator>::value), "");
-
-#if TEST_STD_VER >= 11
-//  Initializer list  (which has no reverse iterators)
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::initializer_list<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::initializer_list<char>::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 78f8429598c0c..0000000000000
--- 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
-//
-//===----------------------------------------------------------------------===//
-//
-
-// <iterator>
-
-// __libcpp_is_trivial_iterator<Tp>
-
-// __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 <iterator>
-#include <cassert>
-#include <string>
-#include <vector>
-#include <initializer_list>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-int main(int, char**)
-{
-//  basic tests
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<char *>::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<const char *>::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::move_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::move_iterator<const char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<const char *> >::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::__wrap_iter<char *> >      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::__wrap_iter<const char *> >::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::reverse_iterator<std::__wrap_iter<char *> > > ::value), "");
-
-//  iterators in the libc++ test suite
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<output_iterator       <char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<input_iterator        <char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<forward_iterator      <char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<bidirectional_iterator<char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<random_access_iterator<char *> >::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<ThrowingIterator      <char *> >::value), "");
-
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<NonThrowingIterator   <char *> >::value), "");
-
-//
-//  iterators from libc++'s containers
-//
-
-//  string
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::vector<char>::const_reverse_iterator>::value), "");
-
-//  vector
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::const_iterator>        ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::reverse_iterator>      ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::basic_string<char>::const_reverse_iterator>::value), "");
-
-#if TEST_STD_VER >= 11
-//  Initializer list  (which has no reverse iterators)
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::initializer_list<char>::iterator>              ::value), "");
-    static_assert(( std::__libcpp_string_gets_noexcept_iterator<std::initializer_list<char>::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 0000000000000..120f5333a43ef
--- /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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+#include <cassert>
+#include <string>
+
+struct Incomplete;
+template<class T> struct Holder { T t; };
+
+template<class T>
+struct Charlike {
+    char ch_;
+    Charlike(char ch) : ch_(ch) {}
+    operator char() const { return ch_; }
+};
+
+int main(int, char**)
+{
+    std::string s;
+    Charlike<Holder<Incomplete> > 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 d6112601f2e09..8c0d3d78ab8fa 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 <class S, class It>
 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<const unsigned char*>(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<const char*> It;
     typedef std::move_iterator<It> 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 c03b5efdfd577..a4bad33c13d8b 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 <class S, class It>
 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 67ba33debf7d0..471e3019ffced 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::
diff erence_type pos, It first, It last, S expected)
 }
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
+struct Widget { operator char() const { throw 42; } };
+
 template <class S, class It>
 void
 test_exceptions(S s, typename S::
diff erence_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<const unsigned char*>(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<const char*> It;
     typedef std::move_iterator<It> 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 1d4a46b7c886f..3e6907a17dd98 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 <class S, class It>
 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
 


        


More information about the libcxx-commits mailing list