-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[libc++] Improve the test coverage for std::vector::emplace #132440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++] Improve the test coverage for std::vector::emplace #132440
Conversation
@llvm/pr-subscribers-libcxx Author: Louis Dionne (ldionne) ChangesThis patch refactors the test for std::vector::emplace back to cover new corner cases, and increase coverage for normal cases as well. This is in preparation for reworking the implementation of emplace. Full diff: https://github.com/llvm/llvm-project/pull/132440.diff 3 Files Affected:
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h b/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h
index 72cd47a50b2c0..92bea5997337a 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h
@@ -38,7 +38,35 @@ struct Throws {
};
bool Throws::sThrows = false;
-#endif
+
+struct ThrowingMove {
+ TEST_CONSTEXPR ThrowingMove() : value(0), do_throw(false) {}
+ TEST_CONSTEXPR explicit ThrowingMove(int v) : value(v), do_throw(false) {}
+ TEST_CONSTEXPR explicit ThrowingMove(int v, bool do_throw) : value(v), do_throw(do_throw) {}
+
+ ThrowingMove(const ThrowingMove& rhs) = default;
+ ThrowingMove& operator=(const ThrowingMove&) = default;
+
+ TEST_CONSTEXPR_CXX14 ThrowingMove(ThrowingMove&& rhs) : value(rhs.value), do_throw(rhs.do_throw) {
+ if (do_throw)
+ throw 1;
+ }
+ TEST_CONSTEXPR_CXX14 ThrowingMove& operator=(ThrowingMove&& rhs) {
+ value = rhs.value;
+ do_throw = rhs.do_throw;
+ if (do_throw)
+ throw 1;
+ return *this;
+ }
+
+ TEST_CONSTEXPR_CXX14 friend bool operator==(ThrowingMove const& lhs, ThrowingMove const& rhs) {
+ return lhs.value == rhs.value;
+ }
+
+ int value;
+ bool do_throw;
+};
+#endif // TEST_HAS_NO_EXCEPTIONS
struct Tracker {
int copy_assignments = 0;
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
index 667ce483806e7..3431f4336d1e2 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
@@ -7,150 +7,339 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03 && !stdlib=libc++
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=9000000
// <vector>
// template <class... Args> iterator emplace(const_iterator pos, Args&&... args);
-#include <vector>
#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+#include <vector>
-#include "test_macros.h"
-#include "test_allocator.h"
-#include "min_allocator.h"
#include "asan_testing.h"
+#include "common.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
-class A {
- int i_;
- double d_;
+struct NonCopyable {
+ int i;
- A(const A&);
- A& operator=(const A&);
+ TEST_CONSTEXPR_CXX14 explicit NonCopyable(int i) : i(i) {}
-public:
- TEST_CONSTEXPR_CXX14 A(int i, double d) : i_(i), d_(d) {}
+ NonCopyable(NonCopyable const&) = delete;
+ NonCopyable& operator=(NonCopyable const&) = delete;
- TEST_CONSTEXPR_CXX14 A(A&& a) : i_(a.i_), d_(a.d_) {
- a.i_ = 0;
- a.d_ = 0;
- }
+ TEST_CONSTEXPR_CXX14 NonCopyable(NonCopyable&& other) : i(other.i) { other.i = -1; }
- TEST_CONSTEXPR_CXX14 A& operator=(A&& a) {
- i_ = a.i_;
- d_ = a.d_;
- a.i_ = 0;
- a.d_ = 0;
+ TEST_CONSTEXPR_CXX14 NonCopyable& operator=(NonCopyable&& other) {
+ i = other.i;
+ other.i = -1;
return *this;
}
- TEST_CONSTEXPR_CXX14 int geti() const { return i_; }
- TEST_CONSTEXPR_CXX14 double getd() const { return d_; }
+ TEST_CONSTEXPR_CXX14 friend bool operator==(NonCopyable const& lhs, NonCopyable const& rhs) { return lhs.i == rhs.i; }
};
-TEST_CONSTEXPR_CXX20 bool tests() {
+template <class T>
+struct has_moved_from_sentinel : std::false_type {};
+
+template <>
+struct has_moved_from_sentinel<NonCopyable> : std::true_type {};
+
+template <template <class...> class Allocator, class T>
+TEST_CONSTEXPR_CXX20 void test() {
+ using Vector = std::vector<T, Allocator<T> >;
+ using Iterator = typename Vector::iterator;
+
+ // Check the return type
+ {
+ Vector v;
+ ASSERT_SAME_TYPE(decltype(v.emplace(v.cbegin(), 1)), Iterator);
+ }
+
+ // Emplace at the end of a vector with increasing size
+ {
+ Vector v;
+
+ // starts with size 0
+ {
+ Iterator it = v.emplace(v.cend(), 0);
+ assert(it == v.end() - 1);
+ assert(v.size() == 1);
+ assert(v[0] == T(0));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size 1
+ {
+ Iterator it = v.emplace(v.cend(), 1);
+ assert(it == v.end() - 1);
+ assert(v.size() == 2);
+ assert(v[0] == T(0));
+ assert(v[1] == T(1));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size 2
+ {
+ Iterator it = v.emplace(v.cend(), 2);
+ assert(it == v.end() - 1);
+ assert(v.size() == 3);
+ assert(v[0] == T(0));
+ assert(v[1] == T(1));
+ assert(v[2] == T(2));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size n...
+ for (std::size_t n = 3; n != 100; ++n) {
+ Iterator it = v.emplace(v.cend(), n);
+ assert(it == v.end() - 1);
+ assert(v.size() == n + 1);
+ for (std::size_t i = 0; i != n + 1; ++i)
+ assert(v[i] == T(i));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+ }
+
+ // Emplace at the start of a vector with increasing size
+ {
+ Vector v;
+
+ // starts with size 0
+ {
+ Iterator it = v.emplace(v.cbegin(), 0);
+ assert(it == v.begin());
+ assert(v.size() == 1);
+ assert(v[0] == T(0));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size 1
+ {
+ Iterator it = v.emplace(v.cbegin(), 1);
+ assert(it == v.begin());
+ assert(v.size() == 2);
+ assert(v[0] == T(1));
+ assert(v[1] == T(0));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size 2
+ {
+ Iterator it = v.emplace(v.cbegin(), 2);
+ assert(it == v.begin());
+ assert(v.size() == 3);
+ assert(v[0] == T(2));
+ assert(v[1] == T(1));
+ assert(v[2] == T(0));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // starts with size n...
+ for (std::size_t n = 3; n != 100; ++n) {
+ Iterator it = v.emplace(v.cbegin(), n);
+ assert(it == v.begin());
+ assert(v.size() == n + 1);
+ for (std::size_t i = 0; i != n + 1; ++i)
+ assert(v[i] == T(n - i));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+ }
+
+ // Emplace somewhere inside the vector
{
- std::vector<A> c;
- std::vector<A>::iterator i = c.emplace(c.cbegin(), 2, 3.5);
- assert(i == c.begin());
- assert(c.size() == 1);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(is_contiguous_container_asan_correct(c));
- i = c.emplace(c.cend(), 3, 4.5);
- assert(i == c.end() - 1);
- assert(c.size() == 2);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- assert(is_contiguous_container_asan_correct(c));
- i = c.emplace(c.cbegin() + 1, 4, 6.5);
- assert(i == c.begin() + 1);
- assert(c.size() == 3);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c[1].geti() == 4);
- assert(c[1].getd() == 6.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- assert(is_contiguous_container_asan_correct(c));
+ Vector v;
+ v.emplace_back(0);
+ v.emplace_back(1);
+ v.emplace_back(2);
+ // vector is {0, 1, 2}
+
+ {
+ Iterator it = v.emplace(v.cbegin() + 1, 3);
+ // vector is {0, 3, 1, 2}
+ assert(it == v.begin() + 1);
+ assert(v.size() == 4);
+ assert(v[0] == T(0));
+ assert(v[1] == T(3));
+ assert(v[2] == T(1));
+ assert(v[3] == T(2));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ {
+ Iterator it = v.emplace(v.cbegin() + 2, 4);
+ // vector is {0, 3, 4, 1, 2}
+ assert(it == v.begin() + 2);
+ assert(v.size() == 5);
+ assert(v[0] == T(0));
+ assert(v[1] == T(3));
+ assert(v[2] == T(4));
+ assert(v[3] == T(1));
+ assert(v[4] == T(2));
+ assert(is_contiguous_container_asan_correct(v));
+ }
}
+
+ // Emplace after reserving
{
- std::vector<A, limited_allocator<A, 7> > c;
- std::vector<A, limited_allocator<A, 7> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
- assert(i == c.begin());
- assert(c.size() == 1);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(is_contiguous_container_asan_correct(c));
- i = c.emplace(c.cend(), 3, 4.5);
- assert(i == c.end() - 1);
- assert(c.size() == 2);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- assert(is_contiguous_container_asan_correct(c));
- i = c.emplace(c.cbegin() + 1, 4, 6.5);
- assert(i == c.begin() + 1);
- assert(c.size() == 3);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c[1].geti() == 4);
- assert(c[1].getd() == 6.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- assert(is_contiguous_container_asan_correct(c));
+ Vector v;
+ v.emplace_back(0);
+ v.emplace_back(1);
+ v.emplace_back(2);
+ // vector is {0, 1, 2}
+
+ v.reserve(1000);
+ Iterator it = v.emplace(v.cbegin() + 1, 3);
+ assert(it == v.begin() + 1);
+ assert(v.size() == 4);
+ assert(v[0] == T(0));
+ assert(v[1] == T(3));
+ assert(v[2] == T(1));
+ assert(v[3] == T(2));
+ assert(is_contiguous_container_asan_correct(v));
}
+
+ // Emplace with the same type that's stored in the vector (as opposed to just constructor arguments)
{
- std::vector<A, min_allocator<A> > c;
- std::vector<A, min_allocator<A> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
- assert(i == c.begin());
- assert(c.size() == 1);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- i = c.emplace(c.cend(), 3, 4.5);
- assert(i == c.end() - 1);
- assert(c.size() == 2);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- i = c.emplace(c.cbegin() + 1, 4, 6.5);
- assert(i == c.begin() + 1);
- assert(c.size() == 3);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c[1].geti() == 4);
- assert(c[1].getd() == 6.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
+ Vector v;
+ Iterator it = v.emplace(v.cbegin(), T(1));
+ assert(it == v.begin());
+ assert(v.size() == 1);
+ assert(v[0] == T(1));
+ assert(is_contiguous_container_asan_correct(v));
}
+
+ // Emplace from an element inside the vector itself. This is interesting for two reasons. First, if the
+ // vector must increase capacity, the implementation needs to make sure that it doesn't end up inserting
+ // from a dangling reference.
+ //
+ // Second, if the vector doesn't need to grow but its elements get shifted internally, the implementation
+ // must make sure that it doesn't end up inserting from an element whose position has changed.
{
- std::vector<A, safe_allocator<A> > c;
- std::vector<A, safe_allocator<A> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
- assert(i == c.begin());
- assert(c.size() == 1);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- i = c.emplace(c.cend(), 3, 4.5);
- assert(i == c.end() - 1);
- assert(c.size() == 2);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
- i = c.emplace(c.cbegin() + 1, 4, 6.5);
- assert(i == c.begin() + 1);
- assert(c.size() == 3);
- assert(c.front().geti() == 2);
- assert(c.front().getd() == 3.5);
- assert(c[1].geti() == 4);
- assert(c[1].getd() == 6.5);
- assert(c.back().geti() == 3);
- assert(c.back().getd() == 4.5);
+ // When capacity must increase
+ {
+ Vector v;
+ v.emplace_back(0);
+ v.emplace_back(1);
+
+ while (v.size() < v.capacity()) {
+ v.emplace_back(2);
+ }
+ assert(v.size() == v.capacity());
+ // vector is {0, 1, 2...}
+
+ std::size_t old_cap = v.capacity();
+ v.emplace(v.cbegin(), std::move(v[1]));
+ assert(v.capacity() > old_cap); // test the test
+
+ // vector is {1, 0, -1, 2...}
+ // Note that old v[1] has been set to -1 when it was moved-from
+ assert(v.size() >= 3);
+ assert(v[0] == T(1));
+ assert(v[1] == T(0));
+ if (has_moved_from_sentinel<T>::value)
+ assert(v[2] == T(-1));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // When elements shift around
+ {
+ Vector v;
+ v.emplace_back(0);
+ v.emplace_back(1);
+ // vector is {0, 1}
+
+ v.reserve(3);
+ std::size_t old_cap = v.capacity();
+ v.emplace(v.cbegin(), std::move(v[1]));
+ assert(v.capacity() == old_cap); // test the test
+
+ // vector is {1, 0, -1}
+ // Note that old v[1] has been set to -1 when it was moved-from
+ assert(v.size() == 3);
+ assert(v[0] == T(1));
+ assert(v[1] == T(0));
+ if (has_moved_from_sentinel<T>::value)
+ assert(v[2] == T(-1));
+ assert(is_contiguous_container_asan_correct(v));
+ }
}
+ // Make sure that we don't reallocate when we have sufficient capacity
+ {
+ Vector v;
+ v.reserve(8);
+ assert(v.capacity() >= 8);
+
+ std::size_t old_capacity = v.capacity();
+ v.emplace_back(0);
+ v.emplace_back(1);
+ v.emplace_back(2);
+ v.emplace_back(3);
+ assert(v.capacity() == old_capacity);
+
+ v.emplace(v.cend(), 4);
+ assert(v.size() == 5);
+ assert(v.capacity() == old_capacity);
+ assert(v[0] == T(0));
+ assert(v[1] == T(1));
+ assert(v[2] == T(2));
+ assert(v[3] == T(3));
+ assert(v[4] == T(4));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+
+ // Make sure that we correctly handle the case where an exception would be thrown if moving the element into place.
+ // This is a very specific test that aims to validate that the implementation doesn't create a temporary object e.g.
+ // on the stack and then moves it into its final location inside the newly allocated vector storage.
+ //
+ // If that were the case, and if the element happened to throw upon move construction or move assignment into its
+ // final location, we would have invalidated iterators despite the overall emplace being required to have the
+ // strong exception safety guarantee.
+ //
+ // Instead, a conforming implementation needs to emplace the new element into its final location immediately, and
+ // only after this has been done, then start making non-reversible changes to the vector's underlying storage.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ {
+ std::vector<ThrowingMove, Allocator<ThrowingMove> > v;
+ v.emplace_back(0, /* do throw */ false);
+ v.emplace_back(1, /* do throw */ false);
+
+ while (v.size() < v.capacity()) {
+ v.emplace_back(2, /* do throw */ false);
+ }
+ assert(v.size() == v.capacity()); // the next emplace will be forced to invalidate iterators
+
+ v.emplace(v.cend(), 3, /* do throw */ true); // this shouldn't throw since we shouldn't move
+
+ assert(v.size() >= 3);
+ assert(v[0] == ThrowingMove(0));
+ assert(v[1] == ThrowingMove(1));
+ assert(v.back() == ThrowingMove(3));
+ assert(is_contiguous_container_asan_correct(v));
+ }
+#endif // TEST_HAS_NO_EXCEPTIONS
+}
+
+TEST_CONSTEXPR_CXX20 bool tests() {
+ test<std::allocator, int>();
+ test<min_allocator, int>();
+ test<safe_allocator, int>();
+
+ test<std::allocator, NonCopyable>();
+ test<min_allocator, NonCopyable>();
+ test<safe_allocator, NonCopyable>();
+
+ test<std::allocator, NonTriviallyRelocatable>();
+ test<min_allocator, NonTriviallyRelocatable>();
+ test<safe_allocator, NonTriviallyRelocatable>();
+
+ // test<limited_allocator<int, 7> >();
return true;
}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
deleted file mode 100644
index 43990b148cad6..0000000000000
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_extra.pass.cpp
+++ /dev/null
@@ -1,101 +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
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03
-
-// <vector>
-
-// template <class... Args> iterator emplace(const_iterator pos, Args&&... args);
-
-#include <vector>
-#include <cassert>
-
-#include "test_macros.h"
-#include "min_allocator.h"
-#include "asan_testing.h"
-
-TEST_CONSTEXPR_CXX20 bool tests() {
- {
- std::vector<int> v;
- v.reserve(3);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int> v;
- v.reserve(4);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int, min_allocator<int>> v;
- v.reserve(3);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int, min_allocator<int>> v;
- v.reserve(4);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int, safe_allocator<int>> v;
- v.reserve(3);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int, safe_allocator<int>> v;
- v.reserve(4);
- assert(is_contiguous_container_asan_correct(v));
- v = {1, 2, 3};
- v.emplace(v.begin(), v.back());
- assert(v[0] == 3);
- assert(is_contiguous_container_asan_correct(v));
- }
- {
- std::vector<int> v;
- v.reserve(8);
- std::size_t old_capacity = v.capacity();
- assert(old_capacity >= 8);
-
- v.resize(4); // keep the existing capacity
- assert(v.capacity() == old_capacity);
-
- v.emplace(v.cend(), 42);
- assert(v.size() == 5);
- assert(v.capacity() == old_capacity);
- assert(v[4] == 42);
- }
-
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 17
- static_assert(tests());
-#endif
- return 0;
-}
|
libcxx/test/std/containers/sequences/vector/vector.modifiers/common.h
Outdated
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Outdated
Show resolved
Hide resolved
00fb86b
to
7524b2d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mostly LGTM, with some nitpicks.
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace.pass.cpp
Show resolved
Hide resolved
This patch refactors the test for std::vector::emplace back to cover new corner cases, and increase coverage for normal cases as well. This is in preparation for reworking the implementation of emplace.
ab85261
to
71eb8d1
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/11/builds/14528 Here is the relevant piece of the build log for the reference
|
) This patch refactors the test for std::vector::emplace back to cover new corner cases, and increase coverage for normal cases as well. This is building towards llvm#129328.
This patch refactors the test for std::vector::emplace back to cover new corner cases, and increase coverage for normal cases as well.
This is building towards #129328.