[libcxx-commits] [libcxx] 1cac2be - [libc++] Optimize copy construction and assignment of __tree (#151304)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 5 00:49:43 PDT 2025


Author: Nikolas Klauser
Date: 2025-08-05T09:49:40+02:00
New Revision: 1cac2be874c5f1b1b70bdaea8891f5e5b76a84e2

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

LOG: [libc++] Optimize copy construction and assignment of __tree (#151304)

```
----------------------------------------------------------------------------------------------------------
Benchmark                                                                              old             new
----------------------------------------------------------------------------------------------------------
std::map<int, int>::ctor(const&)/0                                                 15.5 ns         14.9 ns
std::map<int, int>::ctor(const&)/32                                                 474 ns          321 ns
std::map<int, int>::ctor(const&)/1024                                             24591 ns        11101 ns
std::map<int, int>::ctor(const&)/8192                                            236153 ns        98868 ns
std::map<std::string, int>::ctor(const&)/0                                         15.2 ns         14.9 ns
std::map<std::string, int>::ctor(const&)/32                                        2673 ns         2340 ns
std::map<std::string, int>::ctor(const&)/1024                                    115354 ns        86088 ns
std::map<std::string, int>::ctor(const&)/8192                                   1298510 ns       626876 ns
std::map<int, int>::operator=(const&) (into cleared Container)/0                   16.5 ns         16.1 ns
std::map<int, int>::operator=(const&) (into cleared Container)/32                   548 ns          323 ns
std::map<int, int>::operator=(const&) (into cleared Container)/1024               28418 ns        11026 ns
std::map<int, int>::operator=(const&) (into cleared Container)/8192              281827 ns        97113 ns
std::map<int, int>::operator=(const&) (into populated Container)/0                 2.42 ns         1.85 ns
std::map<int, int>::operator=(const&) (into populated Container)/32                 369 ns         73.0 ns
std::map<int, int>::operator=(const&) (into populated Container)/1024             24078 ns         2322 ns
std::map<int, int>::operator=(const&) (into populated Container)/8192            266537 ns        22963 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/0           16.6 ns         16.2 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/32          2614 ns         1622 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/1024      116826 ns        63281 ns
std::map<std::string, int>::operator=(const&) (into cleared Container)/8192     1316655 ns       649177 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/0         2.42 ns         1.89 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/32        1264 ns          581 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/1024    238826 ns        39943 ns
std::map<std::string, int>::operator=(const&) (into populated Container)/8192   2412327 ns       379456 ns
```

Fixes #77658
Fixes #62571

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/include/__tree
    libcxx/include/map
    libcxx/include/set
    libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
    libcxx/test/benchmarks/containers/associative/map.bench.cpp
    libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
    libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
    libcxx/test/benchmarks/containers/associative/set.bench.cpp
    libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
    libcxx/test/std/containers/associative/map/map.cons/copy.pass.cpp
    libcxx/test/std/containers/associative/map/map.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/associative/map/map.cons/copy_assign.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/copy.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
    libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 15bf46d44b07f..8b8dce5083149 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -43,6 +43,9 @@ Implemented Papers
 Improvements and New Features
 -----------------------------
 
+- The performance of ``map::map(const map&)`` has been improved up to 2.3x
+- The performance of ``map::operator=(const map&)`` has been improved by up to 11x
+
 Deprecations and Removals
 -------------------------
 

diff  --git a/libcxx/include/__tree b/libcxx/include/__tree
index f8bb4f01b1e29..1b1bb538029da 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1213,6 +1213,104 @@ private:
     __node_pointer __cache_root_;
     __node_pointer __cache_elem_;
   };
+
+  class __tree_deleter {
+    __node_allocator& __alloc_;
+
+  public:
+    using pointer = __node_pointer;
+
+    _LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
+
+#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
+    _LIBCPP_HIDE_FROM_ABI
+#endif
+    void
+    operator()(__node_pointer __ptr) {
+      if (!__ptr)
+        return;
+
+      (*this)(static_cast<__node_pointer>(__ptr->__left_));
+
+      auto __right = __ptr->__right_;
+
+      __node_traits::destroy(__alloc_, std::addressof(__ptr->__value_));
+      __node_traits::deallocate(__alloc_, __ptr, 1);
+
+      (*this)(static_cast<__node_pointer>(__right));
+    }
+  };
+
+  // This copy construction will always produce a correct red-black-tree assuming the incoming tree is correct, since we
+  // copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
+  // didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
+  // balanced tree.
+#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
+  _LIBCPP_HIDE_FROM_ABI
+#endif
+  __node_pointer
+  __copy_construct_tree(__node_pointer __src) {
+    if (!__src)
+      return nullptr;
+
+    __node_holder __new_node = __construct_node(__src->__value_);
+
+    unique_ptr<__node, __tree_deleter> __left(
+        __copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
+    __node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
+
+    __node_pointer __new_node_ptr = __new_node.release();
+
+    __new_node_ptr->__is_black_ = __src->__is_black_;
+    __new_node_ptr->__left_     = static_cast<__node_base_pointer>(__left.release());
+    __new_node_ptr->__right_    = static_cast<__node_base_pointer>(__right);
+    if (__new_node_ptr->__left_)
+      __new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+    if (__new_node_ptr->__right_)
+      __new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
+    return __new_node_ptr;
+  }
+
+  // This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
+  // own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
+  // temporarily not met until all of the incoming red-black tree is copied.
+#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
+  _LIBCPP_HIDE_FROM_ABI
+#endif
+  __node_pointer
+  __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
+    if (!__src) {
+      destroy(__dest);
+      return nullptr;
+    }
+
+    __assign_value(__dest->__value_, __src->__value_);
+    __dest->__is_black_ = __src->__is_black_;
+
+    // If we already have a left node in the destination tree, reuse it and copy-assign recursively
+    if (__dest->__left_) {
+      __dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
+          static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
+
+      // Otherwise, we must create new nodes; copy-construct from here on
+    } else if (__src->__left_) {
+      auto __new_left       = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
+      __dest->__left_       = static_cast<__node_base_pointer>(__new_left);
+      __new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
+    }
+
+    // Identical to the left case above, just for the right nodes
+    if (__dest->__right_) {
+      __dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
+          static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
+    } else if (__src->__right_) {
+      auto __new_right       = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
+      __dest->__right_       = static_cast<__node_base_pointer>(__new_right);
+      __new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
+    }
+
+    return __dest;
+  }
 };
 
 template <class _Tp, class _Compare, class _Allocator>
@@ -1277,11 +1375,22 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_poin
 
 template <class _Tp, class _Compare, class _Allocator>
 __tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
-  if (this != std::addressof(__t)) {
-    value_comp() = __t.value_comp();
-    __copy_assign_alloc(__t);
-    __assign_multi(__t.begin(), __t.end());
+  if (this == std::addressof(__t))
+    return *this;
+
+  value_comp() = __t.value_comp();
+  __copy_assign_alloc(__t);
+
+  if (__size_ != 0) {
+    *__root_ptr() = static_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
+  } else {
+    *__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+    if (__root())
+      __root()->__parent_ = __end_node();
   }
+  __begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
+  __size_       = __t.size();
+
   return *this;
 }
 
@@ -1327,11 +1436,17 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
 
 template <class _Tp, class _Compare, class _Allocator>
 __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
-    : __begin_node_(),
+    : __begin_node_(__end_node()),
       __node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
       __size_(0),
       __value_comp_(__t.value_comp()) {
-  __begin_node_ = __end_node();
+  if (__t.size() == 0)
+    return;
+
+  *__root_ptr()       = static_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
+  __root()->__parent_ = __end_node();
+  __begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
+  __size_       = __t.size();
 }
 
 template <class _Tp, class _Compare, class _Allocator>
@@ -1430,13 +1545,7 @@ __tree<_Tp, _Compare, _Allocator>::~__tree() {
 
 template <class _Tp, class _Compare, class _Allocator>
 void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT {
-  if (__nd != nullptr) {
-    destroy(static_cast<__node_pointer>(__nd->__left_));
-    destroy(static_cast<__node_pointer>(__nd->__right_));
-    __node_allocator& __na = __node_alloc();
-    __node_traits::destroy(__na, std::addressof(__nd->__value_));
-    __node_traits::deallocate(__na, __nd, 1);
-  }
+  (__tree_deleter(__node_alloc_))(__nd);
 }
 
 template <class _Tp, class _Compare, class _Allocator>

diff  --git a/libcxx/include/map b/libcxx/include/map
index 2251565801470..0a43bd09a0b16 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -970,7 +970,7 @@ public:
       : map(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI map(const map& __m) : __tree_(__m.__tree_) { insert(__m.begin(), __m.end()); }
+  _LIBCPP_HIDE_FROM_ABI map(const map& __m) = default;
 
   _LIBCPP_HIDE_FROM_ABI map& operator=(const map& __m) = default;
 
@@ -1637,11 +1637,7 @@ public:
       : multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m)
-      : __tree_(__m.__tree_.value_comp(),
-                __alloc_traits::select_on_container_copy_construction(__m.__tree_.__alloc())) {
-    insert(__m.begin(), __m.end());
-  }
+  _LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m) = default;
 
   _LIBCPP_HIDE_FROM_ABI multimap& operator=(const multimap& __m) = default;
 

diff  --git a/libcxx/include/set b/libcxx/include/set
index 1f2fd7fc91bbb..342a5294c814f 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -660,7 +660,7 @@ public:
       : set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI set(const set& __s) : __tree_(__s.__tree_) { insert(__s.begin(), __s.end()); }
+  _LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
 
   _LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
 
@@ -1119,11 +1119,7 @@ public:
       : multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s)
-      : __tree_(__s.__tree_.value_comp(),
-                __alloc_traits::select_on_container_copy_construction(__s.__tree_.__alloc())) {
-    insert(__s.begin(), __s.end());
-  }
+  _LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
 
   _LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
 

diff  --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 0ff7f15164d8a..535a52f0a08ab 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -151,7 +151,7 @@ void associative_container_benchmarks(std::string container) {
   /////////////////////////
   // Assignment
   /////////////////////////
-  bench("operator=(const&)", [=](auto& st) {
+  bench("operator=(const&) (into cleared Container)", [=](auto& st) {
     const std::size_t size = st.range(0);
     std::vector<Value> in  = make_value_types(generate_unique_keys(size));
     Container src(in.begin(), in.end());
@@ -172,6 +172,42 @@ void associative_container_benchmarks(std::string container) {
     }
   });
 
+  bench("operator=(const&) (into partially populated Container)", [=](auto& st) {
+    const std::size_t size = st.range(0);
+    std::vector<Value> in  = make_value_types(generate_unique_keys(size));
+    Container src(in.begin(), in.end());
+    Container c[BatchSize];
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        c[i] = src;
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        c[i].clear();
+      }
+      st.ResumeTiming();
+    }
+  });
+
+  bench("operator=(const&) (into populated Container)", [=](auto& st) {
+    const std::size_t size = st.range(0);
+    std::vector<Value> in  = make_value_types(generate_unique_keys(size));
+    Container src(in.begin(), in.end());
+    Container c[BatchSize];
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        c[i] = src;
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+    }
+  });
+
   /////////////////////////
   // Insertion
   /////////////////////////

diff  --git a/libcxx/test/benchmarks/containers/associative/map.bench.cpp b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
index cee669ae0a667..bd664dbb56ee7 100644
--- a/libcxx/test/benchmarks/containers/associative/map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
@@ -29,6 +29,7 @@ struct support::adapt_operations<std::map<K, V>> {
 
 int main(int argc, char** argv) {
   support::associative_container_benchmarks<std::map<int, int>>("std::map<int, int>");
+  support::associative_container_benchmarks<std::map<std::string, int>>("std::map<std::string, int>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();

diff  --git a/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
index 6ae93f06aa363..15a0b573081bb 100644
--- a/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
@@ -28,6 +28,7 @@ struct support::adapt_operations<std::multimap<K, V>> {
 
 int main(int argc, char** argv) {
   support::associative_container_benchmarks<std::multimap<int, int>>("std::multimap<int, int>");
+  support::associative_container_benchmarks<std::multimap<std::string, int>>("std::multimap<std::string, int>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();

diff  --git a/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp b/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
index 894f159a52e4a..c205e0a4f793f 100644
--- a/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
@@ -26,6 +26,7 @@ struct support::adapt_operations<std::multiset<K>> {
 
 int main(int argc, char** argv) {
   support::associative_container_benchmarks<std::multiset<int>>("std::multiset<int>");
+  support::associative_container_benchmarks<std::multiset<std::string>>("std::multiset<std::string>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();

diff  --git a/libcxx/test/benchmarks/containers/associative/set.bench.cpp b/libcxx/test/benchmarks/containers/associative/set.bench.cpp
index 6b7b142c792ba..50ee142b6e8b3 100644
--- a/libcxx/test/benchmarks/containers/associative/set.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/set.bench.cpp
@@ -27,6 +27,7 @@ struct support::adapt_operations<std::set<K>> {
 
 int main(int argc, char** argv) {
   support::associative_container_benchmarks<std::set<int>>("std::set<int>");
+  support::associative_container_benchmarks<std::set<std::string>>("std::set<std::string>");
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();

diff  --git a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
index f125cc9adc491..f5a878582666b 100644
--- a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
+++ b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
@@ -324,6 +324,11 @@ void deque_test() {
 
 void map_test() {
   std::map<int, int> i_am_empty{};
+
+  // Make __tree_itertor available in the debug info
+  // FIXME: Is there any way to avoid this requirement?
+  (void)i_am_empty.begin();
+
   ComparePrettyPrintToChars(i_am_empty, "std::map is empty");
 
   std::map<int, std::string> one_two_three;

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/copy.pass.cpp
index e7a69aff0b036..f1696b003c131 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/copy.pass.cpp
@@ -12,116 +12,129 @@
 
 // map(const map& m);
 
-#include <map>
 #include <cassert>
+#include <map>
 
-#include "test_macros.h"
+#include "min_allocator.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
-#include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::map<int, double, C, A> m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+template <template <class> class Alloc>
+void test_alloc() {
+  { // Simple check
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(3, 6));
+    assert(std::next(copy.begin(), 3) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(3, 6));
+    assert(std::next(orig.begin(), 3) == orig.end());
+  }
+
+  { // copy empty map
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc<V> >;
+
+    const Map orig;
+    Map copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6), V(4, 7), V(5, 0)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(3, 6));
+    assert(*std::next(copy.begin(), 3) == V(4, 7));
+    assert(*std::next(copy.begin(), 4) == V(5, 0));
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(3, 6));
+    assert(*std::next(orig.begin(), 3) == V(4, 7));
+    assert(*std::next(orig.begin(), 4) == V(5, 0));
+    assert(std::next(orig.begin(), 5) == orig.end());
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::map<int, double, C, A> m = mo;
-    assert(m.get_allocator() == A(-2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+}
+
+void test() {
+  test_alloc<std::allocator>();
+  test_alloc<min_allocator>(); // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::map<int, double, C, A> m = mo;
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+
+  { // Ensure that the allocator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, test_allocator<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr), std::less<int>(), test_allocator<V>(10));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == test_allocator<V>(10));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == test_allocator<V>(10));
+    assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
   }
-#endif
+
+  { // Ensure that soccc is handled properly
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, other_allocator<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr), std::less<int>(), other_allocator<V>(10));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == other_allocator<V>(-2));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == other_allocator<V>(10));
+  }
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/copy_alloc.pass.cpp
index aaf1be867b748..caa3490d44faf 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/copy_alloc.pass.cpp
@@ -12,116 +12,100 @@
 
 // map(const map& m, const allocator_type& a);
 
-#include <map>
 #include <cassert>
+#include <map>
 
 #include "test_macros.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::map<int, double, C, A> m(mo, A(3));
-    assert(m.get_allocator() == A(3));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+template <class Alloc>
+void test_alloc(const Alloc& new_alloc) {
+  { // Simple check
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc>;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr));
+    Map copy(orig, new_alloc);
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(3, 6));
+    assert(std::next(copy.begin(), 3) == copy.end());
+    assert(copy.get_allocator() == new_alloc);
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(3, 6));
+    assert(std::next(orig.begin(), 3) == orig.end());
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::map<int, double, C, A> m(mo, A());
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+
+  { // copy empty map
+    using Map = std::map<int, int, std::less<int>, Alloc>;
+
+    const Map orig;
+    Map copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc>;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6), V(4, 7), V(5, 0)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(3, 6));
+    assert(*std::next(copy.begin(), 3) == V(4, 7));
+    assert(*std::next(copy.begin(), 4) == V(5, 0));
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(3, 6));
+    assert(*std::next(orig.begin(), 3) == V(4, 7));
+    assert(*std::next(orig.begin(), 4) == V(5, 0));
+    assert(std::next(orig.begin(), 5) == orig.end());
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef explicit_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A{});
-    std::map<int, double, C, A> m(mo, A{});
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+}
+
+void test() {
+  test_alloc(std::allocator<std::pair<const int, int> >());
+  test_alloc(test_allocator<std::pair<const int, int> >(25)); // Make sure that the new allocator is actually used
+  test_alloc(min_allocator<std::pair<const int, int> >());    // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy(orig, std::allocator<V>());
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
-#endif
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/map/map.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.pass.cpp
index 07c8bab2f201d..e01a10b679ac6 100644
--- a/libcxx/test/std/containers/associative/map/map.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.cons/copy_assign.pass.cpp
@@ -12,11 +12,11 @@
 
 // map& operator=(const map& m);
 
-#include <map>
 #include <algorithm>
 #include <cassert>
 #include <cstdio>
 #include <iterator>
+#include <map>
 #include <vector>
 
 #include "test_macros.h"
@@ -24,274 +24,286 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-#if TEST_STD_VER >= 11
-std::vector<int> ca_allocs;
-std::vector<int> ca_deallocs;
-
 template <class T>
-class counting_allocatorT {
-public:
-  typedef T value_type;
-  int foo{0};
-  counting_allocatorT(int f) noexcept : foo(f) {}
+class tracking_allocator {
+  std::vector<void*>* allocs_;
 
-  using propagate_on_container_copy_assignment = std::true_type;
-  template <class U>
-  counting_allocatorT(const counting_allocatorT<U>& other) noexcept {
-    foo = other.foo;
-  }
-  template <class U>
-  bool operator==(const counting_allocatorT<U>& other) const noexcept {
-    return foo == other.foo;
-  }
   template <class U>
-  bool operator!=(const counting_allocatorT<U>& other) const noexcept {
-    return foo != other.foo;
-  }
+  friend class tracking_allocator;
 
-  T* allocate(std::size_t n) const {
-    ca_allocs.push_back(foo);
-    void* const pv = ::malloc(n * sizeof(T));
-    return static_cast<T*>(pv);
-  }
-  void deallocate(T* p, std::size_t) const noexcept {
-    ca_deallocs.push_back(foo);
-    free(p);
-  }
-};
-
-template <class T>
-class counting_allocatorF {
 public:
-  typedef T value_type;
-  int foo{0};
-  counting_allocatorF(int f) noexcept : foo(f) {}
+  using value_type                             = T;
+  using propagate_on_container_copy_assignment = std::true_type;
+
+  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
 
-  using propagate_on_container_copy_assignment = std::false_type;
-  template <class U>
-  counting_allocatorF(const counting_allocatorF<U>& other) noexcept {
-    foo = other.foo;
-  }
   template <class U>
-  bool operator==(const counting_allocatorF<U>& other) const noexcept {
-    return foo == other.foo;
+  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+
+  T* allocate(std::size_t n) {
+    T* allocation = std::allocator<T>().allocate(n);
+    allocs_->push_back(allocation);
+    return allocation;
   }
-  template <class U>
-  bool operator!=(const counting_allocatorF<U>& other) const noexcept {
-    return foo != other.foo;
+
+  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+    auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
+    assert(res != allocs_->end() && "Trying to deallocate memory from 
diff erent allocator?");
+    allocs_->erase(res);
+    std::allocator<T>().deallocate(ptr, n);
   }
 
-  T* allocate(std::size_t n) const {
-    ca_allocs.push_back(foo);
-    void* const pv = ::malloc(n * sizeof(T));
-    return static_cast<T*>(pv);
+  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ == rhs.allocs_;
   }
-  void deallocate(T* p, std::size_t) const noexcept {
-    ca_deallocs.push_back(foo);
-    free(p);
+
+  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ != rhs.allocs_;
   }
 };
 
-bool balanced_allocs() {
-  std::vector<int> temp1, temp2;
-
-  std::printf("Allocations = %zu, deallocations = %zu\n", ca_allocs.size(), ca_deallocs.size());
-  if (ca_allocs.size() != ca_deallocs.size())
-    return false;
-
-  temp1 = ca_allocs;
-  std::sort(temp1.begin(), temp1.end());
-  temp2.clear();
-  std::unique_copy(temp1.begin(), temp1.end(), std::back_inserter<std::vector<int>>(temp2));
-  std::printf("There were %zu 
diff erent allocators\n", temp2.size());
-
-  for (std::vector<int>::const_iterator it = temp2.begin(); it != temp2.end(); ++it) {
-    std::ptr
diff _t const allocs   = std::count(ca_allocs.begin(), ca_allocs.end(), *it);
-    std::ptr
diff _t const deallocs = std::count(ca_deallocs.begin(), ca_deallocs.end(), *it);
-    std::printf("%d: %td vs %td\n", *it, allocs, deallocs);
-    if (allocs != deallocs)
-      return false;
-  }
+struct NoOp {
+  void operator()() {}
+};
+
+template <class Alloc, class AllocatorInvariant = NoOp>
+void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+                const Alloc& rhs_alloc                   = Alloc(),
+                AllocatorInvariant check_alloc_invariant = NoOp()) {
+  {   // Test empty/non-empy map combinations
+    { // assign from a non-empty container into an empty one
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
 
-  temp1 = ca_allocs;
-  std::sort(temp1.begin(), temp1.end());
-  temp2.clear();
-  std::unique_copy(temp1.begin(), temp1.end(), std::back_inserter<std::vector<int>>(temp2));
-  std::printf("There were %zu 
diff erent (de)allocators\n", temp2.size());
-
-  for (std::vector<int>::const_iterator it = ca_deallocs.begin(); it != ca_deallocs.end(); ++it) {
-    std::ptr
diff _t const allocs   = std::count(ca_allocs.begin(), ca_allocs.end(), *it);
-    std::ptr
diff _t const deallocs = std::count(ca_deallocs.begin(), ca_deallocs.end(), *it);
-    std::printf("%d: %td vs %td\n", *it, allocs, deallocs);
-    if (allocs != deallocs)
-      return false;
+      V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      const Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      Map copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(3, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(3, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into an empty one
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      const Map orig(rhs_alloc);
+      Map copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into a non-empty one
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      const Map orig(rhs_alloc);
+      Map copy(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
   }
 
-  return true;
-}
-#endif
+  {   // Ensure that self-assignment works correctly
+    { // with a non-empty map
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+      V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      orig = static_cast<const Map&>(orig);
+
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(3, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    { // with an empty map
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      Map orig(rhs_alloc);
+      orig = static_cast<const Map&>(orig);
+
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
   }
-  {
-    typedef std::pair<const int, double> V;
-    const V ar[] = {
-        V(1, 1),
-        V(2, 1),
-        V(3, 1),
-    };
-    std::map<int, double> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
-    std::map<int, double>* p = &m;
-    m                        = *p;
 
-    assert(m.size() == 3);
-    assert(std::equal(m.begin(), m.end(), ar));
+  { // check assignment into a non-empty map
+    check_alloc_invariant();
+    { // LHS already contains elements, but fewer than the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(3, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(3, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS contains the same number of elements as the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(3, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(3, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS already contains more elements than the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::map<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0), V(7, 9)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(3, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(3, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+  { // Make a somewhat larget set to exercise the algorithm a bit
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, std::less<int>, Alloc>;
+
+    Map orig(rhs_alloc);
+    for (int i = 0; i != 50; ++i)
+      orig[i] = i + 3;
+
+    Map copy(lhs_alloc);
+    copy  = orig;
+    int i = 0;
+    for (auto v : copy) {
+      assert(v == V(i, i + 3));
+      ++i;
+    }
   }
+  check_alloc_invariant();
+}
+
+void test() {
+  test_alloc<std::allocator<std::pair<const int, int> > >();
 #if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
-    m = mo;
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
-  }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
-    m = mo;
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+  test_alloc<min_allocator<std::pair<const int, int> > >();
+
+  { // Make sure we're allocating/deallocating nodes with the correct allocator
+    // See https://llvm.org/PR29001
+    class AssertEmpty {
+      std::vector<void*>* lhs_allocs_;
+      std::vector<void*>* rhs_allocs_;
+
+    public:
+      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+          : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
+
+      void operator()() {
+        assert(lhs_allocs_->empty());
+        assert(rhs_allocs_->empty());
+      }
+    };
+
+    std::vector<void*> lhs_allocs;
+    std::vector<void*> rhs_allocs;
+    test_alloc<tracking_allocator<std::pair<const int, int> > >(
+        lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
   }
+#endif
+
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::map<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(3, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy;
+    copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
 
-  assert(balanced_allocs());
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef counting_allocatorT<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(1));
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(2));
-    m = mo;
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
-  assert(balanced_allocs());
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {V(1, 1), V(1, 1.5), V(1, 2), V(2, 1), V(2, 1.5), V(2, 2), V(3, 1), V(3, 1.5), V(3, 2)};
-    typedef test_less<int> C;
-    typedef counting_allocatorF<V> A;
-    std::map<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(100));
-    std::map<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(200));
-    m = mo;
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == V(1, 1));
-    assert(*std::next(m.begin()) == V(2, 1));
-    assert(*std::next(m.begin(), 2) == V(3, 1));
-
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == V(1, 1));
-    assert(*std::next(mo.begin()) == V(2, 1));
-    assert(*std::next(mo.begin(), 2) == V(3, 1));
+
+  { // Make sure the color is copied as well
+    std::map<int, int> in;
+
+    for (int i = 0; i != 32; ++i)
+      in[i] = i;
+
+    std::map<int, int> out = in;
+
+    out.erase(std::next(out.begin(), 10), out.end());
+    out = in;
+    out.erase(std::next(out.begin(), 17), out.end());
   }
-  assert(balanced_allocs());
-#endif
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy.pass.cpp
index 9cffbf8c17850..724755d1ef655 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy.pass.cpp
@@ -20,81 +20,122 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::multimap<int, double, C, A> m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
+template <template <class> class Alloc>
+void test_alloc() {
+  { // Simple check
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(2, 6));
+    assert(std::next(copy.begin(), 3) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(2, 6));
+    assert(std::next(orig.begin(), 3) == orig.end());
+  }
+
+  { // copy empty map
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
+
+    const Map orig;
+    Map copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6), V(4, 7), V(5, 0)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(2, 6));
+    assert(*std::next(copy.begin(), 3) == V(4, 7));
+    assert(*std::next(copy.begin(), 4) == V(5, 0));
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(2, 6));
+    assert(*std::next(orig.begin(), 3) == V(4, 7));
+    assert(*std::next(orig.begin(), 4) == V(5, 0));
+    assert(std::next(orig.begin(), 5) == orig.end());
+  }
+}
+
+void test() {
+  test_alloc<std::allocator>();
+  test_alloc<min_allocator>(); // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::multimap<int, double, C, A> m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A(-2));
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
+
+  { // Ensure that the allocator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, test_allocator<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr), std::less<int>(), test_allocator<V>(10));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == test_allocator<V>(10));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == test_allocator<V>(10));
+    assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::multimap<int, double, C, A> m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
+
+  { // Ensure that soccc is handled properly
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, other_allocator<V> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr), std::less<int>(), other_allocator<V>(10));
+    Map copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == other_allocator<V>(-2));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == other_allocator<V>(10));
   }
-#endif
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_alloc.pass.cpp
index 89e6c40aa108d..055efaecfca54 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_alloc.pass.cpp
@@ -20,81 +20,92 @@
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::multimap<int, double, C, A> m(mo, A(3));
-    assert(m == mo);
-    assert(m.get_allocator() == A(3));
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
+template <class Alloc>
+void test_alloc(const Alloc& new_alloc) {
+  { // Simple check
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr));
+    Map copy(orig, new_alloc);
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(2, 6));
+    assert(std::next(copy.begin(), 3) == copy.end());
+    assert(copy.get_allocator() == new_alloc);
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(2, 6));
+    assert(std::next(orig.begin(), 3) == orig.end());
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::multimap<int, double, C, A> m(mo, A());
-    assert(m == mo);
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
+
+  { // copy empty map
+    using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+    const Map orig;
+    Map copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6), V(4, 7), V(5, 0)};
+    const Map orig(begin(arr), end(arr));
+    Map copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == V(1, 1));
+    assert(*std::next(copy.begin(), 1) == V(2, 3));
+    assert(*std::next(copy.begin(), 2) == V(2, 6));
+    assert(*std::next(copy.begin(), 3) == V(4, 7));
+    assert(*std::next(copy.begin(), 4) == V(5, 0));
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == V(1, 1));
+    assert(*std::next(orig.begin(), 1) == V(2, 3));
+    assert(*std::next(orig.begin(), 2) == V(2, 6));
+    assert(*std::next(orig.begin(), 3) == V(4, 7));
+    assert(*std::next(orig.begin(), 4) == V(5, 0));
+    assert(std::next(orig.begin(), 5) == orig.end());
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef explicit_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A{});
-    std::multimap<int, double, C, A> m(mo, A{});
-    assert(m == mo);
-    assert(m.get_allocator() == A{});
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A{});
-    assert(mo.key_comp() == C(5));
+}
+
+void test() {
+  test_alloc(std::allocator<std::pair<const int, int> >());
+  test_alloc(test_allocator<std::pair<const int, int> >(25)); // Make sure that the new allocator is actually used
+  test_alloc(min_allocator<std::pair<const int, int> >());    // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy(orig, std::allocator<V>());
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
-#endif
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.pass.cpp
index 463403a412aaa..5830283e9b0c5 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.cons/copy_assign.pass.cpp
@@ -12,111 +12,286 @@
 
 // multimap& operator=(const multimap& m);
 
-#include <map>
+#include <algorithm>
 #include <cassert>
+#include <map>
+#include <vector>
 
 #include "test_macros.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 #include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
+template <class T>
+class tracking_allocator {
+  std::vector<void*>* allocs_;
+
+  template <class U>
+  friend class tracking_allocator;
+
+public:
+  using value_type                             = T;
+  using propagate_on_container_copy_assignment = std::true_type;
+
+  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+
+  template <class U>
+  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+
+  T* allocate(std::size_t n) {
+    T* allocation = std::allocator<T>().allocate(n);
+    allocs_->push_back(allocation);
+    return allocation;
   }
-  {
-    typedef std::pair<const int, double> V;
-    const V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    std::multimap<int, double> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
-    std::multimap<int, double>* p = &m;
-    m                             = *p;
-    assert(m.size() == sizeof(ar) / sizeof(ar[0]));
-    assert(std::equal(m.begin(), m.end(), ar));
+
+  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+    auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
+    assert(res != allocs_->end() && "Trying to deallocate memory from 
diff erent allocator?");
+    allocs_->erase(res);
+    std::allocator<T>().deallocate(ptr, n);
   }
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
-    };
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A(2));
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
+
+  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ == rhs.allocs_;
+  }
+
+  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ != rhs.allocs_;
   }
+};
+
+struct NoOp {
+  void operator()() {}
+};
+
+template <class Alloc, class AllocatorInvariant = NoOp>
+void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+                const Alloc& rhs_alloc                   = Alloc(),
+                AllocatorInvariant check_alloc_invariant = NoOp()) {
+  {   // Test empty/non-empy multimap combinations
+    { // assign from a non-empty container into an empty one
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      const Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      Map copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(2, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(2, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into an empty one
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      const Map orig(rhs_alloc);
+      Map copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into a non-empty one
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      const Map orig(rhs_alloc);
+      Map copy(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+  }
+
+  {   // Ensure that self-assignment works correctly
+    { // with a non-empty multimap
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      Map orig(begin(arr), end(arr), std::less<int>(), rhs_alloc);
+      orig = static_cast<const Map&>(orig);
+
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(2, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    { // with an empty multimap
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      Map orig(rhs_alloc);
+      orig = static_cast<const Map&>(orig);
+
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+  }
+
+  { // check assignment into a non-empty multimap
+    check_alloc_invariant();
+    { // LHS already contains elements, but fewer than the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(2, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(2, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS contains the same number of elements as the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(2, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(2, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS already contains more elements than the RHS
+      using V   = std::pair<const int, int>;
+      using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+      V lhs_arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+      const Map orig(begin(lhs_arr), end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      V rhs_arr[] = {V(4, 2), V(5, 1), V(6, 0), V(7, 9)};
+      Map copy(begin(rhs_arr), end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == V(1, 1));
+      assert(*std::next(copy.begin(), 1) == V(2, 3));
+      assert(*std::next(copy.begin(), 2) == V(2, 6));
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == V(1, 1));
+      assert(*std::next(orig.begin(), 1) == V(2, 3));
+      assert(*std::next(orig.begin(), 2) == V(2, 6));
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+  }
+  { // Make a somewhat larget set to exercise the algorithm a bit
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, std::less<int>, Alloc>;
+
+    Map orig(rhs_alloc);
+    for (int i = 0; i != 50; ++i) {
+      orig.insert(V(i, i + 3));
+      orig.insert(V(i, i + 5));
+    }
+
+    Map copy(lhs_alloc);
+    copy  = orig;
+    int i = 0;
+    for (auto iter = copy.begin(); iter != copy.end();) {
+      assert(*iter++ == V(i, i + 3));
+      assert(*iter++ == V(i, i + 5));
+      ++i;
+    }
+  }
+  check_alloc_invariant();
+}
+
+void test() {
+  test_alloc<std::allocator<std::pair<const int, int> > >();
 #if TEST_STD_VER >= 11
-  {
-    typedef std::pair<const int, double> V;
-    V ar[] = {
-        V(1, 1),
-        V(1, 1.5),
-        V(1, 2),
-        V(2, 1),
-        V(2, 1.5),
-        V(2, 2),
-        V(3, 1),
-        V(3, 1.5),
-        V(3, 2),
+  test_alloc<min_allocator<std::pair<const int, int> > >();
+
+  { // Make sure we're allocating/deallocating nodes with the correct allocator
+    // See https://llvm.org/PR29001
+    class AssertEmpty {
+      std::vector<void*>* lhs_allocs_;
+      std::vector<void*>* rhs_allocs_;
+
+    public:
+      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+          : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
+
+      void operator()() {
+        assert(lhs_allocs_->empty());
+        assert(rhs_allocs_->empty());
+      }
     };
-    typedef test_less<int> C;
-    typedef min_allocator<V> A;
-    std::multimap<int, double, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A());
-    std::multimap<int, double, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A());
-    m = mo;
-    assert(m == mo);
-    assert(m.get_allocator() == A());
-    assert(m.key_comp() == C(5));
-
-    assert(mo.get_allocator() == A());
-    assert(mo.key_comp() == C(5));
+
+    std::vector<void*> lhs_allocs;
+    std::vector<void*> rhs_allocs;
+    test_alloc<tracking_allocator<std::pair<const int, int> > >(
+        lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
   }
 #endif
 
+  { // Ensure that the comparator is copied
+    using V   = std::pair<const int, int>;
+    using Map = std::multimap<int, int, test_less<int> >;
+
+    V arr[] = {V(1, 1), V(2, 3), V(2, 6)};
+    const Map orig(begin(arr), end(arr), test_less<int>(3));
+    Map copy;
+    copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+}
+
+int main(int, char**) {
+  test();
+
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
index 8138227128e8a..c747828aa01cc 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy.pass.cpp
@@ -16,83 +16,121 @@
 #include <cassert>
 #include <iterator>
 
+#include "min_allocator.h"
 #include "test_macros.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::multiset<int, C, A> m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 9);
-    assert(std::distance(m.begin(), m.end()) == 9);
-    assert(*std::next(m.begin(), 0) == 1);
-    assert(*std::next(m.begin(), 1) == 1);
-    assert(*std::next(m.begin(), 2) == 1);
-    assert(*std::next(m.begin(), 3) == 2);
-    assert(*std::next(m.begin(), 4) == 2);
-    assert(*std::next(m.begin(), 5) == 2);
-    assert(*std::next(m.begin(), 6) == 3);
-    assert(*std::next(m.begin(), 7) == 3);
-    assert(*std::next(m.begin(), 8) == 3);
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 9);
-    assert(std::distance(mo.begin(), mo.end()) == 9);
-    assert(*std::next(mo.begin(), 0) == 1);
-    assert(*std::next(mo.begin(), 1) == 1);
-    assert(*std::next(mo.begin(), 2) == 1);
-    assert(*std::next(mo.begin(), 3) == 2);
-    assert(*std::next(mo.begin(), 4) == 2);
-    assert(*std::next(mo.begin(), 5) == 2);
-    assert(*std::next(mo.begin(), 6) == 3);
-    assert(*std::next(mo.begin(), 7) == 3);
-    assert(*std::next(mo.begin(), 8) == 3);
+template <template <class> class Alloc>
+void test_alloc() {
+  { // Simple check
+    using Set = std::multiset<int, std::less<int>, Alloc<int> >;
+
+    int arr[] = {1, 2, 2};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 2);
+    assert(std::next(copy.begin(), 3) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 2);
+    assert(std::next(orig.begin(), 3) == orig.end());
+  }
+
+  { // copy empty set
+    using Set = std::multiset<int, std::less<int>, Alloc<int> >;
+
+    const Set orig;
+    Set copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::multiset<int, C, A> m = mo;
-    assert(m.get_allocator() == A(-2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 9);
-    assert(std::distance(m.begin(), m.end()) == 9);
-    assert(*std::next(m.begin(), 0) == 1);
-    assert(*std::next(m.begin(), 1) == 1);
-    assert(*std::next(m.begin(), 2) == 1);
-    assert(*std::next(m.begin(), 3) == 2);
-    assert(*std::next(m.begin(), 4) == 2);
-    assert(*std::next(m.begin(), 5) == 2);
-    assert(*std::next(m.begin(), 6) == 3);
-    assert(*std::next(m.begin(), 7) == 3);
-    assert(*std::next(m.begin(), 8) == 3);
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 9);
-    assert(std::distance(mo.begin(), mo.end()) == 9);
-    assert(*std::next(mo.begin(), 0) == 1);
-    assert(*std::next(mo.begin(), 1) == 1);
-    assert(*std::next(mo.begin(), 2) == 1);
-    assert(*std::next(mo.begin(), 3) == 2);
-    assert(*std::next(mo.begin(), 4) == 2);
-    assert(*std::next(mo.begin(), 5) == 2);
-    assert(*std::next(mo.begin(), 6) == 3);
-    assert(*std::next(mo.begin(), 7) == 3);
-    assert(*std::next(mo.begin(), 8) == 3);
+
+  { // only some leaf nodes exist
+    using Set = std::multiset<int, std::less<int>, Alloc<int> >;
+
+    int arr[] = {1, 2, 3, 3, 5};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(*std::next(copy.begin(), 3) == 3);
+    assert(*std::next(copy.begin(), 4) == 5);
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(*std::next(orig.begin(), 3) == 3);
+    assert(*std::next(orig.begin(), 4) == 5);
+    assert(std::next(orig.begin(), 5) == orig.end());
   }
-#endif
+}
+
+void test() {
+  test_alloc<std::allocator>();
+  test_alloc<min_allocator>(); // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using Set = std::multiset<int, test_less<int> >;
+
+    int arr[] = {1, 2, 2};
+    const Set orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+
+  { // Ensure that the allocator is copied
+    using Set = std::multiset<int, std::less<int>, test_allocator<int> >;
+
+    int arr[] = {1, 2, 2};
+    const Set orig(std::begin(arr), std::end(arr), std::less<int>(), test_allocator<int>(10));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == test_allocator<int>(10));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == test_allocator<int>(10));
+    assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
+  }
+
+  { // Ensure that soccc is handled properly
+    using Set = std::multiset<int, std::less<int>, other_allocator<int> >;
+
+    int arr[] = {1, 2, 2};
+    const Set orig(std::begin(arr), std::end(arr), std::less<int>(), other_allocator<int>(10));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == other_allocator<int>(-2));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == other_allocator<int>(10));
+  }
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
index 7570673066829..868072ef15288 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_alloc.pass.cpp
@@ -16,44 +16,92 @@
 #include <cassert>
 #include <iterator>
 
+#include "min_allocator.h"
 #include "test_macros.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
+template <class Alloc>
+void test_alloc(const Alloc& new_alloc) {
+  { // Simple check
+    using Set = std::multiset<int, std::less<int>, Alloc>;
+
+    int arr[] = {1, 2, 2};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy(orig, new_alloc);
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 2);
+    assert(std::next(copy.begin(), 3) == copy.end());
+    assert(copy.get_allocator() == new_alloc);
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 2);
+    assert(std::next(orig.begin(), 3) == orig.end());
+  }
+
+  { // copy empty set
+    using Set = std::multiset<int, std::less<int>, Alloc>;
+
+    const Set orig;
+    Set copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using Set = std::multiset<int, std::less<int>, Alloc>;
+
+    int arr[] = {1, 2, 3, 3, 5};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(*std::next(copy.begin(), 3) == 3);
+    assert(*std::next(copy.begin(), 4) == 5);
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(*std::next(orig.begin(), 3) == 3);
+    assert(*std::next(orig.begin(), 4) == 5);
+    assert(std::next(orig.begin(), 5) == orig.end());
+  }
+}
+
+void test() {
+  test_alloc(std::allocator<int>());
+  test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
+  test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    int arr[] = {1, 2, 2};
+    const std::multiset<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    std::multiset<int, test_less<int> > copy(orig, std::allocator<int>());
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+}
+
 int main(int, char**) {
-  typedef int V;
-  V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-  typedef test_less<int> C;
-  typedef test_allocator<V> A;
-  std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-  std::multiset<int, C, A> m(mo, A(3));
-  assert(m.get_allocator() == A(3));
-  assert(m.key_comp() == C(5));
-  assert(m.size() == 9);
-  assert(std::distance(m.begin(), m.end()) == 9);
-  assert(*std::next(m.begin(), 0) == 1);
-  assert(*std::next(m.begin(), 1) == 1);
-  assert(*std::next(m.begin(), 2) == 1);
-  assert(*std::next(m.begin(), 3) == 2);
-  assert(*std::next(m.begin(), 4) == 2);
-  assert(*std::next(m.begin(), 5) == 2);
-  assert(*std::next(m.begin(), 6) == 3);
-  assert(*std::next(m.begin(), 7) == 3);
-  assert(*std::next(m.begin(), 8) == 3);
-
-  assert(mo.get_allocator() == A(7));
-  assert(mo.key_comp() == C(5));
-  assert(mo.size() == 9);
-  assert(std::distance(mo.begin(), mo.end()) == 9);
-  assert(*std::next(mo.begin(), 0) == 1);
-  assert(*std::next(mo.begin(), 1) == 1);
-  assert(*std::next(mo.begin(), 2) == 1);
-  assert(*std::next(mo.begin(), 3) == 2);
-  assert(*std::next(mo.begin(), 4) == 2);
-  assert(*std::next(mo.begin(), 5) == 2);
-  assert(*std::next(mo.begin(), 6) == 3);
-  assert(*std::next(mo.begin(), 7) == 3);
-  assert(*std::next(mo.begin(), 8) == 3);
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
index d6b394ff66698..8511f05ab57e4 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.cons/copy_assign.pass.cpp
@@ -12,96 +12,271 @@
 
 // multiset& operator=(const multiset& s);
 
-#include <set>
+#include <algorithm>
 #include <cassert>
 #include <iterator>
+#include <set>
+#include <vector>
 
-#include "test_macros.h"
 #include "../../../test_compare.h"
-#include "test_allocator.h"
+#include "min_allocator.h"
+#include "test_macros.h"
 
-int main(int, char**) {
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::multiset<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 9);
-    assert(std::distance(m.begin(), m.end()) == 9);
-    assert(*std::next(m.begin(), 0) == 1);
-    assert(*std::next(m.begin(), 1) == 1);
-    assert(*std::next(m.begin(), 2) == 1);
-    assert(*std::next(m.begin(), 3) == 2);
-    assert(*std::next(m.begin(), 4) == 2);
-    assert(*std::next(m.begin(), 5) == 2);
-    assert(*std::next(m.begin(), 6) == 3);
-    assert(*std::next(m.begin(), 7) == 3);
-    assert(*std::next(m.begin(), 8) == 3);
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 9);
-    assert(std::distance(mo.begin(), mo.end()) == 9);
-    assert(*std::next(mo.begin(), 0) == 1);
-    assert(*std::next(mo.begin(), 1) == 1);
-    assert(*std::next(mo.begin(), 2) == 1);
-    assert(*std::next(mo.begin(), 3) == 2);
-    assert(*std::next(mo.begin(), 4) == 2);
-    assert(*std::next(mo.begin(), 5) == 2);
-    assert(*std::next(mo.begin(), 6) == 3);
-    assert(*std::next(mo.begin(), 7) == 3);
-    assert(*std::next(mo.begin(), 8) == 3);
+template <class T>
+class tracking_allocator {
+  std::vector<void*>* allocs_;
+
+  template <class U>
+  friend class tracking_allocator;
+
+public:
+  using value_type                             = T;
+  using propagate_on_container_copy_assignment = std::true_type;
+
+  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+
+  template <class U>
+  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+
+  T* allocate(std::size_t n) {
+    T* allocation = std::allocator<T>().allocate(n);
+    allocs_->push_back(allocation);
+    return allocation;
+  }
+
+  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+    auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
+    assert(res != allocs_->end() && "Trying to deallocate memory from 
diff erent allocator?");
+    allocs_->erase(res);
+    std::allocator<T>().deallocate(ptr, n);
+  }
+
+  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ == rhs.allocs_;
   }
-  {
-    typedef int V;
-    const V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    std::multiset<int> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
-    std::multiset<int>* p = &m;
-    m                     = *p;
-    assert(m.size() == 9);
-    assert(std::equal(m.begin(), m.end(), ar));
+
+  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ != rhs.allocs_;
   }
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::multiset<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::multiset<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 9);
-    assert(std::distance(m.begin(), m.end()) == 9);
-    assert(*std::next(m.begin(), 0) == 1);
-    assert(*std::next(m.begin(), 1) == 1);
-    assert(*std::next(m.begin(), 2) == 1);
-    assert(*std::next(m.begin(), 3) == 2);
-    assert(*std::next(m.begin(), 4) == 2);
-    assert(*std::next(m.begin(), 5) == 2);
-    assert(*std::next(m.begin(), 6) == 3);
-    assert(*std::next(m.begin(), 7) == 3);
-    assert(*std::next(m.begin(), 8) == 3);
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 9);
-    assert(std::distance(mo.begin(), mo.end()) == 9);
-    assert(*std::next(mo.begin(), 0) == 1);
-    assert(*std::next(mo.begin(), 1) == 1);
-    assert(*std::next(mo.begin(), 2) == 1);
-    assert(*std::next(mo.begin(), 3) == 2);
-    assert(*std::next(mo.begin(), 4) == 2);
-    assert(*std::next(mo.begin(), 5) == 2);
-    assert(*std::next(mo.begin(), 6) == 3);
-    assert(*std::next(mo.begin(), 7) == 3);
-    assert(*std::next(mo.begin(), 8) == 3);
+};
+
+struct NoOp {
+  void operator()() {}
+};
+
+template <class Alloc, class AllocatorInvariant = NoOp>
+void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+                const Alloc& rhs_alloc                   = Alloc(),
+                AllocatorInvariant check_alloc_invariant = NoOp()) {
+  {   // Test empty/non-empy multiset combinations
+    { // assign from a non-empty container into an empty one
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 2};
+      const Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      Set copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 2);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 2);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into an empty one
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      const Set orig(rhs_alloc);
+      Set copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into a non-empty one
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 2};
+      const Set orig(rhs_alloc);
+      Set copy(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
   }
 
+  {   // Ensure that self-assignment works correctly
+    { // with a non-empty multiset
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 2};
+      Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      orig = static_cast<const Set&>(orig);
+
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 2);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    { // with an empty multiset
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      Set orig(rhs_alloc);
+      orig = static_cast<const Set&>(orig);
+
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+  }
+
+  { // check assignment into a non-empty multiset
+    check_alloc_invariant();
+    { // LHS already contains elements, but fewer than the RHS
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 2};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 2);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 2);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS contains the same number of elements as the RHS
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 2};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5, 6};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 2);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 2);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS already contains more elements than the RHS
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 2};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5, 6};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 2);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 2);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // Make a somewhat larget multiset to exercise the algorithm a bit
+      using Set = std::multiset<int, std::less<int>, Alloc>;
+
+      Set orig(rhs_alloc);
+      for (int i = 0; i != 50; ++i)
+        orig.insert(i);
+
+      Set copy(lhs_alloc);
+      copy  = orig;
+      int i = 0;
+      for (auto v : copy) {
+        assert(v == i++);
+      }
+    }
+    check_alloc_invariant();
+  }
+}
+
+void test() {
+  test_alloc<std::allocator<int> >();
+#if TEST_STD_VER >= 11
+  test_alloc<min_allocator<int> >();
+
+  { // Make sure we're allocating/deallocating nodes with the correct allocator
+    // See https://llvm.org/PR29001
+    class AssertEmpty {
+      std::vector<void*>* lhs_allocs_;
+      std::vector<void*>* rhs_allocs_;
+
+    public:
+      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+          : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
+
+      void operator()() {
+        assert(lhs_allocs_->empty());
+        assert(rhs_allocs_->empty());
+      }
+    };
+
+    std::vector<void*> lhs_allocs;
+    std::vector<void*> rhs_allocs;
+    test_alloc<tracking_allocator<int> >(lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
+  }
+#endif
+
+  { // Ensure that the comparator is copied
+    int arr[] = {1, 2, 2};
+    const std::multiset<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    std::multiset<int, test_less<int> > copy;
+    copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+}
+
+int main(int, char**) {
+  test();
+
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
index 01a1b6ab59af0..8fac335175c80 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy.pass.cpp
@@ -12,63 +12,123 @@
 
 // set(const set& m);
 
-#include <set>
 #include <cassert>
-#include <iterator>
+#include <set>
 
-#include "test_macros.h"
+#include "min_allocator.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::set<int, C, A> m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == 1);
-    assert(*std::next(m.begin()) == 2);
-    assert(*std::next(m.begin(), 2) == 3);
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == 1);
-    assert(*std::next(mo.begin()) == 2);
-    assert(*std::next(mo.begin(), 2) == 3);
+template <template <class> class Alloc>
+void test_alloc() {
+  { // Simple check
+    using Set = std::set<int, std::less<int>, Alloc<int> >;
+
+    int arr[] = {1, 2, 3};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(std::next(copy.begin(), 3) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(std::next(orig.begin(), 3) == orig.end());
   }
-#if TEST_STD_VER >= 11
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-    std::set<int, C, A> m = mo;
-    assert(m.get_allocator() == A(-2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == 1);
-    assert(*std::next(m.begin()) == 2);
-    assert(*std::next(m.begin(), 2) == 3);
-
-    assert(mo.get_allocator() == A(7));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == 1);
-    assert(*std::next(mo.begin()) == 2);
-    assert(*std::next(mo.begin(), 2) == 3);
+
+  { // copy empty set
+    using Set = std::set<int, std::less<int>, Alloc<int> >;
+
+    const Set orig;
+    Set copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
   }
-#endif
+
+  { // only some leaf nodes exist
+    using Set = std::set<int, std::less<int>, Alloc<int> >;
+
+    int arr[] = {1, 2, 3, 4, 5};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(*std::next(copy.begin(), 3) == 4);
+    assert(*std::next(copy.begin(), 4) == 5);
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(*std::next(orig.begin(), 3) == 4);
+    assert(*std::next(orig.begin(), 4) == 5);
+    assert(std::next(orig.begin(), 5) == orig.end());
+  }
+}
+
+void test() {
+  test_alloc<std::allocator>();
+  test_alloc<min_allocator>(); // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    using Set = std::set<int, test_less<int> >;
+
+    int arr[] = {1, 2, 3};
+    const Set orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+
+  { // Ensure that the allocator is copied
+    using Set = std::set<int, std::less<int>, test_allocator<int> >;
+
+    int arr[] = {1, 2, 3};
+    const Set orig(std::begin(arr), std::end(arr), std::less<int>(), test_allocator<int>(10));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == test_allocator<int>(10));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == test_allocator<int>(10));
+    assert(orig.get_allocator().get_id() != test_alloc_base::moved_value);
+  }
+
+  { // Ensure that soccc is handled properly
+    using Set = std::set<int, std::less<int>, other_allocator<int> >;
+
+    int arr[] = {1, 2, 3};
+    const Set orig(std::begin(arr), std::end(arr), std::less<int>(), other_allocator<int>(10));
+    Set copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.get_allocator() == other_allocator<int>(-2));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.get_allocator() == other_allocator<int>(10));
+  }
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
index 8f9dd769329c0..2784af65b0a0f 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_alloc.pass.cpp
@@ -12,36 +12,95 @@
 
 // set(const set& m, const allocator_type& a);
 
-#include <set>
 #include <cassert>
-#include <iterator>
+#include <set>
 
-#include "test_macros.h"
 #include "../../../test_compare.h"
+#include "min_allocator.h"
+#include "test_macros.h"
 #include "test_allocator.h"
 
+template <class Alloc>
+void test_alloc(const Alloc& new_alloc) {
+  { // Simple check
+    using Set = std::set<int, std::less<int>, Alloc>;
+
+    int arr[] = {1, 2, 3};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy(orig, new_alloc);
+    assert(copy.size() == 3);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(std::next(copy.begin(), 3) == copy.end());
+    assert(copy.get_allocator() == new_alloc);
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(std::next(orig.begin(), 3) == orig.end());
+  }
+
+  { // copy empty set
+    using Set = std::set<int, std::less<int>, Alloc>;
+
+    const Set orig;
+    Set copy = orig;
+    assert(copy.size() == 0);
+    assert(copy.begin() == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 0);
+    assert(orig.begin() == orig.end());
+  }
+
+  { // only some leaf nodes exist
+    using Set = std::set<int, std::less<int>, Alloc>;
+
+    int arr[] = {1, 2, 3, 4, 5};
+    const Set orig(std::begin(arr), std::end(arr));
+    Set copy = orig;
+    assert(copy.size() == 5);
+    assert(*std::next(copy.begin(), 0) == 1);
+    assert(*std::next(copy.begin(), 1) == 2);
+    assert(*std::next(copy.begin(), 2) == 3);
+    assert(*std::next(copy.begin(), 3) == 4);
+    assert(*std::next(copy.begin(), 4) == 5);
+    assert(std::next(copy.begin(), 5) == copy.end());
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 5);
+    assert(*std::next(orig.begin(), 0) == 1);
+    assert(*std::next(orig.begin(), 1) == 2);
+    assert(*std::next(orig.begin(), 2) == 3);
+    assert(*std::next(orig.begin(), 3) == 4);
+    assert(*std::next(orig.begin(), 4) == 5);
+    assert(std::next(orig.begin(), 5) == orig.end());
+  }
+}
+
+void test() {
+  test_alloc(std::allocator<int>());
+  test_alloc(test_allocator<int>(25)); // Make sure that the new allocator is actually used
+  test_alloc(min_allocator<int>());    // Make sure that fancy pointers work
+
+  { // Ensure that the comparator is copied
+    int arr[] = {1, 2, 3};
+    const std::set<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    std::set<int, test_less<int> > copy(orig, std::allocator<int>());
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
+
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
+  }
+}
+
 int main(int, char**) {
-  typedef int V;
-  V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-  typedef test_less<int> C;
-  typedef test_allocator<V> A;
-  std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(7));
-  std::set<int, C, A> m(mo, A(3));
-  assert(m.get_allocator() == A(3));
-  assert(m.key_comp() == C(5));
-  assert(m.size() == 3);
-  assert(std::distance(m.begin(), m.end()) == 3);
-  assert(*m.begin() == 1);
-  assert(*std::next(m.begin()) == 2);
-  assert(*std::next(m.begin(), 2) == 3);
-
-  assert(mo.get_allocator() == A(7));
-  assert(mo.key_comp() == C(5));
-  assert(mo.size() == 3);
-  assert(std::distance(mo.begin(), mo.end()) == 3);
-  assert(*mo.begin() == 1);
-  assert(*std::next(mo.begin()) == 2);
-  assert(*std::next(mo.begin(), 2) == 3);
+  test();
 
   return 0;
 }

diff  --git a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
index f154f5d8b994c..85bc4e761f2c3 100644
--- a/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.cons/copy_assign.pass.cpp
@@ -12,83 +12,273 @@
 
 // set& operator=(const set& s);
 
-#include <set>
+#include <algorithm>
 #include <cassert>
+#include <cstdio>
 #include <iterator>
+#include <set>
+#include <vector>
 
 #include "test_macros.h"
 #include "../../../test_compare.h"
 #include "test_allocator.h"
+#include "min_allocator.h"
 
-int main(int, char**) {
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef test_allocator<V> A;
-    std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::set<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(7));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == 1);
-    assert(*std::next(m.begin()) == 2);
-    assert(*std::next(m.begin(), 2) == 3);
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == 1);
-    assert(*std::next(mo.begin()) == 2);
-    assert(*std::next(mo.begin(), 2) == 3);
+template <class T>
+class tracking_allocator {
+  std::vector<void*>* allocs_;
+
+  template <class U>
+  friend class tracking_allocator;
+
+public:
+  using value_type                             = T;
+  using propagate_on_container_copy_assignment = std::true_type;
+
+  tracking_allocator(std::vector<void*>& allocs) : allocs_(&allocs) {}
+
+  template <class U>
+  tracking_allocator(const tracking_allocator<U>& other) : allocs_(other.allocs_) {}
+
+  T* allocate(std::size_t n) {
+    T* allocation = std::allocator<T>().allocate(n);
+    allocs_->push_back(allocation);
+    return allocation;
+  }
+
+  void deallocate(T* ptr, std::size_t n) TEST_NOEXCEPT {
+    auto res = std::remove(allocs_->begin(), allocs_->end(), ptr);
+    assert(res != allocs_->end() && "Trying to deallocate memory from 
diff erent allocator?");
+    allocs_->erase(res);
+    std::allocator<T>().deallocate(ptr, n);
+  }
+
+  friend bool operator==(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ == rhs.allocs_;
+  }
+
+  friend bool operator!=(const tracking_allocator& lhs, const tracking_allocator& rhs) {
+    return lhs.allocs_ != rhs.allocs_;
+  }
+};
+
+struct NoOp {
+  void operator()() {}
+};
+
+template <class Alloc, class AllocatorInvariant = NoOp>
+void test_alloc(const Alloc& lhs_alloc                   = Alloc(),
+                const Alloc& rhs_alloc                   = Alloc(),
+                AllocatorInvariant check_alloc_invariant = NoOp()) {
+  {   // Test empty/non-empy set combinations
+    { // assign from a non-empty container into an empty one
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 3};
+      const Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      Set copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 3);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 3);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into an empty one
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      const Set orig(rhs_alloc);
+      Set copy(lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+    check_alloc_invariant();
+    { // assign from an empty container into a non-empty one
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 3};
+      const Set orig(rhs_alloc);
+      Set copy(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      copy = orig;
+      assert(copy.size() == 0);
+      assert(copy.begin() == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
+  }
+
+  {   // Ensure that self-assignment works correctly
+    { // with a non-empty set
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int arr[] = {1, 2, 3};
+      Set orig(std::begin(arr), std::end(arr), std::less<int>(), rhs_alloc);
+      orig = static_cast<const Set&>(orig);
+
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 3);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    { // with an empty set
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      Set orig(rhs_alloc);
+      orig = static_cast<const Set&>(orig);
+
+      assert(orig.size() == 0);
+      assert(orig.begin() == orig.end());
+    }
   }
-  {
-    typedef int V;
-    const V ar[] = {1, 2, 3};
-    std::set<int> m(ar, ar + sizeof(ar) / sizeof(ar[0]));
-    std::set<int>* p = &m;
-    m                = *p;
-
-    assert(m.size() == 3);
-    assert(std::equal(m.begin(), m.end(), ar));
+
+  { // check assignment into a non-empty set
+    check_alloc_invariant();
+    { // LHS already contains elements, but fewer than the RHS
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 3};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 3);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 3);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS contains the same number of elements as the RHS
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 3};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5, 6};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 3);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 3);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // LHS already contains more elements than the RHS
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      int lhs_arr[] = {1, 2, 3};
+      const Set orig(std::begin(lhs_arr), std::end(lhs_arr), std::less<int>(), rhs_alloc);
+
+      int rhs_arr[] = {4, 5, 6};
+      Set copy(std::begin(rhs_arr), std::end(rhs_arr), std::less<int>(), lhs_alloc);
+      copy = orig;
+      assert(copy.size() == 3);
+      assert(*std::next(copy.begin(), 0) == 1);
+      assert(*std::next(copy.begin(), 1) == 2);
+      assert(*std::next(copy.begin(), 2) == 3);
+      assert(std::next(copy.begin(), 3) == copy.end());
+
+      // Check that orig is still what is expected
+      assert(orig.size() == 3);
+      assert(*std::next(orig.begin(), 0) == 1);
+      assert(*std::next(orig.begin(), 1) == 2);
+      assert(*std::next(orig.begin(), 2) == 3);
+      assert(std::next(orig.begin(), 3) == orig.end());
+    }
+    check_alloc_invariant();
+    { // Make a somewhat larget set to exercise the algorithm a bit
+      using Set = std::set<int, std::less<int>, Alloc>;
+
+      Set orig(rhs_alloc);
+      for (int i = 0; i != 50; ++i)
+        orig.insert(i);
+
+      Set copy(lhs_alloc);
+      copy  = orig;
+      int i = 0;
+      for (auto v : copy) {
+        assert(v == i++);
+      }
+    }
+    check_alloc_invariant();
   }
-  {
-    typedef int V;
-    V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
-    typedef test_less<int> C;
-    typedef other_allocator<V> A;
-    std::set<int, C, A> mo(ar, ar + sizeof(ar) / sizeof(ar[0]), C(5), A(2));
-    std::set<int, C, A> m(ar, ar + sizeof(ar) / sizeof(ar[0]) / 2, C(3), A(7));
-    m = mo;
-    assert(m.get_allocator() == A(2));
-    assert(m.key_comp() == C(5));
-    assert(m.size() == 3);
-    assert(std::distance(m.begin(), m.end()) == 3);
-    assert(*m.begin() == 1);
-    assert(*std::next(m.begin()) == 2);
-    assert(*std::next(m.begin(), 2) == 3);
-
-    assert(mo.get_allocator() == A(2));
-    assert(mo.key_comp() == C(5));
-    assert(mo.size() == 3);
-    assert(std::distance(mo.begin(), mo.end()) == 3);
-    assert(*mo.begin() == 1);
-    assert(*std::next(mo.begin()) == 2);
-    assert(*std::next(mo.begin(), 2) == 3);
+}
+
+void test() {
+  test_alloc<std::allocator<int> >();
+#if TEST_STD_VER >= 11
+  test_alloc<min_allocator<int> >();
+
+  { // Make sure we're allocating/deallocating nodes with the correct allocator
+    // See https://llvm.org/PR29001
+    class AssertEmpty {
+      std::vector<void*>* lhs_allocs_;
+      std::vector<void*>* rhs_allocs_;
+
+    public:
+      AssertEmpty(std::vector<void*>& lhs_allocs, std::vector<void*>& rhs_allocs)
+          : lhs_allocs_(&lhs_allocs), rhs_allocs_(&rhs_allocs) {}
+
+      void operator()() {
+        assert(lhs_allocs_->empty());
+        assert(rhs_allocs_->empty());
+      }
+    };
+
+    std::vector<void*> lhs_allocs;
+    std::vector<void*> rhs_allocs;
+    test_alloc<tracking_allocator<int> >(lhs_allocs, rhs_allocs, AssertEmpty(lhs_allocs, rhs_allocs));
   }
+#endif
 
-  { // Test with std::pair, since we have some special handling for pairs inside __tree
-    std::pair<int, int> arr[] = {
-        std::make_pair(1, 2), std::make_pair(2, 3), std::make_pair(3, 4), std::make_pair(4, 5)};
-    std::set<std::pair<int, int> > a(arr, arr + 4);
-    std::set<std::pair<int, int> > b;
+  { // Ensure that the comparator is copied
+    int arr[] = {1, 2, 3};
+    const std::set<int, test_less<int> > orig(std::begin(arr), std::end(arr), test_less<int>(3));
+    std::set<int, test_less<int> > copy;
+    copy = orig;
+    assert(copy.size() == 3);
+    assert(copy.key_comp() == test_less<int>(3));
 
-    b = a;
-    assert(a == b);
+    // Check that orig is still what is expected
+    assert(orig.size() == 3);
+    assert(orig.key_comp() == test_less<int>(3));
   }
+}
+
+int main(int, char**) {
+  test();
 
   return 0;
 }


        


More information about the libcxx-commits mailing list