From 31ee7a2bfb0b6ea49b73547338d8291992bc669a Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 1 Apr 2019 06:45:02 +0200
Subject: [PATCH 1/2] Update immer library to current master
 (0a718d2d76bab6ebdcf43de943bd6c7d2dbfe2f9)

---
 src/immer/array.hpp                           |  19 +-
 src/immer/atom.hpp                            | 259 ++++++++++++++++
 src/immer/box.hpp                             |  15 +-
 src/immer/config.hpp                          |  12 +-
 src/immer/detail/arrays/no_capacity.hpp       |  10 +-
 src/immer/detail/arrays/node.hpp              |   9 +-
 src/immer/detail/arrays/with_capacity.hpp     |   9 +-
 src/immer/detail/hamts/bits.hpp               |  72 ++++-
 src/immer/detail/hamts/champ.hpp              |  15 +-
 src/immer/detail/hamts/champ_iterator.hpp     |  26 +-
 src/immer/detail/hamts/node.hpp               |  82 ++---
 src/immer/detail/rbts/operations.hpp          | 285 ++++++++++--------
 src/immer/detail/rbts/position.hpp            |  38 ++-
 src/immer/detail/rbts/rbtree.hpp              |   8 +-
 src/immer/detail/rbts/rbtree_iterator.hpp     |   4 +-
 src/immer/detail/rbts/rrbtree.hpp             |   8 +-
 src/immer/detail/rbts/rrbtree_iterator.hpp    |   4 +-
 src/immer/detail/rbts/visitor.hpp             |  95 +++---
 src/immer/detail/type_traits.hpp              | 191 ++++++++++++
 src/immer/detail/util.hpp                     | 108 ++++++-
 src/immer/flex_vector.hpp                     |  12 +-
 src/immer/heap/debug_size_heap.hpp            |  13 +-
 src/immer/map.hpp                             |   6 +-
 src/immer/refcount/no_refcount_policy.hpp     |  14 +
 src/immer/refcount/refcount_policy.hpp        |  59 ++++
 src/immer/refcount/unsafe_refcount_policy.hpp |   2 +
 src/immer/set.hpp                             |   6 +-
 src/immer/vector.hpp                          |   8 +-
 28 files changed, 1065 insertions(+), 324 deletions(-)
 create mode 100644 src/immer/atom.hpp
 create mode 100644 src/immer/detail/type_traits.hpp

diff --git a/src/immer/array.hpp b/src/immer/array.hpp
index b3ad63e33421..0f73649fb3ac 100644
--- a/src/immer/array.hpp
+++ b/src/immer/array.hpp
@@ -35,10 +35,6 @@ class array_transient;
  *    of doubt, measure.  For basic types, using an `array` when
  *    :math:`n < 100` is a good heuristic.
  *
- * .. warning:: The current implementation depends on
- *    ``boost::intrusive_ptr`` and does not support :doc:`memory
- *    policies<memory>`.  This will be fixed soon.
- *
  * @endrst
  */
 template <typename T, typename MemoryPolicy = default_memory_policy>
@@ -73,23 +69,26 @@ class array
     array() = default;
 
     /*!
-     * Constructs a vector containing the elements in `values`.
+     * Constructs an array containing the elements in `values`.
      */
     array(std::initializer_list<T> values)
         : impl_{impl_t::from_initializer_list(values)}
     {}
 
     /*!
-     * Constructs a vector containing the elements in the range
-     * defined by the input iterators `first` and `last`.
+     * Constructs a array containing the elements in the range
+     * defined by the forward iterator `first` and range sentinel `last`.
      */
-    template <typename Iter>
-    array(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <detail::compatible_sentinel_v<Iter, Sent>
+               && detail::is_forward_iterator_v<Iter>, bool> = true>
+    array(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
 
     /*!
-     * Constructs a vector containing the element `val` repeated `n`
+     * Constructs a array containing the element `val` repeated `n`
      * times.
      */
     array(size_type n, T v = {})
diff --git a/src/immer/atom.hpp b/src/immer/atom.hpp
new file mode 100644
index 000000000000..206f3c497a09
--- /dev/null
+++ b/src/immer/atom.hpp
@@ -0,0 +1,259 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/box.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
+
+#include <atomic>
+#include <type_traits>
+
+namespace immer {
+
+namespace detail {
+
+template <typename T, typename MemoryPolicy>
+struct refcount_atom_impl
+{
+    using box_type = box<T, MemoryPolicy>;
+    using value_type = T;
+    using memory_policy = MemoryPolicy;
+    using spinlock_t = typename MemoryPolicy::refcount::spinlock_type;
+    using scoped_lock_t = typename spinlock_t::scoped_lock;
+
+    refcount_atom_impl(const refcount_atom_impl&) = delete;
+    refcount_atom_impl(refcount_atom_impl&&) = delete;
+    refcount_atom_impl& operator=(const refcount_atom_impl&) = delete;
+    refcount_atom_impl& operator=(refcount_atom_impl&&) = delete;
+
+    refcount_atom_impl(box_type b)
+        : impl_{std::move(b)}
+    {}
+
+    box_type load() const
+    {
+        scoped_lock_t lock{lock_};
+        return impl_;
+    }
+
+    void store(box_type b)
+    {
+        scoped_lock_t lock{lock_};
+        impl_ = std::move(b);
+    }
+
+    box_type exchange(box_type b)
+    {
+        {
+            scoped_lock_t lock{lock_};
+            swap(b, impl_);
+        }
+        return std::move(b);
+    }
+
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    {
+        while (true) {
+            auto oldv = load();
+            auto newv = oldv.update(fn);
+            {
+                scoped_lock_t lock{lock_};
+                if (oldv.impl_ == impl_.impl_) {
+                    impl_ = newv;
+                    return { newv };
+                }
+            }
+        }
+    }
+
+private:
+    mutable spinlock_t lock_;
+    box_type impl_;
+};
+
+template <typename T, typename MemoryPolicy>
+struct gc_atom_impl
+{
+    using box_type = box<T, MemoryPolicy>;
+    using value_type = T;
+    using memory_policy = MemoryPolicy;
+
+    static_assert(
+        std::is_same<typename MemoryPolicy::refcount,
+                     no_refcount_policy>::value,
+        "gc_atom_impl can only be used when there is no refcount!");
+
+    gc_atom_impl(const gc_atom_impl&) = delete;
+    gc_atom_impl(gc_atom_impl&&) = delete;
+    gc_atom_impl& operator=(const gc_atom_impl&) = delete;
+    gc_atom_impl& operator=(gc_atom_impl&&) = delete;
+
+    gc_atom_impl(box_type b)
+        : impl_{b.impl_}
+    {}
+
+    box_type load() const
+    { return {impl_.load()}; }
+
+    void store(box_type b)
+    { impl_.store(b.impl_); }
+
+    box_type exchange(box_type b)
+    { return {impl_.exchange(b.impl_)}; }
+
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    {
+        while (true) {
+            auto oldv = box_type{impl_.load()};
+            auto newv = oldv.update(fn);
+            if (impl_.compare_exchange_weak(oldv.impl_, newv.impl_))
+                return { newv };
+        }
+    }
+
+private:
+    std::atomic<typename box_type::holder*> impl_;
+};
+
+} // namespace detail
+
+/*!
+ * Stores for boxed values of type `T` in a thread-safe manner.
+ *
+ * @see box
+ *
+ * @rst
+ *
+ * .. warning:: If memory policy used includes thread unsafe reference counting,
+ *    no no thread safety is assumed, and the atom becomes thread unsafe too!
+ *
+ * .. note:: ``box<T>`` provides a value based box of type ``T``, this is, we can
+ *    think about it as a value-based version of ``std::shared_ptr``.  In a
+ *    similar fashion, ``atom<T>`` is in spirit the value-based equivalent of
+ *    C++20 ``std::atomic_shared_ptr``.  However, the API does not follow
+ *    ``std::atomic`` interface closely, since it attempts to be a higher level
+ *    construction, most similar to Clojure's ``(atom)``.  It is remarkable in
+ *    particular that, since ``box<T>`` underlying object is immutable, using
+ *    ``atom<T>`` is fully thread-safe in ways that ``std::atmic_shared_ptr`` is
+ *    not. This is so because dereferencing the underlying pointer in a
+ *    ``std::atomic_share_ptr`` may require further synchronization, in particular
+ *    when invoking non-const methods.
+ *
+ * @endrst
+ */
+template <typename T,
+          typename MemoryPolicy = default_memory_policy>
+class atom
+{
+public:
+    using box_type = box<T, MemoryPolicy>;
+    using value_type = T;
+    using memory_policy = MemoryPolicy;
+
+    atom(const atom&) = delete;
+    atom(atom&&) = delete;
+    void operator=(const atom&) = delete;
+    void operator=(atom&&) = delete;
+
+    /*!
+     * Constructs an atom holding a value `b`;
+     */
+    atom(box_type v={})
+        : impl_{std::move(v)}
+    {}
+
+    /*!
+     * Sets a new value in the atom.
+     */
+    atom& operator=(box_type b)
+    {
+        impl_.store(std::move(b));
+        return *this;
+    }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    operator box_type() const
+    { return impl_.load(); }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    operator value_type() const
+    { return *impl_.load(); }
+
+    /*!
+     * Reads the currently stored value in a thread-safe manner.
+     */
+    box_type load() const
+    { return impl_.load(); }
+
+    /*!
+     * Stores a new value in a thread-safe manner.
+     */
+    void store(box_type b)
+    { impl_.store(std::move(b)); }
+
+    /*!
+     * Stores a new value and returns the old value, in a thread-safe manner.
+     */
+    box_type exchange(box_type b)
+    { return impl_.exchange(std::move(b)); }
+
+    /*!
+     * Stores the result of applying `fn` to the current value atomically and
+     * returns the new resulting value.
+     *
+     * @rst
+     *
+     * .. warning:: ``fn`` must be a pure function and have no side effects! The
+     *    function might be evaluated multiple times when multiple threads
+     *    content to update the value.
+     *
+     * @endrst
+     */
+    template <typename Fn>
+    box_type update(Fn&& fn)
+    { return impl_.update(std::forward<Fn>(fn)); }
+
+private:
+    struct get_refcount_atom_impl
+    {
+        template <typename U, typename MP>
+        struct apply
+        {
+            using type = detail::refcount_atom_impl<U, MP>;
+        };
+    };
+
+    struct get_gc_atom_impl
+    {
+        template <typename U, typename MP>
+        struct apply
+        {
+            using type = detail::gc_atom_impl<U, MP>;
+        };
+    };
+
+    // If we are using "real" garbage collection (we assume this when we use
+    // `no_refcount_policy`), we just store the pointer in an atomic.  If we use
+    // reference counting, we rely on the reference counting spinlock.
+    using impl_t = typename std::conditional_t<
+        std::is_same<typename MemoryPolicy::refcount, no_refcount_policy>::value,
+        get_gc_atom_impl,
+        get_refcount_atom_impl
+    >::template apply<T, MemoryPolicy>::type;
+
+    impl_t impl_;
+};
+
+}
diff --git a/src/immer/box.hpp b/src/immer/box.hpp
index 2d9f159db474..b8ad19e1a499 100644
--- a/src/immer/box.hpp
+++ b/src/immer/box.hpp
@@ -13,6 +13,16 @@
 
 namespace immer {
 
+namespace detail {
+
+template <typename U, typename MP>
+struct gc_atom_impl;
+
+template <typename U, typename MP>
+struct refcount_atom_impl;
+
+} // namespace detail
+
 /*!
  * Immutable box for a single value of type `T`.
  *
@@ -21,9 +31,12 @@ namespace immer {
  * moving just copy the underlying pointers.
  */
 template <typename T,
-          typename MemoryPolicy   = default_memory_policy>
+          typename MemoryPolicy = default_memory_policy>
 class box
 {
+    friend struct detail::gc_atom_impl<T, MemoryPolicy>;
+    friend struct detail::refcount_atom_impl<T, MemoryPolicy>;
+
     struct holder : MemoryPolicy::refcount
     {
         T value;
diff --git a/src/immer/config.hpp b/src/immer/config.hpp
index f3f19f1ad584..67697ef36b07 100644
--- a/src/immer/config.hpp
+++ b/src/immer/config.hpp
@@ -35,12 +35,20 @@
 #define IMMER_TRACE_E(expr)                             \
     IMMER_TRACE("    " << #expr << " = " << (expr))
 
+#if defined(_MSC_VER)
+#define IMMER_UNREACHABLE    __assume(false)
+#define IMMER_LIKELY(cond)   cond
+#define IMMER_UNLIKELY(cond) cond
+#define IMMER_FORCEINLINE    __forceinline
+#define IMMER_PREFETCH(p)
+#else
 #define IMMER_UNREACHABLE    __builtin_unreachable()
 #define IMMER_LIKELY(cond)   __builtin_expect(!!(cond), 1)
 #define IMMER_UNLIKELY(cond) __builtin_expect(!!(cond), 0)
-// #define IMMER_PREFETCH(p)    __builtin_prefetch(p)
-#define IMMER_PREFETCH(p)
 #define IMMER_FORCEINLINE    inline __attribute__ ((always_inline))
+#define IMMER_PREFETCH(p)
+// #define IMMER_PREFETCH(p)    __builtin_prefetch(p)
+#endif
 
 #define IMMER_DESCENT_DEEP 0
 
diff --git a/src/immer/detail/arrays/no_capacity.hpp b/src/immer/detail/arrays/no_capacity.hpp
index 381750cc1142..33135d0cc37e 100644
--- a/src/immer/detail/arrays/no_capacity.hpp
+++ b/src/immer/detail/arrays/no_capacity.hpp
@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <immer/algorithm.hpp>
 #include <immer/detail/arrays/node.hpp>
 
 namespace immer {
@@ -90,10 +91,13 @@ struct no_capacity
     T* data() { return ptr->data(); }
     const T* data() const { return ptr->data(); }
 
-    template <typename Iter>
-    static no_capacity from_range(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <is_forward_iterator_v<Iter> 
+	       && compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static no_capacity from_range(Iter first, Sent last)
     {
-        auto count = static_cast<size_t>(std::distance(first, last));
+        auto count = static_cast<size_t>(distance(first, last));
         return {
             node_t::copy_n(count, first, last),
             count,
diff --git a/src/immer/detail/arrays/node.hpp b/src/immer/detail/arrays/node.hpp
index 5721f5779d30..7883d167f2f4 100644
--- a/src/immer/detail/arrays/node.hpp
+++ b/src/immer/detail/arrays/node.hpp
@@ -9,6 +9,7 @@
 #pragma once
 
 #include <immer/detail/util.hpp>
+#include <immer/detail/type_traits.hpp>
 #include <immer/detail/combine_standard_layout.hpp>
 
 #include <limits>
@@ -92,12 +93,14 @@ struct node
         }
     }
 
-    template <typename Iter>
-    static node_t* copy_n(size_t n, Iter first, Iter last)
+    template <typename Iter, typename Sent,
+            std::enable_if_t
+            <detail::compatible_sentinel_v<Iter,Sent>, bool> = true>
+    static node_t* copy_n(size_t n, Iter first, Sent last)
     {
         auto p = make_n(n);
         try {
-            std::uninitialized_copy(first, last, p->data());
+            uninitialized_copy(first, last, p->data());
             return p;
         } catch (...) {
             heap::deallocate(sizeof_n(n), p);
diff --git a/src/immer/detail/arrays/with_capacity.hpp b/src/immer/detail/arrays/with_capacity.hpp
index 79f97ad4c95c..d408b38aa3a5 100644
--- a/src/immer/detail/arrays/with_capacity.hpp
+++ b/src/immer/detail/arrays/with_capacity.hpp
@@ -111,10 +111,13 @@ struct with_capacity
         }
     }
 
-    template <typename Iter>
-    static with_capacity from_range(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <is_forward_iterator_v<Iter>
+               && compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static with_capacity from_range(Iter first, Sent last)
     {
-        auto count = static_cast<size_t>(std::distance(first, last));
+        auto count = static_cast<size_t>(distance(first, last));
         return {
             node_t::copy_n(count, first, last),
             count,
diff --git a/src/immer/detail/hamts/bits.hpp b/src/immer/detail/hamts/bits.hpp
index a308b1517255..92c7e45cf2ef 100644
--- a/src/immer/detail/hamts/bits.hpp
+++ b/src/immer/detail/hamts/bits.hpp
@@ -10,43 +10,89 @@
 
 #include <cstdint>
 
+#if defined(_MSC_VER)
+#include <intrin.h> // __popcnt
+#endif
+
 namespace immer {
 namespace detail {
 namespace hamts {
 
+using size_t   = std::size_t;
+using hash_t   = std::size_t;
 using bits_t   = std::uint32_t;
-using bitmap_t = std::uint32_t;
 using count_t  = std::uint32_t;
 using shift_t  = std::uint32_t;
-using size_t   = std::size_t;
-using hash_t   = std::size_t;
+
+template <bits_t B>
+struct get_bitmap_type
+{
+    static_assert(B < 6u, "B > 6 is not supported.");
+
+    using type = std::uint32_t;
+};
+
+template <>
+struct get_bitmap_type<6u>
+{
+    using type = std::uint64_t;
+};
 
 template <bits_t B, typename T=count_t>
-constexpr T branches = T{1} << B;
+constexpr T branches = T{1u} << B;
 
 template <bits_t B, typename T=size_t>
-constexpr T mask = branches<B, T> - 1;
+constexpr T mask = branches<B, T> - 1u;
 
 template <bits_t B, typename T=count_t>
-constexpr T max_depth = (sizeof(hash_t) * 8 + B - 1) / B;
+constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B;
 
 template <bits_t B, typename T=count_t>
 constexpr T max_shift = max_depth<B, count_t> * B;
 
 #define IMMER_HAS_BUILTIN_POPCOUNT 1
 
-inline count_t popcount(bitmap_t x)
+inline auto popcount_fallback(std::uint32_t x)
 {
-#if IMMER_HAS_BUILTIN_POPCOUNT
-    return __builtin_popcount(x);
-#else
     // More alternatives:
     // https://en.wikipedia.org/wiki/Hamming_weight
     // http://wm.ite.pl/articles/sse-popcount.html
     // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
-    x = x - ((x >> 1) & 0x55555555);
-    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
-    return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
+    x = x - ((x >> 1) & 0x55555555u);
+    x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u);
+    return (((x + (x >> 4u)) & 0xF0F0F0Fu) * 0x1010101u) >> 24u;
+}
+
+inline auto popcount_fallback(std::uint64_t x)
+{
+    x = x - ((x >> 1) & 0x5555555555555555u);
+    x = (x & 0x3333333333333333u) + ((x >> 2u) & 0x3333333333333333u);
+    return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56u;
+}
+
+inline count_t popcount(std::uint32_t x)
+{
+#if IMMER_HAS_BUILTIN_POPCOUNT
+#  if defined(_MSC_VER)
+    return  __popcnt(x);
+#  else
+    return __builtin_popcount(x);
+#  endif
+#else
+    return popcount_fallback(x);
+#endif
+}
+
+inline count_t popcount(std::uint64_t x)
+{
+#if IMMER_HAS_BUILTIN_POPCOUNT
+#  if defined(_MSC_VER)
+    return  __popcnt64(x);
+#  else
+    return __builtin_popcountll(x);
+#  endif
+#else
+    return popcount_fallback(x);
 #endif
 }
 
diff --git a/src/immer/detail/hamts/champ.hpp b/src/immer/detail/hamts/champ.hpp
index 11c3435c145b..d3687ffbee18 100644
--- a/src/immer/detail/hamts/champ.hpp
+++ b/src/immer/detail/hamts/champ.hpp
@@ -24,11 +24,12 @@ template <typename T,
           bits_t B>
 struct champ
 {
-    static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
-
     static constexpr auto bits = B;
 
     using node_t = node<T, Hash, Equal, MemoryPolicy, B>;
+    using bitmap_t = typename get_bitmap_type<B>::type;
+
+    static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
 
     node_t* root;
     size_t  size;
@@ -126,7 +127,7 @@ struct champ
         auto node = root;
         auto hash = Hash{}(k);
         for (auto i = count_t{}; i < max_depth<B>; ++i) {
-            auto bit = 1 << (hash & mask<B>);
+            auto bit = bitmap_t{1u} << (hash & mask<B>);
             if (node->nodemap() & bit) {
                 auto offset = popcount(node->nodemap() & (bit - 1));
                 node = node->children() [offset];
@@ -168,7 +169,7 @@ struct champ
             };
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
-            auto bit = 1 << idx;
+            auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
                 auto offset = popcount(node->nodemap() & (bit - 1));
                 auto result = do_add(node->children() [offset],
@@ -250,7 +251,7 @@ struct champ
             };
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
-            auto bit = 1 << idx;
+            auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
                 auto offset = popcount(node->nodemap() & (bit - 1));
                 auto result = do_update<Project, Default, Combine>(
@@ -355,7 +356,7 @@ struct champ
             return {};
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
-            auto bit = 1 << idx;
+            auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
                 auto offset = popcount(node->nodemap() & (bit - 1));
                 auto result = do_sub(node->children() [offset],
@@ -444,7 +445,7 @@ struct champ
                 if (!equals_tree<Eq>(a->children()[i], b->children()[i], depth + 1))
                     return false;
             auto nv = popcount(a->datamap());
-            return equals_values<Eq>(a->values(), b->values(), nv);
+            return !nv || equals_values<Eq>(a->values(), b->values(), nv);
         }
     }
 
diff --git a/src/immer/detail/hamts/champ_iterator.hpp b/src/immer/detail/hamts/champ_iterator.hpp
index a0f101bfaf96..07d552dacab2 100644
--- a/src/immer/detail/hamts/champ_iterator.hpp
+++ b/src/immer/detail/hamts/champ_iterator.hpp
@@ -20,7 +20,9 @@ struct champ_iterator
     : iterator_facade<champ_iterator<T, Hash, Eq, MP, B>,
                       std::forward_iterator_tag,
                       T,
-                      const T&>
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
 {
     using tree_t = champ<T, Hash, Eq, MP, B>;
     using node_t = typename tree_t::node_t;
@@ -30,10 +32,14 @@ struct champ_iterator
     champ_iterator() = default;
 
     champ_iterator(const tree_t& v)
-        : cur_   { v.root->values()  }
-        , end_   { v.root->values() + popcount(v.root->datamap()) }
-        , depth_ { 0 }
+        : depth_ { 0 }
     {
+        if (v.root->datamap()) {
+            cur_ = v.root->values();
+            end_ = v.root->values() + popcount(v.root->datamap());
+        } else {
+            cur_ = end_ = nullptr;
+        }
         path_[0] = &v.root;
         ensure_valid_();
     }
@@ -77,8 +83,10 @@ struct champ_iterator
                 path_[depth_] = parent->children();
                 auto child = *path_[depth_];
                 if (depth_ < max_depth<B>) {
-                    cur_ = child->values();
-                    end_ = cur_ + popcount(child->datamap());
+                    if (child->datamap()) {
+                        cur_ = child->values();
+                        end_ = cur_ + popcount(child->datamap());
+                    }
                 } else {
                     cur_ = child->collisions();
                     end_ = cur_ + child->collision_count();
@@ -99,8 +107,10 @@ struct champ_iterator
                 path_[depth_] = next;
                 auto child = *path_[depth_];
                 if (depth_ < max_depth<B>) {
-                    cur_ = child->values();
-                    end_ = cur_ + popcount(child->datamap());
+                    if (child->datamap()) {
+                        cur_ = child->values();
+                        end_ = cur_ + popcount(child->datamap());
+                    }
                 } else {
                     cur_ = child->collisions();
                     end_ = cur_ + child->collision_count();
diff --git a/src/immer/detail/hamts/node.hpp b/src/immer/detail/hamts/node.hpp
index cfe7d195fd29..61aa381336f0 100644
--- a/src/immer/detail/hamts/node.hpp
+++ b/src/immer/detail/hamts/node.hpp
@@ -41,6 +41,7 @@ struct node
     using ownee_t     = typename transience::ownee;
     using edit_t      = typename transience::edit;
     using value_t     = T;
+    using bitmap_t    = typename get_bitmap_type<B>::type;
 
     enum class kind_t
     {
@@ -117,12 +118,14 @@ struct node
     auto values()
     {
         assert(kind() == kind_t::inner);
+        assert(impl.d.data.inner.values);
         return (T*) &impl.d.data.inner.values->d.buffer;
     }
 
     auto values() const
     {
         assert(kind() == kind_t::inner);
+        assert(impl.d.data.inner.values);
         return (const T*) &impl.d.data.inner.values->d.buffer;
     }
 
@@ -220,7 +223,7 @@ struct node
     {
         assert(n >= 1);
         auto p = make_inner_n(n);
-        p->impl.d.data.inner.nodemap = 1 << idx;
+        p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx;
         p->children()[0] = child;
         return p;
     }
@@ -246,7 +249,7 @@ struct node
     {
         assert(idx1 != idx2);
         auto p = make_inner_n(n, 2);
-        p->impl.d.data.inner.datamap = (1 << idx1) | (1 << idx2);
+        p->impl.d.data.inner.datamap = (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
         auto assign = [&] (auto&& x1, auto&& x2) {
             auto vp = p->values();
             try {
@@ -438,21 +441,23 @@ struct node
         auto noffset = popcount(src->nodemap() & (bit - 1));
         dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
         dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
-        try {
-            std::uninitialized_copy(
-                    src->values(), src->values() + voffset,
-                    dst->values());
+        if (nv > 1) {
             try {
                 std::uninitialized_copy(
-                    src->values() + voffset + 1, src->values() + nv,
-                    dst->values() + voffset);
+                    src->values(), src->values() + voffset,
+                    dst->values());
+                try {
+                    std::uninitialized_copy(
+                        src->values() + voffset + 1, src->values() + nv,
+                        dst->values() + voffset);
+                } catch (...) {
+                    destroy_n(dst->values(), voffset);
+                    throw;
+                }
             } catch (...) {
-                destroy_n(dst->values(), voffset);
+                deallocate_inner(dst, n + 1, nv - 1);
                 throw;
             }
-        } catch (...) {
-            deallocate_inner(dst, n + 1, nv - 1);
-            throw;
         }
         inc_nodes(src->children(), n);
         std::uninitialized_copy(
@@ -479,15 +484,17 @@ struct node
         dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
         dst->impl.d.data.inner.datamap = src->datamap() | bit;
         try {
-            std::uninitialized_copy(
-                src->values(), src->values() + voffset,
-                dst->values());
+            if (nv)
+                std::uninitialized_copy(
+                    src->values(), src->values() + voffset,
+                    dst->values());
             try {
                 new (dst->values() + voffset) T{std::move(value)};
                 try {
-                    std::uninitialized_copy(
-                        src->values() + voffset, src->values() + nv,
-                        dst->values() + voffset + 1);
+                    if (nv)
+                        std::uninitialized_copy(
+                            src->values() + voffset, src->values() + nv,
+                            dst->values() + voffset + 1);
                 } catch (...) {
                     dst->values()[voffset].~T();
                     throw;
@@ -523,21 +530,23 @@ struct node
         auto dst     = make_inner_n(n, nv - 1);
         dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
         dst->impl.d.data.inner.nodemap = src->nodemap();
-        try {
-            std::uninitialized_copy(
-                src->values(), src->values() + voffset,
-                dst->values());
+        if (nv > 1) {
             try {
                 std::uninitialized_copy(
-                    src->values() + voffset + 1, src->values() + nv,
-                    dst->values() + voffset);
+                    src->values(), src->values() + voffset,
+                    dst->values());
+                try {
+                    std::uninitialized_copy(
+                        src->values() + voffset + 1, src->values() + nv,
+                        dst->values() + voffset);
+                } catch (...) {
+                    destroy_n(dst->values(), voffset);
+                    throw;
+                }
             } catch (...) {
-                destroy_n(dst->values(), voffset);
+                deallocate_inner(dst, n, nv - 1);
                 throw;
             }
-        } catch (...) {
-            deallocate_inner(dst, n, nv - 1);
-            throw;
         }
         inc_nodes(src->children(), n);
         std::uninitialized_copy(
@@ -555,14 +564,16 @@ struct node
         dst->impl.d.data.inner.datamap = src->datamap() | bit;
         dst->impl.d.data.inner.nodemap = src->nodemap();
         try {
-            std::uninitialized_copy(
-                src->values(), src->values() + offset, dst->values());
+            if (nv)
+                std::uninitialized_copy(
+                    src->values(), src->values() + offset, dst->values());
             try {
                 new (dst->values() + offset) T{std::move(v)};
                 try {
-                    std::uninitialized_copy(
-                        src->values() + offset, src->values() + nv,
-                        dst->values() + offset + 1);
+                    if (nv)
+                        std::uninitialized_copy(
+                            src->values() + offset, src->values() + nv,
+                            dst->values() + offset + 1);
                 } catch (...) {
                     dst->values()[offset].~T();
                     throw;
@@ -632,7 +643,6 @@ struct node
     static void delete_values(values_t* p, count_t n)
     {
         assert(p);
-        destroy_n(&p->d.buffer, n);
         deallocate_values(p, n);
     }
 
@@ -651,7 +661,6 @@ struct node
         assert(p);
         assert(p->kind() == kind_t::collision);
         auto n = p->collision_count();
-        destroy_n(p->collisions(), n);
         deallocate_collision(p, n);
     }
 
@@ -702,7 +711,8 @@ struct node
 
     static void deallocate_inner(node_t* p, count_t n, count_t nv)
     {
-        deallocate_values(p->impl.d.data.inner.values, nv);
+        assert(nv);
+        heap::deallocate(node_t::sizeof_values_n(nv), p->impl.d.data.inner.values);
         heap::deallocate(node_t::sizeof_inner_n(n), p);
     }
 };
diff --git a/src/immer/detail/rbts/operations.hpp b/src/immer/detail/rbts/operations.hpp
index 3d54e92fa40b..17ec9911ef1c 100644
--- a/src/immer/detail/rbts/operations.hpp
+++ b/src/immer/detail/rbts/operations.hpp
@@ -24,86 +24,86 @@ namespace detail {
 namespace rbts {
 
 template <typename T>
-struct array_for_visitor
+struct array_for_visitor : visitor_base<array_for_visitor<T>>
 {
     using this_t = array_for_visitor;
 
     template <typename PosT>
-    friend T* visit_inner(this_t, PosT&& pos, size_t idx)
+    static T* visit_inner(PosT&& pos, size_t idx)
     { return pos.descend(this_t{}, idx); }
 
     template <typename PosT>
-    friend T* visit_leaf(this_t, PosT&& pos, size_t)
+    static T* visit_leaf(PosT&& pos, size_t)
     { return pos.node()->leaf(); }
 };
 
 template <typename T>
-struct region_for_visitor
+struct region_for_visitor : visitor_base<region_for_visitor<T>>
 {
     using this_t = region_for_visitor;
     using result_t = std::tuple<T*, size_t, size_t>;
 
     template <typename PosT>
-    friend result_t visit_inner(this_t, PosT&& pos, size_t idx)
+    static result_t visit_inner(PosT&& pos, size_t idx)
     { return pos.towards(this_t{}, idx); }
 
     template <typename PosT>
-    friend result_t visit_leaf(this_t, PosT&& pos, size_t idx)
+    static result_t visit_leaf(PosT&& pos, size_t idx)
     { return { pos.node()->leaf(), pos.index(idx), pos.count() }; }
 };
 
 template <typename T>
-struct get_visitor
+struct get_visitor : visitor_base<get_visitor<T>>
 {
     using this_t = get_visitor;
 
     template <typename PosT>
-    friend const T& visit_inner(this_t, PosT&& pos, size_t idx)
+    static const T& visit_inner(PosT&& pos, size_t idx)
     { return pos.descend(this_t{}, idx); }
 
     template <typename PosT>
-    friend const T& visit_leaf(this_t, PosT&& pos, size_t idx)
+    static const T& visit_leaf(PosT&& pos, size_t idx)
     { return pos.node()->leaf() [pos.index(idx)]; }
 };
 
-struct for_each_chunk_visitor
+struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor>
 {
     using this_t = for_each_chunk_visitor;
 
     template <typename Pos, typename Fn>
-    friend void visit_inner(this_t, Pos&& pos, Fn&& fn)
+    static void visit_inner(Pos&& pos, Fn&& fn)
     { pos.each(this_t{}, fn); }
 
     template <typename Pos, typename Fn>
-    friend void visit_leaf(this_t, Pos&& pos, Fn&& fn)
+    static void visit_leaf(Pos&& pos, Fn&& fn)
     {
         auto data = pos.node()->leaf();
         fn(data, data + pos.count());
     }
 };
 
-struct for_each_chunk_p_visitor
+struct for_each_chunk_p_visitor : visitor_base<for_each_chunk_p_visitor>
 {
     using this_t = for_each_chunk_p_visitor;
 
     template <typename Pos, typename Fn>
-    friend bool visit_inner(this_t, Pos&& pos, Fn&& fn)
+    static bool visit_inner(Pos&& pos, Fn&& fn)
     { return pos.each_pred(this_t{}, fn); }
 
     template <typename Pos, typename Fn>
-    friend bool visit_leaf(this_t, Pos&& pos, Fn&& fn)
+    static bool visit_leaf(Pos&& pos, Fn&& fn)
     {
         auto data = pos.node()->leaf();
         return fn(data, data + pos.count());
     }
 };
 
-struct for_each_chunk_left_visitor
+struct for_each_chunk_left_visitor : visitor_base<for_each_chunk_left_visitor>
 {
     using this_t = for_each_chunk_left_visitor;
 
     template <typename Pos, typename Fn>
-    friend void visit_inner(this_t, Pos&& pos,
+    static void visit_inner(Pos&& pos,
                             size_t last, Fn&& fn)
     {
         auto l = pos.index(last);
@@ -112,7 +112,7 @@ struct for_each_chunk_left_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend void visit_leaf(this_t, Pos&& pos,
+    static void visit_leaf(Pos&& pos,
                            size_t last,
                            Fn&& fn)
     {
@@ -122,12 +122,12 @@ struct for_each_chunk_left_visitor
     }
 };
 
-struct for_each_chunk_right_visitor
+struct for_each_chunk_right_visitor : visitor_base<for_each_chunk_right_visitor>
 {
     using this_t = for_each_chunk_right_visitor;
 
     template <typename Pos, typename Fn>
-    friend void visit_inner(this_t, Pos&& pos,
+    static void visit_inner(Pos&& pos,
                             size_t first, Fn&& fn)
     {
         auto f = pos.index(first);
@@ -136,7 +136,7 @@ struct for_each_chunk_right_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend void visit_leaf(this_t, Pos&& pos,
+    static void visit_leaf(Pos&& pos,
                            size_t first,
                            Fn&& fn)
     {
@@ -146,12 +146,12 @@ struct for_each_chunk_right_visitor
     }
 };
 
-struct for_each_chunk_i_visitor
+struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor>
 {
     using this_t = for_each_chunk_i_visitor;
 
     template <typename Pos, typename Fn>
-    friend void visit_relaxed(this_t, Pos&& pos,
+    static void visit_relaxed(Pos&& pos,
                               size_t first, size_t last,
                               Fn&& fn)
     {
@@ -173,7 +173,7 @@ struct for_each_chunk_i_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend void visit_regular(this_t, Pos&& pos,
+    static void visit_regular(Pos&& pos,
                               size_t first, size_t last,
                               Fn&& fn)
     {
@@ -192,7 +192,7 @@ struct for_each_chunk_i_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend void visit_leaf(this_t, Pos&& pos,
+    static void visit_leaf(Pos&& pos,
                            size_t first, size_t last,
                            Fn&& fn)
     {
@@ -206,11 +206,12 @@ struct for_each_chunk_i_visitor
 };
 
 struct for_each_chunk_p_left_visitor
+    : visitor_base<for_each_chunk_p_left_visitor>
 {
     using this_t = for_each_chunk_p_left_visitor;
 
     template <typename Pos, typename Fn>
-    friend bool visit_inner(this_t, Pos&& pos,
+    static bool visit_inner(Pos&& pos,
                             size_t last, Fn&& fn)
     {
         auto l = pos.index(last);
@@ -219,7 +220,7 @@ struct for_each_chunk_p_left_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend bool visit_leaf(this_t, Pos&& pos,
+    static bool visit_leaf(Pos&& pos,
                            size_t last,
                            Fn&& fn)
     {
@@ -230,11 +231,12 @@ struct for_each_chunk_p_left_visitor
 };
 
 struct for_each_chunk_p_right_visitor
+    : visitor_base<for_each_chunk_p_right_visitor>
 {
     using this_t = for_each_chunk_p_right_visitor;
 
     template <typename Pos, typename Fn>
-    friend bool visit_inner(this_t, Pos&& pos,
+    static bool visit_inner(Pos&& pos,
                             size_t first, Fn&& fn)
     {
         auto f = pos.index(first);
@@ -243,7 +245,7 @@ struct for_each_chunk_p_right_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend bool visit_leaf(this_t, Pos&& pos,
+    static bool visit_leaf(Pos&& pos,
                            size_t first,
                            Fn&& fn)
     {
@@ -253,12 +255,12 @@ struct for_each_chunk_p_right_visitor
     }
 };
 
-struct for_each_chunk_p_i_visitor
+struct for_each_chunk_p_i_visitor : visitor_base<for_each_chunk_p_i_visitor>
 {
     using this_t = for_each_chunk_p_i_visitor;
 
     template <typename Pos, typename Fn>
-    friend bool visit_relaxed(this_t, Pos&& pos,
+    static bool visit_relaxed(Pos&& pos,
                               size_t first, size_t last,
                               Fn&& fn)
     {
@@ -281,7 +283,7 @@ struct for_each_chunk_p_i_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend bool visit_regular(this_t, Pos&& pos,
+    static bool visit_regular(Pos&& pos,
                               size_t first, size_t last,
                               Fn&& fn)
     {
@@ -301,7 +303,7 @@ struct for_each_chunk_p_i_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend bool visit_leaf(this_t, Pos&& pos,
+    static bool visit_leaf(Pos&& pos,
                            size_t first, size_t last,
                            Fn&& fn)
     {
@@ -315,29 +317,29 @@ struct for_each_chunk_p_i_visitor
     }
 };
 
-struct equals_visitor
+struct equals_visitor : visitor_base<equals_visitor>
 {
     using this_t = equals_visitor;
 
-    struct this_aux_t
+    struct this_aux_t : visitor_base<this_aux_t>
     {
         template <typename PosR, typename PosL,typename Iter>
-        friend bool visit_inner(this_aux_t, PosR&& posr,
-                               count_t i, PosL&& posl,
-                               Iter&& first, size_t idx)
+        static bool visit_inner(PosR&& posr,
+                                count_t i, PosL&& posl,
+                                Iter&& first, size_t idx)
         { return posl.nth_sub(i, this_t{}, posr, first, idx); }
 
         template <typename PosR, typename PosL,typename Iter>
-        friend bool visit_leaf(this_aux_t, PosR&& posr,
+        static bool visit_leaf(PosR&& posr,
                                count_t i, PosL&& posl,
                                Iter&& first, size_t idx)
         { return posl.nth_sub_leaf(i, this_t{}, posr, first, idx); }
     };
 
-    struct rrb
+    struct rrb : visitor_base<rrb>
     {
         template <typename PosR, typename Iter, typename Node>
-        friend bool visit_node(rrb, PosR&& posr, Iter&& first,
+        static bool visit_node(PosR&& posr, Iter&& first,
                                Node* rootl, shift_t shiftl, size_t sizel)
         {
             assert(shiftl <= posr.shift());
@@ -364,7 +366,7 @@ struct equals_visitor
     }
 
     template <typename PosL, typename PosR, typename Iter>
-    friend bool visit_relaxed(this_t, PosL&& posl, PosR&& posr,
+    static bool visit_relaxed(PosL&& posl, PosR&& posr,
                               Iter&& first, size_t idx)
     {
         auto nl = posl.node();
@@ -390,23 +392,23 @@ struct equals_visitor
     }
 
     template <typename PosL, typename PosR, typename Iter>
-    friend std::enable_if_t<is_relaxed_v<PosR>, bool>
-    visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    static std::enable_if_t<is_relaxed_v<PosR>, bool>
+    visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
     {
-        return visit_relaxed(this_t{}, posl, posr, first, idx);
+        return this_t::visit_relaxed(posl, posr, first, idx);
     }
 
     template <typename PosL, typename PosR, typename Iter>
-    friend std::enable_if_t<!is_relaxed_v<PosR>, bool>
-    visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
+    static std::enable_if_t<!is_relaxed_v<PosR>, bool>
+    visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
     {
         return posl.count() >= posr.count()
-            ? visit_regular(this_t{}, posl, posr.node())
-            : visit_regular(this_t{}, posr, posl.node());
+            ? this_t::visit_regular(posl, posr.node())
+            : this_t::visit_regular(posr, posl.node());
     }
 
     template <typename PosL, typename PosR, typename Iter>
-    friend bool visit_leaf(this_t, PosL&& posl,
+    static bool visit_leaf(PosL&& posl,
                            PosR&& posr, Iter&& first, size_t idx)
     {
         if (posl.node() == posr.node())
@@ -424,7 +426,7 @@ struct equals_visitor
     }
 
     template <typename Pos, typename NodeT>
-    friend bool visit_regular(this_t, Pos&& pos, NodeT* other)
+    static bool visit_regular(Pos&& pos, NodeT* other)
     {
         auto node = pos.node();
         return node == other
@@ -432,7 +434,7 @@ struct equals_visitor
     }
 
     template <typename Pos, typename NodeT>
-    friend bool visit_leaf(this_t, Pos&& pos, NodeT* other)
+    static bool visit_leaf(Pos&& pos, NodeT* other)
     {
         auto node = pos.node();
         return node == other
@@ -442,13 +444,13 @@ struct equals_visitor
 };
 
 template <typename NodeT>
-struct update_visitor
+struct update_visitor : visitor_base<update_visitor<NodeT>>
 {
     using node_t = NodeT;
     using this_t = update_visitor;
 
     template <typename Pos, typename Fn>
-    friend node_t* visit_relaxed(this_t, Pos&& pos, size_t idx, Fn&& fn)
+    static node_t* visit_relaxed(Pos&& pos, size_t idx, Fn&& fn)
     {
         auto offset  = pos.index(idx);
         auto count   = pos.count();
@@ -466,7 +468,7 @@ struct update_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend node_t* visit_regular(this_t, Pos&& pos, size_t idx, Fn&& fn)
+    static node_t* visit_regular(Pos&& pos, size_t idx, Fn&& fn)
     {
         auto offset  = pos.index(idx);
         auto count   = pos.count();
@@ -484,7 +486,7 @@ struct update_visitor
     }
 
     template <typename Pos, typename Fn>
-    friend node_t* visit_leaf(this_t, Pos&& pos, size_t idx, Fn&& fn)
+    static node_t* visit_leaf(Pos&& pos, size_t idx, Fn&& fn)
     {
         auto offset  = pos.index(idx);
         auto node    = node_t::copy_leaf(pos.node(), pos.count());
@@ -499,12 +501,12 @@ struct update_visitor
     }
 };
 
-struct dec_visitor
+struct dec_visitor : visitor_base<dec_visitor>
 {
     using this_t = dec_visitor;
 
     template <typename Pos>
-    friend void visit_relaxed(this_t, Pos&& p)
+    static void visit_relaxed(Pos&& p)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -515,7 +517,7 @@ struct dec_visitor
     }
 
     template <typename Pos>
-    friend void visit_regular(this_t, Pos&& p)
+    static void visit_regular(Pos&& p)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -526,7 +528,7 @@ struct dec_visitor
     }
 
     template <typename Pos>
-    friend void visit_leaf(this_t, Pos&& p)
+    static void visit_leaf(Pos&& p)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -567,7 +569,7 @@ void dec_empty_regular(NodeT* node)
 }
 
 template <typename NodeT>
-struct get_mut_visitor
+struct get_mut_visitor : visitor_base<get_mut_visitor<NodeT>>
 {
     using node_t  = NodeT;
     using this_t  = get_mut_visitor;
@@ -575,7 +577,7 @@ struct get_mut_visitor
     using edit_t  = typename NodeT::edit_t;
 
     template <typename Pos>
-    friend value_t& visit_relaxed(this_t, Pos&& pos, size_t idx,
+    static value_t& visit_relaxed(Pos&& pos, size_t idx,
                                   edit_t e, node_t** location)
     {
         auto offset  = pos.index(idx);
@@ -600,7 +602,7 @@ struct get_mut_visitor
     }
 
     template <typename Pos>
-    friend value_t& visit_regular(this_t, Pos&& pos, size_t idx,
+    static value_t& visit_regular(Pos&& pos, size_t idx,
                                   edit_t e, node_t** location)
     {
         assert(pos.node() == *location);
@@ -626,7 +628,7 @@ struct get_mut_visitor
     }
 
     template <typename Pos>
-    friend value_t& visit_leaf(this_t, Pos&& pos, size_t idx,
+    static value_t& visit_leaf(Pos&& pos, size_t idx,
                                edit_t e, node_t** location)
     {
         assert(pos.node() == *location);
@@ -644,6 +646,7 @@ struct get_mut_visitor
 
 template <typename NodeT, bool Mutating = true>
 struct push_tail_mut_visitor
+    : visitor_base<push_tail_mut_visitor<NodeT, Mutating>>
 {
     static constexpr auto B  = NodeT::bits;
     static constexpr auto BL = NodeT::bits_leaf;
@@ -654,7 +657,7 @@ struct push_tail_mut_visitor
     using edit_t = typename NodeT::edit_t;
 
     template <typename Pos>
-    friend node_t* visit_relaxed(this_t, Pos&& pos, edit_t e, node_t* tail, count_t ts)
+    static node_t* visit_relaxed(Pos&& pos, edit_t e, node_t* tail, count_t ts)
     {
         auto node        = pos.node();
         auto level       = pos.shift();
@@ -710,7 +713,7 @@ struct push_tail_mut_visitor
     }
 
     template <typename Pos, typename... Args>
-    friend node_t* visit_regular(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...)
+    static node_t* visit_regular(Pos&& pos, edit_t e, node_t* tail, Args&&...)
     {
         assert((pos.size() & mask<BL>) == 0);
         auto node        = pos.node();
@@ -739,12 +742,12 @@ struct push_tail_mut_visitor
     }
 
     template <typename Pos, typename... Args>
-    friend node_t* visit_leaf(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...)
+    static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...)
     { IMMER_UNREACHABLE; }
 };
 
 template <typename NodeT>
-struct push_tail_visitor
+struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>>
 {
     static constexpr auto B  = NodeT::bits;
     static constexpr auto BL = NodeT::bits_leaf;
@@ -753,7 +756,7 @@ struct push_tail_visitor
     using node_t = NodeT;
 
     template <typename Pos>
-    friend node_t* visit_relaxed(this_t, Pos&& pos, node_t* tail, count_t ts)
+    static node_t* visit_relaxed(Pos&& pos, node_t* tail, count_t ts)
     {
         auto level       = pos.shift();
         auto idx         = pos.count() - 1;
@@ -793,7 +796,7 @@ struct push_tail_visitor
     }
 
     template <typename Pos, typename... Args>
-    friend node_t* visit_regular(this_t, Pos&& pos, node_t* tail, Args&&...)
+    static node_t* visit_regular(Pos&& pos, node_t* tail, Args&&...)
     {
         assert((pos.size() & mask<BL>) == 0);
         auto idx         = pos.index(pos.size() - 1);
@@ -812,17 +815,17 @@ struct push_tail_visitor
     }
 
     template <typename Pos, typename... Args>
-    friend node_t* visit_leaf(this_t, Pos&& pos, node_t* tail, Args&&...)
+    static node_t* visit_leaf(Pos&& pos, node_t* tail, Args&&...)
     { IMMER_UNREACHABLE; }
 };
 
-struct dec_right_visitor
+struct dec_right_visitor : visitor_base<dec_right_visitor>
 {
     using this_t = dec_right_visitor;
     using dec_t  = dec_visitor;
 
     template <typename Pos>
-    friend void visit_relaxed(this_t, Pos&& p, count_t idx)
+    static void visit_relaxed(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -833,7 +836,7 @@ struct dec_right_visitor
     }
 
     template <typename Pos>
-    friend void visit_regular(this_t, Pos&& p, count_t idx)
+    static void visit_regular(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -844,12 +847,13 @@ struct dec_right_visitor
     }
 
     template <typename Pos>
-    friend void visit_leaf(this_t, Pos&& p, count_t idx)
+    static void visit_leaf(Pos&& p, count_t idx)
     { IMMER_UNREACHABLE; }
 };
 
 template <typename NodeT, bool Collapse=true, bool Mutating=true>
 struct slice_right_mut_visitor
+    : visitor_base<slice_right_mut_visitor<NodeT, Collapse, Mutating>>
 {
     using node_t = NodeT;
     using this_t = slice_right_mut_visitor;
@@ -865,7 +869,7 @@ struct slice_right_mut_visitor
     static constexpr auto BL = NodeT::bits_leaf;
 
     template <typename PosT>
-    friend result_t visit_relaxed(this_t, PosT&& pos, size_t last, edit_t e)
+    static result_t visit_relaxed(PosT&& pos, size_t last, edit_t e)
     {
         auto idx = pos.index(last);
         auto node = pos.node();
@@ -934,7 +938,7 @@ struct slice_right_mut_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_regular(this_t, PosT&& pos, size_t last, edit_t e)
+    static result_t visit_regular(PosT&& pos, size_t last, edit_t e)
     {
         auto idx = pos.index(last);
         auto node = pos.node();
@@ -995,7 +999,7 @@ struct slice_right_mut_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_leaf(this_t, PosT&& pos, size_t last, edit_t e)
+    static result_t visit_leaf(PosT&& pos, size_t last, edit_t e)
     {
         auto old_tail_size = pos.count();
         auto new_tail_size = pos.index(last) + 1;
@@ -1017,7 +1021,7 @@ struct slice_right_mut_visitor
 };
 
 template <typename NodeT, bool Collapse=true>
-struct slice_right_visitor
+struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>>
 {
     using node_t = NodeT;
     using this_t = slice_right_visitor;
@@ -1030,7 +1034,7 @@ struct slice_right_visitor
     static constexpr auto BL = NodeT::bits_leaf;
 
     template <typename PosT>
-    friend result_t visit_relaxed(this_t, PosT&& pos, size_t last)
+    static result_t visit_relaxed(PosT&& pos, size_t last)
     {
         auto idx = pos.index(last);
         if (Collapse && idx == 0) {
@@ -1070,7 +1074,7 @@ struct slice_right_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_regular(this_t, PosT&& pos, size_t last)
+    static result_t visit_regular(PosT&& pos, size_t last)
     {
         auto idx = pos.index(last);
         if (Collapse && idx == 0) {
@@ -1106,7 +1110,7 @@ struct slice_right_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_leaf(this_t, PosT&& pos, size_t last)
+    static result_t visit_leaf(PosT&& pos, size_t last)
     {
         auto old_tail_size = pos.count();
         auto new_tail_size = pos.index(last) + 1;
@@ -1117,13 +1121,13 @@ struct slice_right_visitor
     }
 };
 
-struct dec_left_visitor
+struct dec_left_visitor : visitor_base<dec_left_visitor>
 {
     using this_t = dec_left_visitor;
     using dec_t  = dec_visitor;
 
     template <typename Pos>
-    friend void visit_relaxed(this_t, Pos&& p, count_t idx)
+    static void visit_relaxed(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -1134,7 +1138,7 @@ struct dec_left_visitor
     }
 
     template <typename Pos>
-    friend void visit_regular(this_t, Pos&& p, count_t idx)
+    static void visit_regular(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
         auto node = p.node();
@@ -1145,12 +1149,13 @@ struct dec_left_visitor
     }
 
     template <typename Pos>
-    friend void visit_leaf(this_t, Pos&& p, count_t idx)
+    static void visit_leaf(Pos&& p, count_t idx)
     { IMMER_UNREACHABLE; }
 };
 
 template <typename NodeT, bool Collapse=true, bool Mutating=true>
 struct slice_left_mut_visitor
+    : visitor_base<slice_left_mut_visitor<NodeT, Collapse, Mutating>>
 {
     using node_t = NodeT;
     using this_t = slice_left_mut_visitor;
@@ -1168,7 +1173,7 @@ struct slice_left_mut_visitor
     static constexpr auto BL = NodeT::bits_leaf;
 
     template <typename PosT>
-    friend result_t visit_relaxed(this_t, PosT&& pos, size_t first, edit_t e)
+    static result_t visit_relaxed(PosT&& pos, size_t first, edit_t e)
     {
         auto idx    = pos.subindex(first);
         auto count  = pos.count();
@@ -1218,7 +1223,7 @@ struct slice_left_mut_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_regular(this_t, PosT&& pos, size_t first, edit_t e)
+    static result_t visit_regular(PosT&& pos, size_t first, edit_t e)
     {
         auto idx    = pos.subindex(first);
         auto count  = pos.count();
@@ -1287,7 +1292,7 @@ struct slice_left_mut_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_leaf(this_t, PosT&& pos, size_t first, edit_t e)
+    static result_t visit_leaf(PosT&& pos, size_t first, edit_t e)
     {
         auto node   = pos.node();
         auto idx    = pos.index(first);
@@ -1310,7 +1315,7 @@ struct slice_left_mut_visitor
 };
 
 template <typename NodeT, bool Collapse=true>
-struct slice_left_visitor
+struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>>
 {
     using node_t = NodeT;
     using this_t = slice_left_visitor;
@@ -1323,7 +1328,7 @@ struct slice_left_visitor
     static constexpr auto BL = NodeT::bits_leaf;
 
     template <typename PosT>
-    friend result_t visit_inner(this_t, PosT&& pos, size_t first)
+    static result_t visit_inner(PosT&& pos, size_t first)
     {
         auto idx    = pos.subindex(first);
         auto count  = pos.count();
@@ -1360,7 +1365,7 @@ struct slice_left_visitor
     }
 
     template <typename PosT>
-    friend result_t visit_leaf(this_t, PosT&& pos, size_t first)
+    static result_t visit_leaf(PosT&& pos, size_t first)
     {
         auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count());
         return { 0, n };
@@ -1587,25 +1592,26 @@ struct concat_merger
     }
 };
 
-struct concat_merger_visitor
+struct concat_merger_visitor : visitor_base<concat_merger_visitor>
 {
     using this_t = concat_merger_visitor;
 
     template <typename Pos, typename Merger>
-    friend void visit_inner(this_t, Pos&& p, Merger& merger)
+    static void visit_inner(Pos&& p, Merger& merger)
     { merger.merge_inner(p); }
 
     template <typename Pos, typename Merger>
-    friend void visit_leaf(this_t, Pos&& p, Merger& merger)
+    static void visit_leaf(Pos&& p, Merger& merger)
     { merger.merge_leaf(p); }
 };
 
 struct concat_rebalance_plan_fill_visitor
+    : visitor_base<concat_rebalance_plan_fill_visitor>
 {
     using this_t = concat_rebalance_plan_fill_visitor;
 
     template <typename Pos, typename Plan>
-    friend void visit_node(this_t, Pos&& p, Plan& plan)
+    static void visit_node(Pos&& p, Plan& plan)
     {
         auto count = p.count();
         assert(plan.n < Plan::max_children);
@@ -1637,8 +1643,10 @@ struct concat_rebalance_plan
     void shuffle(shift_t shift)
     {
         // gcc seems to not really understand this code... :(
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
         constexpr count_t rrb_extras    = 2;
         constexpr count_t rrb_invariant = 1;
         const auto bits     = shift == BL ? BL : B;
@@ -1661,7 +1669,9 @@ struct concat_rebalance_plan
             --n;
             --i;
         }
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic pop
+#endif
     }
 
     template <typename LPos, typename CPos, typename RPos>
@@ -1751,72 +1761,75 @@ concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos)
 }
 
 template <typename Node>
-struct concat_left_visitor
+struct concat_left_visitor : visitor_base<concat_left_visitor<Node>>
 {
     using this_t = concat_left_visitor;
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_pos<Node>
-    visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    static concat_center_pos<Node>
+    visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos)
     { return concat_inners<Node>(lpos, tpos, rpos); }
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_pos<Node>
-    visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    static concat_center_pos<Node>
+    visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos)
     { IMMER_UNREACHABLE; }
 };
 
 template <typename Node>
-struct concat_right_visitor
+struct concat_right_visitor : visitor_base<concat_right_visitor<Node>>
 {
     using this_t = concat_right_visitor;
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_pos<Node>
-    visit_inner(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    static concat_center_pos<Node>
+    visit_inner(RPos&& rpos, LPos&& lpos, TPos&& tpos)
     { return concat_inners<Node>(lpos, tpos, rpos); }
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_pos<Node>
-    visit_leaf(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    static concat_center_pos<Node>
+    visit_leaf(RPos&& rpos, LPos&& lpos, TPos&& tpos)
     { return concat_leafs<Node>(lpos, tpos, rpos); }
 };
 
 template <typename Node>
 struct concat_both_visitor
+    : visitor_base<concat_both_visitor<Node>>
 {
     using this_t = concat_both_visitor;
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_pos<Node>
-    visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    static concat_center_pos<Node>
+    visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos)
     { return rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos); }
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_pos<Node>
-    visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos)
+    static concat_center_pos<Node>
+    visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos)
     { return rpos.first_sub_leaf(concat_right_visitor<Node>{}, lpos, tpos); }
 };
 
 template <typename Node>
 struct concat_trees_right_visitor
+    : visitor_base<concat_trees_right_visitor<Node>>
 {
     using this_t = concat_trees_right_visitor;
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_pos<Node>
-    visit_node(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos)
+    static concat_center_pos<Node>
+    visit_node(RPos&& rpos, LPos&& lpos, TPos&& tpos)
     { return concat_inners<Node>(lpos, tpos, rpos); }
 };
 
 template <typename Node>
 struct concat_trees_left_visitor
+    : visitor_base<concat_trees_left_visitor<Node>>
 {
     using this_t = concat_trees_left_visitor;
 
     template <typename LPos, typename TPos, typename... Args>
-    friend concat_center_pos<Node>
-    visit_node(this_t, LPos&& lpos, TPos&& tpos, Args&& ...args)
+    static concat_center_pos<Node>
+    visit_node(LPos&& lpos, TPos&& tpos, Args&& ...args)
     { return visit_maybe_relaxed_sub(
             args...,
             concat_trees_right_visitor<Node>{},
@@ -2039,17 +2052,17 @@ struct concat_merger_mut
     }
 };
 
-struct concat_merger_mut_visitor
+struct concat_merger_mut_visitor : visitor_base<concat_merger_mut_visitor>
 {
     using this_t = concat_merger_mut_visitor;
 
     template <typename Pos, typename Merger>
-    friend void visit_inner(this_t, Pos&& p,
+    static void visit_inner(Pos&& p,
                             Merger& merger, edit_type<Pos> e)
     { merger.merge_inner(p, e); }
 
     template <typename Pos, typename Merger>
-    friend void visit_leaf(this_t, Pos&& p,
+    static void visit_leaf(Pos&& p,
                            Merger& merger, edit_type<Pos> e)
     { merger.merge_leaf(p, e); }
 };
@@ -2167,22 +2180,22 @@ concat_inners_mut(edit_type<Node> ec,
 }
 
 template <typename Node>
-struct concat_left_mut_visitor
+struct concat_left_mut_visitor : visitor_base<concat_left_mut_visitor<Node>>
 {
     using this_t = concat_left_mut_visitor;
     using edit_t = typename Node::edit_t;
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_mut_pos<Node>
-    visit_inner(this_t, LPos&& lpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_inner(LPos&& lpos, edit_t ec,
                 edit_t el, TPos&& tpos,
                 edit_t er, RPos&& rpos)
     { return concat_inners_mut<Node>(
             ec, el, lpos, tpos, er, rpos); }
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_mut_pos<Node>
-    visit_leaf(this_t, LPos&& lpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_leaf(LPos&& lpos, edit_t ec,
                edit_t el, TPos&& tpos,
                edit_t er, RPos&& rpos)
     { IMMER_UNREACHABLE; }
@@ -2190,21 +2203,22 @@ struct concat_left_mut_visitor
 
 template <typename Node>
 struct concat_right_mut_visitor
+    : visitor_base<concat_right_mut_visitor<Node>>
 {
     using this_t = concat_right_mut_visitor;
     using edit_t = typename Node::edit_t;
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_mut_pos<Node>
-    visit_inner(this_t, RPos&& rpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_inner(RPos&& rpos, edit_t ec,
                 edit_t el, LPos&& lpos, TPos&& tpos,
                 edit_t er)
     { return concat_inners_mut<Node>(
             ec, el, lpos, tpos, er, rpos); }
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_mut_pos<Node>
-    visit_leaf(this_t, RPos&& rpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_leaf(RPos&& rpos, edit_t ec,
                edit_t el, LPos&& lpos, TPos&& tpos,
                edit_t er)
     { return concat_leafs_mut<Node>(
@@ -2213,21 +2227,22 @@ struct concat_right_mut_visitor
 
 template <typename Node>
 struct concat_both_mut_visitor
+    : visitor_base<concat_both_mut_visitor<Node>>
 {
     using this_t = concat_both_mut_visitor;
     using edit_t = typename Node::edit_t;
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_mut_pos<Node>
-    visit_inner(this_t, LPos&& lpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_inner(LPos&& lpos, edit_t ec,
                 edit_t el, TPos&& tpos,
                 edit_t er, RPos&& rpos)
     { return rpos.first_sub(concat_right_mut_visitor<Node>{},
                             ec, el, lpos, tpos, er); }
 
     template <typename LPos, typename TPos, typename RPos>
-    friend concat_center_mut_pos<Node>
-    visit_leaf(this_t, LPos&& lpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_leaf(LPos&& lpos, edit_t ec,
                edit_t el, TPos&& tpos,
                edit_t er, RPos&& rpos)
     { return rpos.first_sub_leaf(concat_right_mut_visitor<Node>{},
@@ -2236,13 +2251,14 @@ struct concat_both_mut_visitor
 
 template <typename Node>
 struct concat_trees_right_mut_visitor
+    : visitor_base<concat_trees_right_mut_visitor<Node>>
 {
     using this_t = concat_trees_right_mut_visitor;
     using edit_t = typename Node::edit_t;
 
     template <typename RPos, typename LPos, typename TPos>
-    friend concat_center_mut_pos<Node>
-    visit_node(this_t, RPos&& rpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_node(RPos&& rpos, edit_t ec,
                edit_t el, LPos&& lpos, TPos&& tpos,
                edit_t er)
     { return concat_inners_mut<Node>(
@@ -2251,13 +2267,14 @@ struct concat_trees_right_mut_visitor
 
 template <typename Node>
 struct concat_trees_left_mut_visitor
+    : visitor_base<concat_trees_left_mut_visitor<Node>>
 {
     using this_t = concat_trees_left_mut_visitor;
     using edit_t = typename Node::edit_t;
 
     template <typename LPos, typename TPos, typename... Args>
-    friend concat_center_mut_pos<Node>
-    visit_node(this_t, LPos&& lpos, edit_t ec,
+    static concat_center_mut_pos<Node>
+    visit_node(LPos&& lpos, edit_t ec,
                edit_t el, TPos&& tpos,
                edit_t er, Args&& ...args)
     { return visit_maybe_relaxed_sub(
diff --git a/src/immer/detail/rbts/position.hpp b/src/immer/detail/rbts/position.hpp
index f4cdf88db68d..4ff579f8f90b 100644
--- a/src/immer/detail/rbts/position.hpp
+++ b/src/immer/detail/rbts/position.hpp
@@ -50,7 +50,7 @@ struct empty_regular_pos
     template <typename Visitor, typename... Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -74,7 +74,7 @@ struct empty_leaf_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_leaf(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -105,7 +105,7 @@ struct leaf_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_leaf(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -137,7 +137,7 @@ struct leaf_sub_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_leaf(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -168,7 +168,7 @@ struct leaf_descent_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_leaf(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -198,7 +198,7 @@ struct full_leaf_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_leaf(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -294,7 +294,7 @@ struct regular_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -703,7 +703,7 @@ struct singleton_regular_sub_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -942,7 +942,7 @@ struct regular_sub_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -971,10 +971,14 @@ struct regular_descent_pos
     node_t* node()  const { return node_; }
     shift_t shift() const { return Shift; }
     count_t index(size_t idx) const {
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshift-count-overflow"
+#endif
         return (idx >> Shift) & mask<B>;
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic pop
+#endif
     }
 
     template <typename Visitor>
@@ -988,7 +992,7 @@ struct regular_descent_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -1013,7 +1017,7 @@ struct regular_descent_pos<NodeT, BL, B, BL>
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -1281,7 +1285,7 @@ struct full_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_regular(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -1668,7 +1672,7 @@ struct relaxed_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_relaxed(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -1726,10 +1730,14 @@ struct relaxed_descent_pos
     count_t index(size_t idx) const
     {
         // make gcc happy
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshift-count-overflow"
+#endif
         auto offset = idx >> Shift;
+#if !defined(_MSC_VER)
 #pragma GCC diagnostic pop
+#endif
         while (relaxed_->d.sizes[offset] <= idx) ++offset;
         return offset;
     }
@@ -1750,7 +1758,7 @@ struct relaxed_descent_pos
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_relaxed(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
 };
 
@@ -1787,7 +1795,7 @@ struct relaxed_descent_pos<NodeT, BL, B, BL>
     template <typename Visitor, typename ...Args>
     decltype(auto) visit(Visitor v, Args&& ...args)
     {
-        return visit_relaxed(v, *this, std::forward<Args>(args)...);
+        return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
 };
 
diff --git a/src/immer/detail/rbts/rbtree.hpp b/src/immer/detail/rbts/rbtree.hpp
index c6b39aa160e3..aab5bd9fe38a 100644
--- a/src/immer/detail/rbts/rbtree.hpp
+++ b/src/immer/detail/rbts/rbtree.hpp
@@ -12,6 +12,8 @@
 #include <immer/detail/rbts/position.hpp>
 #include <immer/detail/rbts/operations.hpp>
 
+#include <immer/detail/type_traits.hpp>
+
 #include <cassert>
 #include <memory>
 #include <numeric>
@@ -56,8 +58,10 @@ struct rbtree
         return result;
     }
 
-    template <typename Iter>
-    static auto from_range(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static auto from_range(Iter first, Sent last)
     {
         auto e = owner_t{};
         auto result = rbtree{empty()};
diff --git a/src/immer/detail/rbts/rbtree_iterator.hpp b/src/immer/detail/rbts/rbtree_iterator.hpp
index f1890dc053a1..672484eae3ea 100644
--- a/src/immer/detail/rbts/rbtree_iterator.hpp
+++ b/src/immer/detail/rbts/rbtree_iterator.hpp
@@ -20,7 +20,9 @@ struct rbtree_iterator
     : iterator_facade<rbtree_iterator<T, MP, B, BL>,
                       std::random_access_iterator_tag,
                       T,
-                      const T&>
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
 {
     using tree_t = rbtree<T, MP, B, BL>;
 
diff --git a/src/immer/detail/rbts/rrbtree.hpp b/src/immer/detail/rbts/rrbtree.hpp
index f18de3528e4d..7d26e50a66f6 100644
--- a/src/immer/detail/rbts/rrbtree.hpp
+++ b/src/immer/detail/rbts/rrbtree.hpp
@@ -13,6 +13,8 @@
 #include <immer/detail/rbts/position.hpp>
 #include <immer/detail/rbts/operations.hpp>
 
+#include <immer/detail/type_traits.hpp>
+
 #include <cassert>
 #include <memory>
 #include <numeric>
@@ -63,8 +65,10 @@ struct rrbtree
         return result;
     }
 
-    template <typename Iter>
-    static auto from_range(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <compatible_sentinel_v<Iter, Sent>, bool> = true>
+    static auto from_range(Iter first, Sent last)
     {
         auto e = owner_t{};
         auto result = rrbtree{empty()};
diff --git a/src/immer/detail/rbts/rrbtree_iterator.hpp b/src/immer/detail/rbts/rrbtree_iterator.hpp
index 36222f976839..a40f7f486de0 100644
--- a/src/immer/detail/rbts/rrbtree_iterator.hpp
+++ b/src/immer/detail/rbts/rrbtree_iterator.hpp
@@ -20,7 +20,9 @@ struct rrbtree_iterator
     : iterator_facade<rrbtree_iterator<T, MP, B, BL>,
                       std::random_access_iterator_tag,
                       T,
-                      const T&>
+                      const T&,
+                      std::ptrdiff_t,
+                      const T*>
 {
     using tree_t   = rrbtree<T, MP, B, BL>;
     using region_t = std::tuple<const T*, size_t, size_t>;
diff --git a/src/immer/detail/rbts/visitor.hpp b/src/immer/detail/rbts/visitor.hpp
index d85f96d865ed..eaccfcdca474 100644
--- a/src/immer/detail/rbts/visitor.hpp
+++ b/src/immer/detail/rbts/visitor.hpp
@@ -8,6 +8,8 @@
 
 #pragma once
 
+#include <immer/config.hpp>
+
 #include <tuple>
 #include <utility>
 
@@ -15,68 +17,39 @@ namespace immer {
 namespace detail {
 namespace rbts {
 
-struct visitor_tag {};
-
-template <typename... Fns>
-using fn_visitor = std::tuple<visitor_tag, Fns...>;
-
-template <typename... Fns>
-auto make_visitor(Fns&& ...fns)
-{
-    return std::make_tuple(visitor_tag{}, std::forward<Fns>(fns)...);
-}
-
-template <typename FnR, typename FnI, typename FnL, typename... Args>
-decltype(auto) visit_relaxed(fn_visitor<FnR, FnI, FnL> v, Args&& ...args)
-{ return std::get<1>(v)(v, std::forward<Args>(args)...); }
-
-template <typename FnR, typename FnI, typename FnL, typename... Args>
-decltype(auto) visit_regular(fn_visitor<FnR, FnI, FnL> v, Args&& ...args)
-{ return std::get<2>(v)(v, std::forward<Args>(args)...); }
-
-template <typename FnR, typename FnI, typename FnL, typename... Args>
-decltype(auto) visit_leaf(fn_visitor<FnR, FnI, FnL> v, Args&& ...args)
-{ return std::get<3>(v)(v, std::forward<Args>(args)...); }
-
-template <typename FnI, typename FnL, typename... Args>
-decltype(auto) visit_inner(fn_visitor<FnI, FnL> v, Args&& ...args)
-{ return std::get<1>(v)(v, std::forward<Args>(args)...); }
-
-template <typename FnI, typename FnL, typename... Args>
-decltype(auto) visit_leaf(fn_visitor<FnI, FnL> v, Args&& ...args)
-{ return std::get<2>(v)(v, std::forward<Args>(args)...); }
-
-template <typename Fn, typename... Args>
-decltype(auto) visit_node(fn_visitor<Fn> v, Args&& ...args)
-{ return std::get<1>(v)(v, std::forward<Args>(args)...); }
-
-template <typename Visitor, typename... Args>
-decltype(auto) visit_relaxed(Visitor&& v, Args&& ...args)
-{
-    return visit_inner(std::forward<Visitor>(v),
-                       std::forward<Args>(args)...);
-}
-
-template <typename Visitor, typename... Args>
-decltype(auto) visit_regular(Visitor&& v, Args&& ...args)
-{
-    return visit_inner(std::forward<Visitor>(v),
-                       std::forward<Args>(args)...);
-}
-
-template <typename Visitor, typename... Args>
-decltype(auto) visit_inner(Visitor&& v, Args&& ...args)
-{
-    return visit_node(std::forward<Visitor>(v),
-                      std::forward<Args>(args)...);
-}
-
-template <typename Visitor, typename... Args>
-decltype(auto) visit_leaf(Visitor&& v, Args&& ...args)
+template <typename Deriv>
+struct visitor_base
 {
-    return visit_node(std::forward<Visitor>(v),
-                      std::forward<Args>(args)...);
-}
+    template <typename... Args>
+    static decltype(auto) visit_node(Args&& ...args)
+    {
+        IMMER_UNREACHABLE;
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_relaxed(Args&& ...args)
+    {
+        return Deriv::visit_inner(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_regular(Args&& ...args)
+    {
+        return Deriv::visit_inner(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_inner(Args&& ...args)
+    {
+        return Deriv::visit_node(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    static decltype(auto) visit_leaf(Args&& ...args)
+    {
+        return Deriv::visit_node(std::forward<Args>(args)...);
+    }
+};
 
 } // namespace rbts
 } // namespace detail
diff --git a/src/immer/detail/type_traits.hpp b/src/immer/detail/type_traits.hpp
new file mode 100644
index 000000000000..7820e75d7a56
--- /dev/null
+++ b/src/immer/detail/type_traits.hpp
@@ -0,0 +1,191 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace immer {
+namespace detail {
+
+template <typename... Ts>
+struct make_void { using type = void; };
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename T, typename = void>
+struct is_dereferenceable : std::false_type {};
+
+template <typename T>
+struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>> :
+    std::true_type {};
+
+template <typename T>
+constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
+
+template<typename T, typename U=T, typename = void>
+struct is_equality_comparable : std::false_type {};
+
+template<typename T, typename U>
+struct is_equality_comparable
+<T, U, std::enable_if_t
+ <std::is_same
+  <bool, decltype(std::declval<T&>() == std::declval<U&>())>::value>> :
+    std::true_type {};
+
+template<typename T, typename U=T>
+constexpr bool is_equality_comparable_v = is_equality_comparable<T, U>::value;
+
+template<typename T, typename U=T, typename = void>
+struct is_inequality_comparable : std::false_type {};
+
+template<typename T, typename U>
+struct is_inequality_comparable
+<T, U, std::enable_if_t
+ <std::is_same
+  <bool, decltype(std::declval<T&>() != std::declval<U&>())>::value>> :
+    std::true_type {};
+
+template<typename T, typename U=T>
+constexpr bool is_inequality_comparable_v =
+  is_inequality_comparable<T, U>::value;
+
+template <typename T, typename = void>
+struct is_preincrementable : std::false_type {};
+
+template <typename T>
+struct is_preincrementable
+<T, std::enable_if_t
+ <std::is_same<T&, decltype(++(std::declval<T&>()))>::value>> :
+    std::true_type {};
+
+template <typename T>
+constexpr bool is_preincrementable_v = is_preincrementable<T>::value;
+
+template <typename T, typename U=T, typename = void>
+struct is_subtractable : std::false_type {};
+
+template <typename T, typename U>
+struct is_subtractable
+<T, U, void_t<decltype(std::declval<T&>() - std::declval<U&>())>> :
+    std::true_type {};
+
+template <typename T, typename U = T>
+constexpr bool is_subtractable_v = is_subtractable<T, U>::value;
+
+namespace swappable {
+
+using std::swap;
+
+template <typename T, typename U, typename = void>
+struct with : std::false_type {};
+
+// Does not account for non-referenceable types
+template <typename T, typename U>
+struct with
+<T, U, void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())),
+              decltype(swap(std::declval<U&>(), std::declval<T&>()))>> :
+    std::true_type {};
+
+}
+
+template<typename T, typename U>
+using is_swappable_with = swappable::with<T, U>;
+
+template<typename T>
+using is_swappable = is_swappable_with<T, T>;
+
+template <typename T>
+constexpr bool is_swappable_v = is_swappable_with<T&, T&>::value;
+
+template <typename T, typename = void>
+struct is_iterator : std::false_type {};
+
+// See http://en.cppreference.com/w/cpp/concept/Iterator
+template <typename T>
+struct is_iterator
+<T, void_t
+ <std::enable_if_t
+  <is_preincrementable_v<T>
+   && is_dereferenceable_v<T>
+   // accounts for non-referenceable types
+   && std::is_copy_constructible<T>::value
+   && std::is_copy_assignable<T>::value
+   && std::is_destructible<T>::value
+   && is_swappable_v<T>>,
+  typename std::iterator_traits<T>::value_type,
+  typename std::iterator_traits<T>::difference_type,
+  typename std::iterator_traits<T>::reference,
+  typename std::iterator_traits<T>::pointer,
+  typename std::iterator_traits<T>::iterator_category>> :
+    std::true_type {};
+
+template<typename T>
+constexpr bool is_iterator_v = is_iterator<T>::value;
+
+template<typename T, typename U, typename = void>
+struct compatible_sentinel : std::false_type {};
+
+template<typename T, typename U>
+struct compatible_sentinel
+<T, U, std::enable_if_t
+ <is_iterator_v<T>
+  && is_equality_comparable_v<T, U>
+  && is_inequality_comparable_v<T, U>>> :
+    std::true_type {};
+
+template<typename T, typename U>
+constexpr bool compatible_sentinel_v = compatible_sentinel<T,U>::value;
+
+template<typename T, typename = void>
+struct is_forward_iterator : std::false_type {};
+
+template<typename T>
+struct is_forward_iterator
+<T, std::enable_if_t
+ <is_iterator_v<T> &&
+  std::is_base_of
+  <std::forward_iterator_tag,
+   typename std::iterator_traits<T>::iterator_category>::value>> :
+    std::true_type {};
+
+template<typename T>
+constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value;
+
+template<typename T, typename U, typename = void>
+struct std_distance_supports : std::false_type {};
+
+template<typename T, typename U>
+struct std_distance_supports
+<T, U, void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>> : 
+  std::true_type {};
+
+template<typename T, typename U>
+constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value;
+
+template<typename T, typename U, typename V, typename = void>
+struct std_uninitialized_copy_supports : std::false_type {};
+
+template<typename T, typename U, typename V>
+struct std_uninitialized_copy_supports
+<T, U, V, void_t<decltype(std::uninitialized_copy(std::declval<T>(),
+						  std::declval<U>(),
+						  std::declval<V>()))>> : 
+  std::true_type {};
+
+template<typename T, typename U, typename V>
+constexpr bool std_uninitialized_copy_supports_v = 
+  std_uninitialized_copy_supports<T, U, V>::value;
+
+}
+}
diff --git a/src/immer/detail/util.hpp b/src/immer/detail/util.hpp
index 7490357dbf60..854a263d9e68 100644
--- a/src/immer/detail/util.hpp
+++ b/src/immer/detail/util.hpp
@@ -15,6 +15,12 @@
 #include <type_traits>
 #include <memory>
 
+#include <immer/detail/type_traits.hpp>
+
+#if defined(_MSC_VER)
+#include <intrin.h> // for __lzcnt*
+#endif
+
 namespace immer {
 namespace detail {
 
@@ -74,26 +80,32 @@ struct exact_t
 
 template <typename T>
 inline constexpr auto clz_(T) -> not_supported_t { IMMER_UNREACHABLE; return {}; }
+#if defined(_MSC_VER)
+// inline auto clz_(unsigned short x) { return __lzcnt16(x); }
+// inline auto clz_(unsigned int x) { return __lzcnt(x); }
+// inline auto clz_(unsigned __int64 x) { return __lzcnt64(x); }
+#else
 inline constexpr auto clz_(unsigned int x) { return __builtin_clz(x); }
 inline constexpr auto clz_(unsigned long x) { return __builtin_clzl(x); }
 inline constexpr auto clz_(unsigned long long x) { return __builtin_clzll(x); }
+#endif
 
 template <typename T>
 inline constexpr T log2_aux(T x, T r = 0)
 {
-    return x <= 1 ? r : log2(x >> 1, r + 1);
+    return x <= 1 ? r : log2_aux(x >> 1, r + 1);
 }
 
 template <typename T>
 inline constexpr auto log2(T x)
-    -> std::enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>{}, T>
+    -> std::enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
 {
     return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x);
 }
 
 template <typename T>
 inline constexpr auto log2(T x)
-    -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>{}, T>
+    -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
 {
     return log2_aux(x);
 }
@@ -119,5 +131,95 @@ struct constantly
     T operator() (Args&&...) const { return value; }
 };
 
+/*!
+ * An alias to `std::distance`
+ */
+template <typename Iterator, typename Sentinel,
+          std::enable_if_t
+          <detail::std_distance_supports_v<Iterator,Sentinel>, bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    return std::distance(first, last);
+}
+
+/*!
+ * Equivalent of the `std::distance` applied to the sentinel-delimited
+ * forward range @f$ [first, last) @f$
+ */
+template <typename Iterator, typename Sentinel,
+          std::enable_if_t
+          <(!detail::std_distance_supports_v<Iterator,Sentinel>)
+           && detail::is_forward_iterator_v<Iterator>
+           && detail::compatible_sentinel_v<Iterator,Sentinel>
+           && (!detail::is_subtractable_v<Sentinel, Iterator>), bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    std::size_t result = 0;
+    while (first != last) {
+        ++first;
+        ++result;
+    }
+    return result;
+}
+
+/*!
+ * Equivalent of the `std::distance` applied to the sentinel-delimited
+ * random access range @f$ [first, last) @f$
+ */
+template <typename Iterator, typename Sentinel,
+          std::enable_if_t
+          <(!detail::std_distance_supports_v<Iterator,Sentinel>)
+           && detail::is_forward_iterator_v<Iterator>
+           && detail::compatible_sentinel_v<Iterator,Sentinel>
+           && detail::is_subtractable_v<Sentinel, Iterator>, bool> = true>
+typename std::iterator_traits<Iterator>::difference_type
+distance(Iterator first, Sentinel last)
+{
+    return last - first;
+}
+
+
+
+/*!
+ * An alias to `std::uninitialized_copy`
+ */
+template <typename Iterator, typename Sentinel, typename SinkIter,
+          std::enable_if_t
+          <detail::std_uninitialized_copy_supports_v
+           <Iterator,Sentinel,SinkIter>, bool> = true>
+SinkIter uninitialized_copy(Iterator first, Sentinel last, SinkIter d_first)
+{
+    return std::uninitialized_copy(first, last, d_first);
+}
+
+/*!
+ * Equivalent of the `std::uninitialized_copy` applied to the
+ * sentinel-delimited forward range @f$ [first, last) @f$
+ */
+template <typename SourceIter, typename Sent, typename SinkIter,
+          std::enable_if_t
+          <(!detail::std_uninitialized_copy_supports_v<SourceIter, Sent, SinkIter>)
+           && detail::compatible_sentinel_v<SourceIter,Sent>
+           && detail::is_forward_iterator_v<SinkIter>, bool> = true>
+SinkIter uninitialized_copy(SourceIter first, Sent last, SinkIter d_first)
+{
+    auto current = d_first;
+    try {
+        while (first != last) {
+            *current++ = *first;
+            ++first;
+        }
+    } catch (...) {
+        using Value = typename std::iterator_traits<SinkIter>::value_type;
+        for (;d_first != current; ++d_first){
+            d_first->~Value();
+        }
+        throw;
+    }
+    return current;
+}
+
 } // namespace detail
 } // namespace immer
diff --git a/src/immer/flex_vector.hpp b/src/immer/flex_vector.hpp
index e2ddea24e9a7..7ab8fcd31b16 100644
--- a/src/immer/flex_vector.hpp
+++ b/src/immer/flex_vector.hpp
@@ -89,18 +89,20 @@ class flex_vector
     flex_vector() = default;
 
     /*!
-     * Constructs a vector containing the elements in `values`.
+     * Constructs a flex_vector containing the elements in `values`.
      */
     flex_vector(std::initializer_list<T> values)
         : impl_{impl_t::from_initializer_list(values)}
     {}
 
     /*!
-     * Constructs a vector containing the elements in the range
-     * defined by the input iterators `first` and `last`.
+     * Constructs a flex_vector containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
      */
-    template <typename Iter>
-    flex_vector(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <detail::compatible_sentinel_v<Iter, Sent>, bool> = true>
+    flex_vector(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
 
diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp
index 124beb81b419..4fbc665c69b6 100644
--- a/src/immer/heap/debug_size_heap.hpp
+++ b/src/immer/heap/debug_size_heap.hpp
@@ -8,9 +8,10 @@
 
 #pragma once
 
+#include <cassert>
+#include <cstddef>
 #include <immer/config.hpp>
 #include <immer/heap/identity_heap.hpp>
-#include <cassert>
 
 namespace immer {
 
@@ -23,20 +24,22 @@ namespace immer {
 template <typename Base>
 struct debug_size_heap
 {
+    constexpr static auto extra_size = alignof(std::max_align_t);
+
     template <typename... Tags>
     static void* allocate(std::size_t size, Tags... tags)
     {
-        auto p = (std::size_t*) Base::allocate(size + sizeof(std::size_t), tags...);
+        auto p = (std::size_t*) Base::allocate(size + extra_size, tags...);
         *p = size;
-        return p + 1;
+        return ((char*)p) + extra_size;
     }
 
     template <typename... Tags>
     static void deallocate(std::size_t size, void* data, Tags... tags)
     {
-        auto p = ((std::size_t*) data) - 1;
+        auto p = (std::size_t*) (((char*) data) - extra_size);
         assert(*p == size);
-        Base::deallocate(size + sizeof(std::size_t), p, tags...);
+        Base::deallocate(size + extra_size, p, tags...);
     }
 };
 
diff --git a/src/immer/map.hpp b/src/immer/map.hpp
index 53d450b6835f..99e483a2d40e 100644
--- a/src/immer/map.hpp
+++ b/src/immer/map.hpp
@@ -57,9 +57,9 @@ class map_transient;
  */
 template <typename K,
           typename T,
-          typename Hash          = std::hash<K>,
-          typename Equal         = std::equal_to<K>,
-          typename MemoryPolicy  = default_memory_policy,
+          typename Hash           = std::hash<K>,
+          typename Equal          = std::equal_to<K>,
+          typename MemoryPolicy   = default_memory_policy,
           detail::hamts::bits_t B = default_bits>
 class map
 {
diff --git a/src/immer/refcount/no_refcount_policy.hpp b/src/immer/refcount/no_refcount_policy.hpp
index 188fb9f5594e..95663368f112 100644
--- a/src/immer/refcount/no_refcount_policy.hpp
+++ b/src/immer/refcount/no_refcount_policy.hpp
@@ -12,12 +12,26 @@ namespace immer {
 
 struct disowned {};
 
+struct no_spinlock
+{
+    bool try_lock() { return true; }
+    void lock() {}
+    void unlock() {}
+
+    struct scoped_lock
+    {
+        scoped_lock(no_spinlock&) {}
+    };
+};
+
 /*!
  * Disables reference counting, to be used with an alternative garbage
  * collection strategy like a `gc_heap`.
  */
 struct no_refcount_policy
 {
+    using spinlock_type = no_spinlock;
+
     no_refcount_policy() {};
     no_refcount_policy(disowned) {}
 
diff --git a/src/immer/refcount/refcount_policy.hpp b/src/immer/refcount/refcount_policy.hpp
index b143cbf7e43a..68ba209c8c76 100644
--- a/src/immer/refcount/refcount_policy.hpp
+++ b/src/immer/refcount/refcount_policy.hpp
@@ -13,15 +13,74 @@
 #include <atomic>
 #include <utility>
 #include <cassert>
+#include <thread>
+
+// This has been shamelessly copied from boost...
+#if defined(_MSC_VER) && _MSC_VER >= 1310 && (defined(_M_IX86) || defined(_M_X64)) && !defined(__c2__)
+extern "C" void _mm_pause();
+#  define IMMER_SMT_PAUSE _mm_pause()
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#  define IMMER_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" )
+#endif
 
 namespace immer {
 
+// This is an atomic spinlock similar to the one used by boost to provide
+// "atomic" shared_ptr operations.  It also does not differ much from the one
+// from libc++ or libstdc++...
+struct spinlock
+{
+    std::atomic_flag v_{};
+
+    bool try_lock()
+    {
+        return !v_.test_and_set(std::memory_order_acquire);
+    }
+
+    void lock()
+    {
+        for (auto k = 0u; !try_lock(); ++k) {
+            if (k < 4)
+                continue;
+#ifdef IMMER_SMT_PAUSE
+            else if (k < 16)
+                IMMER_SMT_PAUSE;
+#endif
+            else
+                std::this_thread::yield();
+        }
+    }
+
+    void unlock()
+    {
+        v_.clear(std::memory_order_release);
+    }
+
+    struct scoped_lock
+    {
+        scoped_lock(const scoped_lock&) = delete;
+        scoped_lock& operator=(const scoped_lock& ) = delete;
+
+        explicit scoped_lock(spinlock& sp)
+            : sp_{sp}
+        { sp.lock(); }
+
+        ~scoped_lock()
+        { sp_.unlock();}
+
+    private:
+        spinlock& sp_;
+    };
+};
+
 /*!
  * A reference counting policy implemented using an *atomic* `int`
  * count.  It is **thread-safe**.
  */
 struct refcount_policy
 {
+    using spinlock_type = spinlock;
+
     mutable std::atomic<int> refcount;
 
     refcount_policy() : refcount{1} {};
diff --git a/src/immer/refcount/unsafe_refcount_policy.hpp b/src/immer/refcount/unsafe_refcount_policy.hpp
index 9a77f06a512a..2a170b7573cc 100644
--- a/src/immer/refcount/unsafe_refcount_policy.hpp
+++ b/src/immer/refcount/unsafe_refcount_policy.hpp
@@ -21,6 +21,8 @@ namespace immer {
  */
 struct unsafe_refcount_policy
 {
+    using spinlock_type = no_spinlock;
+
     mutable int refcount;
 
     unsafe_refcount_policy() : refcount{1} {};
diff --git a/src/immer/set.hpp b/src/immer/set.hpp
index 9f2b0b301df4..5d9eed8ca6d3 100644
--- a/src/immer/set.hpp
+++ b/src/immer/set.hpp
@@ -54,9 +54,9 @@ class set_transient;
  *
  */
 template <typename T,
-          typename Hash          = std::hash<T>,
-          typename Equal         = std::equal_to<T>,
-          typename MemoryPolicy  = default_memory_policy,
+          typename Hash           = std::hash<T>,
+          typename Equal          = std::equal_to<T>,
+          typename MemoryPolicy   = default_memory_policy,
           detail::hamts::bits_t B = default_bits>
 class set
 {
diff --git a/src/immer/vector.hpp b/src/immer/vector.hpp
index 296431fb0ae6..b9c9ef48f6c8 100644
--- a/src/immer/vector.hpp
+++ b/src/immer/vector.hpp
@@ -111,10 +111,12 @@ class vector
 
     /*!
      * Constructs a vector containing the elements in the range
-     * defined by the input iterators `first` and `last`.
+     * defined by the input iterator `first` and range sentinel `last`.
      */
-    template <typename Iter>
-    vector(Iter first, Iter last)
+    template <typename Iter, typename Sent,
+              std::enable_if_t
+              <detail::compatible_sentinel_v<Iter, Sent>, bool> = true>
+    vector(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
 

From 1572f7c6db8c2204480794491e0d20377f953099 Mon Sep 17 00:00:00 2001
From: Alexander Block <ablock84@gmail.com>
Date: Mon, 1 Apr 2019 10:23:18 +0200
Subject: [PATCH 2/2] Temporary fix for alignof(std::max_align_t) on MinGW
 32bit builds

See https://github.com/arximboldi/immer/issues/78
---
 src/immer/heap/debug_size_heap.hpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp
index 4fbc665c69b6..5cfb74ca8f65 100644
--- a/src/immer/heap/debug_size_heap.hpp
+++ b/src/immer/heap/debug_size_heap.hpp
@@ -24,7 +24,8 @@ namespace immer {
 template <typename Base>
 struct debug_size_heap
 {
-    constexpr static auto extra_size = alignof(std::max_align_t);
+    // temporary fix until https://github.com/arximboldi/immer/issues/78 is fixed
+    constexpr static auto extra_size = sizeof(void*) * 2; //alignof(std::max_align_t);
 
     template <typename... Tags>
     static void* allocate(std::size_t size, Tags... tags)
