From 1c0cedccb62480d61bc4c0c31c871f9096b4873a Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Tue, 17 Oct 2017 13:03:17 +0000 Subject: [libc++] Fix PR34898 - vector iterator constructors and assign method perform push_back instead of emplace_back. Summary: The constructors `vector(Iter, Iter, Alloc = Alloc{})` and `assign(Iter, Iter)` don't correctly perform EmplaceConstruction from the result of dereferencing the iterator. This results in them performing an additional and unneeded copy. This patch addresses the issue by correctly using `emplace_back` in C++11 and newer. There are also some bugs in our `insert` implementation, but those will be handled separately. @mclow.lists We should probably merge this into 5.1, agreed? Reviewers: mclow.lists, dlj, EricWF Reviewed By: mclow.lists, EricWF Subscribers: cfe-commits, mclow.lists Differential Revision: https://reviews.llvm.org/D38757 llvm-svn: 315994 --- .../vector/vector.cons/assign_iter_iter.pass.cpp | 76 ++++++++++ .../vector.cons/construct_iter_iter.pass.cpp | 157 ++++++++++++++++---- .../vector.cons/construct_iter_iter_alloc.pass.cpp | 160 ++++++++++++++++----- 3 files changed, 331 insertions(+), 62 deletions(-) create mode 100644 libcxx/test/std/containers/sequences/vector/vector.cons/assign_iter_iter.pass.cpp (limited to 'libcxx/test/std/containers/sequences/vector') diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/assign_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_iter_iter.pass.cpp new file mode 100644 index 00000000000..f07495637aa --- /dev/null +++ b/libcxx/test/std/containers/sequences/vector/vector.cons/assign_iter_iter.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// + +// void assign(size_type n, const_reference v); + +#include +#include +#include +#include +#include "test_macros.h" +#include "min_allocator.h" +#include "asan_testing.h" +#include "test_iterators.h" +#if TEST_STD_VER >= 11 +#include "emplace_constructible.h" +#include "container_test_types.h" +#endif + + +void test_emplaceable_concept() { +#if TEST_STD_VER >= 11 + int arr1[] = {42}; + int arr2[] = {1, 101, 42}; + { + using T = EmplaceConstructibleMoveableAndAssignable; + using It = forward_iterator; + { + std::vector v; + v.assign(It(arr1), It(std::end(arr1))); + assert(v[0].value == 42); + } + { + std::vector v; + v.assign(It(arr2), It(std::end(arr2))); + assert(v[0].value == 1); + assert(v[1].value == 101); + assert(v[2].value == 42); + } + } + { + using T = EmplaceConstructibleMoveableAndAssignable; + using It = input_iterator; + { + std::vector v; + v.assign(It(arr1), It(std::end(arr1))); + assert(v[0].copied == 0); + assert(v[0].value == 42); + } + { + std::vector v; + v.assign(It(arr2), It(std::end(arr2))); + //assert(v[0].copied == 0); + assert(v[0].value == 1); + //assert(v[1].copied == 0); + assert(v[1].value == 101); + assert(v[2].copied == 0); + assert(v[2].value == 42); + } + } +#endif +} + + + +int main() +{ + test_emplaceable_concept(); +} diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp index ec4944d1ad1..88613efe7a2 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter.pass.cpp @@ -20,40 +20,137 @@ #include "test_allocator.h" #include "min_allocator.h" #include "asan_testing.h" +#if TEST_STD_VER >= 11 +#include "emplace_constructible.h" +#include "container_test_types.h" +#endif template -void -test(Iterator first, Iterator last) -{ - C c(first, last); - LIBCPP_ASSERT(c.__invariants()); - assert(c.size() == static_cast(std::distance(first, last))); - LIBCPP_ASSERT(is_contiguous_container_asan_correct(c)); - for (typename C::const_iterator i = c.cbegin(), e = c.cend(); i != e; ++i, ++first) - assert(*i == *first); +void test(Iterator first, Iterator last) { + C c(first, last); + LIBCPP_ASSERT(c.__invariants()); + assert(c.size() == static_cast(std::distance(first, last))); + LIBCPP_ASSERT(is_contiguous_container_asan_correct(c)); + for (typename C::const_iterator i = c.cbegin(), e = c.cend(); i != e; + ++i, ++first) + assert(*i == *first); +} + +static void basic_test_cases() { + int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0}; + int* an = a + sizeof(a) / sizeof(a[0]); + test >(input_iterator(a), + input_iterator(an)); + test >(forward_iterator(a), + forward_iterator(an)); + test >(bidirectional_iterator(a), + bidirectional_iterator(an)); + test >(random_access_iterator(a), + random_access_iterator(an)); + test >(a, an); + + test > >( + input_iterator(a), input_iterator(an)); + // Add 1 for implementations that dynamically allocate a container proxy. + test > >( + forward_iterator(a), forward_iterator(an)); + test > >( + bidirectional_iterator(a), + bidirectional_iterator(an)); + test > >( + random_access_iterator(a), + random_access_iterator(an)); + test > >(a, an); +#if TEST_STD_VER >= 11 + test > >(input_iterator(a), + input_iterator(an)); + test > >( + forward_iterator(a), forward_iterator(an)); + test > >( + bidirectional_iterator(a), + bidirectional_iterator(an)); + test > >( + random_access_iterator(a), + random_access_iterator(an)); + test >(a, an); +#endif } -int main() -{ - int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0}; - int* an = a + sizeof(a)/sizeof(a[0]); - test >(input_iterator(a), input_iterator(an)); - test >(forward_iterator(a), forward_iterator(an)); - test >(bidirectional_iterator(a), bidirectional_iterator(an)); - test >(random_access_iterator(a), random_access_iterator(an)); - test >(a, an); - - test > >(input_iterator(a), input_iterator(an)); - // Add 1 for implementations that dynamically allocate a container proxy. - test > >(forward_iterator(a), forward_iterator(an)); - test > >(bidirectional_iterator(a), bidirectional_iterator(an)); - test > >(random_access_iterator(a), random_access_iterator(an)); - test > >(a, an); +void emplaceable_concept_tests() { #if TEST_STD_VER >= 11 - test> >(input_iterator(a), input_iterator(an)); - test> >(forward_iterator(a), forward_iterator(an)); - test> >(bidirectional_iterator(a), bidirectional_iterator(an)); - test> >(random_access_iterator(a), random_access_iterator(an)); - test >(a, an); + int arr1[] = {42}; + int arr2[] = {1, 101, 42}; + { + using T = EmplaceConstructible; + using It = forward_iterator; + { + std::vector v(It(arr1), It(std::end(arr1))); + assert(v[0].value == 42); + } + { + std::vector v(It(arr2), It(std::end(arr2))); + assert(v[0].value == 1); + assert(v[1].value == 101); + assert(v[2].value == 42); + } + } + { + using T = EmplaceConstructibleAndMoveInsertable; + using It = input_iterator; + { + std::vector v(It(arr1), It(std::end(arr1))); + assert(v[0].copied == 0); + assert(v[0].value == 42); + } + { + std::vector v(It(arr2), It(std::end(arr2))); + //assert(v[0].copied == 0); + assert(v[0].value == 1); + //assert(v[1].copied == 0); + assert(v[1].value == 101); + assert(v[2].copied == 0); + assert(v[2].value == 42); + } + } #endif } + +void test_ctor_under_alloc() { +#if TEST_STD_VER >= 11 + int arr1[] = {42}; + int arr2[] = {1, 101, 42}; + { + using C = TCT::vector<>; + using T = typename C::value_type; + using It = forward_iterator; + { + ExpectConstructGuard G(1); + C v(It(arr1), It(std::end(arr1))); + } + { + ExpectConstructGuard G(3); + C v(It(arr2), It(std::end(arr2))); + } + } + { + using C = TCT::vector<>; + using T = typename C::value_type; + using It = input_iterator; + { + ExpectConstructGuard G(1); + C v(It(arr1), It(std::end(arr1))); + } + { + //ExpectConstructGuard G(3); + //C v(It(arr2), It(std::end(arr2)), a); + } + } +#endif +} + + +int main() { + basic_test_cases(); + emplaceable_concept_tests(); // See PR34898 + test_ctor_under_alloc(); +} diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp index b4482ddb272..71743b31d44 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_iter_iter_alloc.pass.cpp @@ -21,56 +21,152 @@ #include "test_allocator.h" #include "min_allocator.h" #include "asan_testing.h" +#if TEST_STD_VER >= 11 +#include "emplace_constructible.h" +#include "container_test_types.h" +#endif template -void -test(Iterator first, Iterator last, const A& a) -{ - C c(first, last, a); - LIBCPP_ASSERT(c.__invariants()); - assert(c.size() == static_cast(std::distance(first, last))); - LIBCPP_ASSERT(is_contiguous_container_asan_correct(c)); - for (typename C::const_iterator i = c.cbegin(), e = c.cend(); i != e; ++i, ++first) - assert(*i == *first); +void test(Iterator first, Iterator last, const A& a) { + C c(first, last, a); + LIBCPP_ASSERT(c.__invariants()); + assert(c.size() == static_cast(std::distance(first, last))); + LIBCPP_ASSERT(is_contiguous_container_asan_correct(c)); + for (typename C::const_iterator i = c.cbegin(), e = c.cend(); i != e; + ++i, ++first) + assert(*i == *first); } #if TEST_STD_VER >= 11 template -struct implicit_conv_allocator : min_allocator -{ - implicit_conv_allocator(void*) {} - implicit_conv_allocator(const implicit_conv_allocator&) = default; +struct implicit_conv_allocator : min_allocator { + implicit_conv_allocator(void*) {} + implicit_conv_allocator(const implicit_conv_allocator&) = default; - template - implicit_conv_allocator(implicit_conv_allocator) {} + template + implicit_conv_allocator(implicit_conv_allocator) {} }; #endif -int main() -{ - { +void basic_tests() { + { int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0}; - int* an = a + sizeof(a)/sizeof(a[0]); + int* an = a + sizeof(a) / sizeof(a[0]); std::allocator alloc; - test >(input_iterator(a), input_iterator(an), alloc); - test >(forward_iterator(a), forward_iterator(an), alloc); - test >(bidirectional_iterator(a), bidirectional_iterator(an), alloc); - test >(random_access_iterator(a), random_access_iterator(an), alloc); + test >(input_iterator(a), + input_iterator(an), alloc); + test >(forward_iterator(a), + forward_iterator(an), alloc); + test >(bidirectional_iterator(a), + bidirectional_iterator(an), alloc); + test >(random_access_iterator(a), + random_access_iterator(an), alloc); test >(a, an, alloc); - } + } #if TEST_STD_VER >= 11 - { + { int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 1, 0}; - int* an = a + sizeof(a)/sizeof(a[0]); + int* an = a + sizeof(a) / sizeof(a[0]); min_allocator alloc; - test> >(input_iterator(a), input_iterator(an), alloc); - test> >(forward_iterator(a), forward_iterator(an), alloc); - test> >(bidirectional_iterator(a), bidirectional_iterator(an), alloc); - test> >(random_access_iterator(a), random_access_iterator(an), alloc); - test> >(a, an, alloc); - test> >(a, an, nullptr); + test > >( + input_iterator(a), input_iterator(an), alloc); + test > >( + forward_iterator(a), forward_iterator(an), + alloc); + test > >( + bidirectional_iterator(a), + bidirectional_iterator(an), alloc); + test > >( + random_access_iterator(a), + random_access_iterator(an), alloc); + test > >(a, an, alloc); + test > >(a, an, nullptr); + } +#endif +} + +void emplaceable_concept_tests() { +#if TEST_STD_VER >= 11 + int arr1[] = {42}; + int arr2[] = {1, 101, 42}; + { + using T = EmplaceConstructible; + using It = forward_iterator; + using Alloc = std::allocator; + Alloc a; + { + std::vector v(It(arr1), It(std::end(arr1)), a); + assert(v[0].value == 42); + } + { + std::vector v(It(arr2), It(std::end(arr2)), a); + assert(v[0].value == 1); + assert(v[1].value == 101); + assert(v[2].value == 42); + } + } + { + using T = EmplaceConstructibleAndMoveInsertable; + using It = input_iterator; + using Alloc = std::allocator; + Alloc a; + { + std::vector v(It(arr1), It(std::end(arr1)), a); + assert(v[0].copied == 0); + assert(v[0].value == 42); + } + { + std::vector v(It(arr2), It(std::end(arr2)), a); + assert(v[0].value == 1); + assert(v[1].value == 101); + assert(v[2].copied == 0); + assert(v[2].value == 42); } + } #endif } + +void test_ctor_under_alloc() { +#if TEST_STD_VER >= 11 + int arr1[] = {42}; + int arr2[] = {1, 101, 42}; + { + using C = TCT::vector<>; + using T = typename C::value_type; + using It = forward_iterator; + using Alloc = typename C::allocator_type; + Alloc a; + { + ExpectConstructGuard G(1); + C v(It(arr1), It(std::end(arr1)), a); + } + { + ExpectConstructGuard G(3); + C v(It(arr2), It(std::end(arr2)), a); + } + } + { + using C = TCT::vector<>; + using T = typename C::value_type; + using It = input_iterator; + using Alloc = typename C::allocator_type; + Alloc a; + { + ExpectConstructGuard G(1); + C v(It(arr1), It(std::end(arr1)), a); + } + { + //ExpectConstructGuard G(3); + //C v(It(arr2), It(std::end(arr2)), a); + } + } +#endif +} + +int main() { + basic_tests(); + emplaceable_concept_tests(); // See PR34898 + test_ctor_under_alloc(); +} -- cgit v1.2.3