[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
This commit is contained in:
@@ -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 <class S, class It>
|
||||
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<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;
|
||||
|
||||
Reference in New Issue
Block a user