summaryrefslogtreecommitdiff
path: root/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
diff options
context:
space:
mode:
authorArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-04-16 17:49:57 -0400
committerArthur O'Dwyer <arthur.j.odwyer@gmail.com>2021-04-26 16:22:43 -0400
commite87479b00fcc852a54b79d2fd7f8d779e2b75f68 (patch)
treed785caabd8cf2a4b1e43e8d0a061483f2e7d9ae0 /libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
parent70d94c3f2cae71ade2ceacdceb3d2e9899d2289a (diff)
[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
Diffstat (limited to 'libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp')
-rw-r--r--libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp22
1 files changed, 22 insertions, 0 deletions
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 d6112601f2e0..8c0d3d78ab8f 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;