tint/utils: More Vector polish

* Added a class template argument deduction guide (CTAD) to infer the
  `T` and `N` template arguments. This lets you write `Vector{1,2,3}`
  instead of `Vector<int, 3>{1,2,3}`. This is important as a mismatch
  between the number of constructor arguments and the `N` template
  argument can cause silent heap allocations, which we're trying to
  avoid. The `T` deduction uses the same smarts as the return-type
  deduction of `Switch()`, so:
   * `Vector{1, 2.0}` would construct a `Vector<double, 2>`
   * `Vector{i32, u32}` would construct a `Vector<const sem::Type*, 2>`
* Removed the Vector(size_t) and Vector(size_t, const T&) constructors.
  This is a move away from the std::vector style API, but these are
  rarely more efficient than calling Reserve() and Push(), as you remove
  the redundant initialization. The main reason for doing this is to
  remove ambiguity between `Vector{1}` and `Vector(1)`.
* Added support for covariance conversion
  (`Vector<Derived*, N>` -> `Vector<Base*, N>`).
  Only supports pointers to `Castable`, as this can  only safely work
  with single-inheritance.
* Added support for conversion of `Vector<T*, N>` -> `Vector<const T*, N>`.
  This will remove pointless vector copies from the sem package.

Bug: tint:1613
Change-Id: I79b9f82d623f90afa14f8ba1613ee49cccceafeb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97020
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/utils/hash_test.cc b/src/tint/utils/hash_test.cc
index 9e60a66..6ce6820 100644
--- a/src/tint/utils/hash_test.cc
+++ b/src/tint/utils/hash_test.cc
@@ -44,10 +44,10 @@
 }
 
 TEST(HashTests, TintVector) {
-    EXPECT_EQ(Hash(Vector<int>({})), Hash(Vector<int>({})));
-    EXPECT_EQ(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3})));
-    EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 4})));
-    EXPECT_NE(Hash(Vector<int>({1, 2, 3})), Hash(Vector<int>({1, 2, 3, 4})));
+    EXPECT_EQ(Hash(Vector<int, 0>({})), Hash(Vector<int, 0>({})));
+    EXPECT_EQ(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3})));
+    EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 4})));
+    EXPECT_NE(Hash(Vector<int, 0>({1, 2, 3})), Hash(Vector<int, 0>({1, 2, 3, 4})));
     EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 4>({1, 2, 3})));
     EXPECT_EQ(Hash(Vector<int, 3>({1, 2, 3})), Hash(Vector<int, 2>({1, 2, 3})));
 }
diff --git a/src/tint/utils/transform.h b/src/tint/utils/transform.h
index 0c91263..412dbed 100644
--- a/src/tint/utils/transform.h
+++ b/src/tint/utils/transform.h
@@ -59,10 +59,12 @@
 /// @returns a new vector with each element of the source vector transformed by `transform`.
 template <typename IN, size_t N, typename TRANSFORMER>
 auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
-    -> Vector<decltype(transform(in[0]))> {
-    Vector<decltype(transform(in[0])), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i]);
+    -> Vector<decltype(transform(in[0])), N> {
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0])), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i]));
     }
     return result;
 }
@@ -74,9 +76,11 @@
 template <typename IN, size_t N, typename TRANSFORMER>
 auto Transform(const Vector<IN, N>& in, TRANSFORMER&& transform)
     -> Vector<decltype(transform(in[0], 1u)), N> {
-    Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i], i);
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0], 1u)), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i], i));
     }
     return result;
 }
@@ -89,9 +93,11 @@
 template <size_t N, typename IN, typename TRANSFORMER>
 auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
     -> Vector<decltype(transform(in[0])), N> {
-    Vector<decltype(transform(in[0])), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i]);
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0])), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i]));
     }
     return result;
 }
@@ -104,9 +110,11 @@
 template <size_t N, typename IN, typename TRANSFORMER>
 auto Transform(const VectorRef<IN>& in, TRANSFORMER&& transform)
     -> Vector<decltype(transform(in[0], 1u)), N> {
-    Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i], i);
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0], 1u)), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i], i));
     }
     return result;
 }
@@ -119,9 +127,11 @@
 template <size_t N, typename IN, typename TRANSFORMER>
 auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
     -> Vector<decltype(transform(in[0])), N> {
-    Vector<decltype(transform(in[0])), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i]);
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0])), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i]));
     }
     return result;
 }
@@ -134,9 +144,11 @@
 template <size_t N, typename IN, typename TRANSFORMER>
 auto Transform(ConstVectorRef<IN> in, TRANSFORMER&& transform)
     -> Vector<decltype(transform(in[0], 1u)), N> {
-    Vector<decltype(transform(in[0], 1u)), N> result(in.Length());
-    for (size_t i = 0; i < result.Length(); ++i) {
-        result[i] = transform(in[i], i);
+    const auto count = in.Length();
+    Vector<decltype(transform(in[0], 1u)), N> result;
+    result.Reserve(count);
+    for (size_t i = 0; i < count; ++i) {
+        result.Push(transform(in[i], i));
     }
     return result;
 }
diff --git a/src/tint/utils/vector.h b/src/tint/utils/vector.h
index daac42c..d339a49 100644
--- a/src/tint/utils/vector.h
+++ b/src/tint/utils/vector.h
@@ -22,6 +22,8 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/castable.h"
+#include "src/tint/traits.h"
 #include "src/tint/utils/bitcast.h"
 
 namespace tint::utils {
@@ -36,8 +38,6 @@
 
 namespace tint::utils {
 
-namespace detail {
-
 /// A slice represents a contigious array of elements of type T.
 template <typename T>
 struct Slice {
@@ -97,7 +97,42 @@
     auto rend() const { return std::reverse_iterator<const T*>(begin()); }
 };
 
-}  // namespace detail
+/// Evaluates whether a `vector<FROM>` and be reinterpreted as a `vector<TO>`.
+/// Vectors can be reinterpreted if both `FROM` and `TO` are pointers to a type that derives from
+/// CastableBase, and the pointee type of `TO` is of the same type as, or is an ancestor of the
+/// pointee type of `FROM`. Vectors of non-`const` Castable pointers can be converted to a vector of
+/// `const` Castable pointers.
+template <typename TO, typename FROM>
+static constexpr bool CanReinterpretSlice =
+    // TO and FROM are both pointer types
+    std::is_pointer_v<TO> && std::is_pointer_v<FROM> &&  //
+    // const can only be applied, not removed
+    (std::is_const_v<std::remove_pointer_t<TO>> ||
+     !std::is_const_v<std::remove_pointer_t<FROM>>)&&  //
+    // TO and FROM are both Castable
+    IsCastable<std::remove_pointer_t<FROM>, std::remove_pointer_t<TO>> &&
+    // FROM is of, or derives from TO
+    traits::IsTypeOrDerived<std::remove_pointer_t<FROM>, std::remove_pointer_t<TO>>;
+
+/// Reinterprets `const Slice<FROM>*` as `const Slice<TO>*`
+/// @param slice a pointer to the slice to reinterpret
+/// @returns the reinterpreted slice
+/// @see CanReinterpretSlice
+template <typename TO, typename FROM>
+const Slice<TO>* ReinterpretSlice(const Slice<FROM>* slice) {
+    static_assert(CanReinterpretSlice<TO, FROM>);
+    return Bitcast<const Slice<TO>*>(slice);
+}
+
+/// Reinterprets `Slice<FROM>*` as `Slice<TO>*`
+/// @param slice a pointer to the slice to reinterpret
+/// @returns the reinterpreted slice
+/// @see CanReinterpretSlice
+template <typename TO, typename FROM>
+Slice<TO>* ReinterpretSlice(Slice<FROM>* slice) {
+    static_assert(CanReinterpretSlice<TO, FROM>);
+    return Bitcast<Slice<TO>*>(slice);
+}
 
 /// Vector is a small-object-optimized, dynamically-sized vector of contigious elements of type T.
 ///
@@ -118,39 +153,20 @@
 ///   array'. This reduces memory copying, but may incur additional memory usage.
 /// * Resizing, or popping elements from a vector that has spilled to a heap allocation does not
 ///   revert back to using the 'small array'. Again, this is to reduce memory copying.
-template <typename T, size_t N = 0>
+template <typename T, size_t N>
 class Vector {
   public:
     /// Type of `T`.
     using value_type = T;
+    /// Value of `N`
+    static constexpr size_t static_length = N;
 
     /// Constructor
     Vector() = default;
 
     /// Constructor
-    /// @param length the initial length of the vector. Elements will be zero-initialized.
-    explicit Vector(size_t length) {
-        Reserve(length);
-        for (size_t i = 0; i < length; i++) {
-            new (&impl_.slice.data[i]) T{};
-        }
-        impl_.slice.len = length;
-    }
-
-    /// Constructor
-    /// @param length the initial length of the vector
-    /// @param value the value to copy into each element of the vector
-    Vector(size_t length, const T& value) {
-        Reserve(length);
-        for (size_t i = 0; i < length; i++) {
-            new (&impl_.slice.data[i]) T{value};
-        }
-        impl_.slice.len = length;
-    }
-
-    /// Constructor
     /// @param elements the elements to place into the vector
-    explicit Vector(std::initializer_list<T> elements) {
+    Vector(std::initializer_list<T> elements) {
         Reserve(elements.size());
         for (auto& el : elements) {
             new (&impl_.slice.data[impl_.slice.len++]) T{el};
@@ -179,17 +195,32 @@
         MoveOrCopy(VectorRef<T>(std::move(other)));
     }
 
+    /// Copy constructor with covariance / const conversion
+    /// @param other the vector to copy
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, size_t N2, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    Vector(const Vector<U, N2>& other) {  // NOLINT(runtime/explicit)
+        Copy(*ReinterpretSlice<T>(&other.impl_.slice));
+    }
+
+    /// Move constructor with covariance / const conversion
+    /// @param other the vector to move
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, size_t N2, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    Vector(Vector<U, N2>&& other) {  // NOLINT(runtime/explicit)
+        MoveOrCopy(VectorRef<T>(std::move(other)));
+    }
+
     /// Move constructor from a mutable vector reference
     /// @param other the vector reference to move
-    Vector(VectorRef<T>&& other) {  // NOLINT(runtime/explicit)
-        MoveOrCopy(std::move(other));
-    }
+    explicit Vector(VectorRef<T>&& other) { MoveOrCopy(std::move(other)); }
 
     /// Copy constructor from an immutable vector reference
     /// @param other the vector reference to copy
-    Vector(const ConstVectorRef<T>& other) {  // NOLINT(runtime/explicit)
-        Copy(other.slice_);
-    }
+    explicit Vector(const ConstVectorRef<T>& other) { Copy(other.slice_); }
+
+    /// Move constructor from an immutable vector reference (invalid)
+    Vector(ConstVectorRef<T>&&) = delete;  // NOLINT(runtime/explicit)
 
     /// Destructor
     ~Vector() { ClearAndFree(); }
@@ -277,6 +308,20 @@
         impl_.slice.len = new_len;
     }
 
+    /// Resizes the vector to the given length, expanding capacity if necessary.
+    /// @param new_len the new vector length
+    /// @param value the value to copy into the new elements
+    void Resize(size_t new_len, const T& value) {
+        Reserve(new_len);
+        for (size_t i = impl_.slice.len; i > new_len; i--) {  // Shrink
+            impl_.slice.data[i - 1].~T();
+        }
+        for (size_t i = impl_.slice.len; i < new_len; i++) {  // Grow
+            new (&impl_.slice.data[i]) T{value};
+        }
+        impl_.slice.len = new_len;
+    }
+
     /// Copies all the elements from `other` to this vector, replacing the content of this vector.
     /// @param other the
     template <typename T2, size_t N2>
@@ -317,7 +362,7 @@
         if (impl_.slice.len >= impl_.slice.cap) {
             Grow();
         }
-        new (&impl_.slice.data[impl_.slice.len++]) T(std::forward<ARGS>(args)...);
+        new (&impl_.slice.data[impl_.slice.len++]) T{std::forward<ARGS>(args)...};
     }
 
     /// Removes and returns the last element from the vector.
@@ -377,7 +422,12 @@
     friend class ConstVectorRef;
 
     /// The slice type used by this vector
-    using Slice = detail::Slice<T>;
+    using Slice = utils::Slice<T>;
+
+    template <typename... Ts>
+    void AppendVariadic(Ts&&... args) {
+        ((new (&impl_.slice.data[impl_.slice.len++]) T(std::forward<Ts>(args))), ...);
+    }
 
     /// Expands the capacity of the vector
     void Grow() { Reserve(impl_.slice.cap * 2); }
@@ -484,6 +534,43 @@
     std::conditional_t<HasSmallArray, ImplWithSmallArray, ImplWithoutSmallArray> impl_;
 };
 
+namespace detail {
+
+/// Helper for determining the Vector element type (`T`) from the vector's constuctor arguments
+/// @tparam IS_CASTABLE true if the types of `Ts` derive from CastableBase
+/// @tparam Ts the vector constructor argument types to infer the vector element type from.
+template <bool IS_CASTABLE, typename... Ts>
+struct VectorCommonType;
+
+/// VectorCommonType specialization for non-castable types.
+template <typename... Ts>
+struct VectorCommonType</*IS_CASTABLE*/ false, Ts...> {
+    /// The common T type to use for the vector
+    using type = std::common_type_t<Ts...>;
+};
+
+/// VectorCommonType specialization for castable types.
+template <typename... Ts>
+struct VectorCommonType</*IS_CASTABLE*/ true, Ts...> {
+    /// The common Castable type (excluding pointer)
+    using common_ty = CastableCommonBase<std::remove_pointer_t<Ts>...>;
+    /// The common T type to use for the vector
+    using type = std::conditional_t<(std::is_const_v<std::remove_pointer_t<Ts>> || ...),
+                                    const common_ty*,
+                                    common_ty*>;
+};
+
+}  // namespace detail
+
+/// Helper for determining the Vector element type (`T`) from the vector's constuctor arguments
+template <typename... Ts>
+using VectorCommonType =
+    typename detail::VectorCommonType<IsCastable<std::remove_pointer_t<Ts>...>, Ts...>::type;
+
+/// Deduction guide for Vector
+template <typename... Ts>
+Vector(Ts...) -> Vector<VectorCommonType<Ts...>, sizeof...(Ts)>;
+
 /// VectorRef is a weak reference to a Vector, used to pass vectors as parameters, avoiding copies
 /// between the caller and the callee. VectorRef can accept a Vector of any 'N' value, decoupling
 /// the caller's vector internal size from the callee's vector size.
@@ -507,16 +594,16 @@
 template <typename T>
 class VectorRef {
     /// The slice type used by this vector reference
-    using Slice = detail::Slice<T>;
+    using Slice = utils::Slice<T>;
 
   public:
-    /// Constructor from a Vector.
-    /// @param vector the vector reference
+    /// Constructor from a Vector
+    /// @param vector the vector to create a reference of
     template <size_t N>
     VectorRef(Vector<T, N>& vector)  // NOLINT(runtime/explicit)
         : slice_(vector.impl_.slice), can_move_(false) {}
 
-    /// Constructor from a std::move()'d Vector
+    /// Constructor from a moved Vector
     /// @param vector the vector being moved
     template <size_t N>
     VectorRef(Vector<T, N>&& vector)  // NOLINT(runtime/explicit)
@@ -530,6 +617,32 @@
     /// @param other the vector reference
     VectorRef(VectorRef&& other) = default;
 
+    /// Copy constructor with covariance / const conversion
+    /// @param other the other vector reference
+    template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    VectorRef(const VectorRef<U>& other)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&other.slice_)), can_move_(false) {}
+
+    /// Move constructor with covariance / const conversion
+    /// @param other the vector reference
+    template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    VectorRef(VectorRef<U>&& other)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&other.slice_)), can_move_(other.can_move_) {}
+
+    /// Constructor from a Vector with covariance / const conversion
+    /// @param vector the vector to create a reference of
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    VectorRef(Vector<U, N>& vector)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(false) {}
+
+    /// Constructor from a moved Vector with covariance / const conversion
+    /// @param vector the vector to create a reference of
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    VectorRef(Vector<U, N>&& vector)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)), can_move_(vector.impl_.CanMove()) {}
+
     /// Index operator
     /// @param i the element index. Must be less than `len`.
     /// @returns a reference to the i'th element.
@@ -587,10 +700,18 @@
     auto rend() const { return slice_.rend(); }
 
   private:
-    /// Friend classes
+    /// Friend class
     template <typename, size_t>
     friend class Vector;
 
+    /// Friend class
+    template <typename>
+    friend class VectorRef;
+
+    /// Friend class
+    template <typename>
+    friend class ConstVectorRef;
+
     /// The slice of the vector being referenced.
     Slice& slice_;
     /// Whether the slice data is passed by r-value reference, and can be moved.
@@ -603,7 +724,7 @@
 template <typename T>
 class ConstVectorRef {
     /// The slice type used by this vector reference
-    using Slice = detail::Slice<T>;
+    using Slice = utils::Slice<T>;
 
   public:
     /// Constructor from a Vector.
@@ -616,6 +737,34 @@
     /// @param other the vector reference
     ConstVectorRef(const ConstVectorRef& other) = default;
 
+    /// Conversion constructor to convert from a non-const to const vector reference
+    /// @param other the vector reference
+    ConstVectorRef(const VectorRef<T>& other) : slice_(other.slice_) {}  // NOLINT(runtime/explicit)
+
+    /// Move constructor. Deleted as this won't move anything.
+    ConstVectorRef(ConstVectorRef&&) = delete;
+
+    /// Constructor from a Vector with covariance / const conversion
+    /// @param vector the vector to create a reference of
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, size_t N, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    ConstVectorRef(const Vector<U, N>& vector)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&vector.impl_.slice)) {}
+
+    /// Constructor from a VectorRef with covariance / const conversion
+    /// @param other the vector reference
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    ConstVectorRef(const VectorRef<U>& other)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&other.slice_)) {}
+
+    /// Constructor from a ConstVectorRef with covariance / const conversion
+    /// @param other the vector reference
+    /// @see CanReinterpretSlice for rules about conversion
+    template <typename U, typename = std::enable_if_t<CanReinterpretSlice<T, U>>>
+    ConstVectorRef(const ConstVectorRef<U>& other)  // NOLINT(runtime/explicit)
+        : slice_(*ReinterpretSlice<T>(&other.slice_)) {}
+
     /// Index operator
     /// @param i the element index. Must be less than `len`.
     /// @returns a reference to the i'th element.
@@ -650,10 +799,14 @@
     auto rend() const { return slice_.rend(); }
 
   private:
-    /// Friend classes
+    /// Friend class
     template <typename, size_t>
     friend class Vector;
 
+    /// Friend class
+    template <typename>
+    friend class ConstVectorRef;
+
     /// The slice of the vector being referenced.
     const Slice& slice_;
 };
@@ -682,14 +835,6 @@
     return out;
 }
 
-/// Helper for constructing a Vector from a set of elements.
-/// The returned Vector's small-array size (`N`) is equal to the number of provided elements.
-/// @param elements the elements used to construct the vector.
-template <typename T, typename... Ts>
-auto MakeVector(Ts&&... elements) {
-    return Vector<T, sizeof...(Ts)>({std::forward<Ts>(elements)...});
-}
-
 }  // namespace tint::utils
 
 #endif  // SRC_TINT_UTILS_VECTOR_H_
diff --git a/src/tint/utils/vector_test.cc b/src/tint/utils/vector_test.cc
index 8c2c0cf..6e529dc 100644
--- a/src/tint/utils/vector_test.cc
+++ b/src/tint/utils/vector_test.cc
@@ -24,6 +24,11 @@
 namespace tint::utils {
 namespace {
 
+class C0 : public Castable<C0> {};
+class C1 : public Castable<C1, C0> {};
+class C2a : public Castable<C2a, C1> {};
+class C2b : public Castable<C2b, C1> {};
+
 /// @returns true if the address of el is within the memory of the vector vec.
 template <typename T, size_t N, typename E>
 bool IsInternal(Vector<T, N>& vec, E& el) {
@@ -54,6 +59,46 @@
     return true;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Static asserts
+////////////////////////////////////////////////////////////////////////////////
+static_assert(std::is_same_v<VectorCommonType<int>, int>);
+static_assert(std::is_same_v<VectorCommonType<int, int>, int>);
+static_assert(std::is_same_v<VectorCommonType<int, float>, float>);
+
+static_assert(std::is_same_v<VectorCommonType<C0*>, C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*>, const C0*>);
+
+static_assert(std::is_same_v<VectorCommonType<C0*, C1*>, C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*, C1*>, const C0*>);
+static_assert(std::is_same_v<VectorCommonType<C0*, const C1*>, const C0*>);
+static_assert(std::is_same_v<VectorCommonType<const C0*, const C1*>, const C0*>);
+
+static_assert(std::is_same_v<VectorCommonType<C2a*, C2b*>, C1*>);
+static_assert(std::is_same_v<VectorCommonType<const C2a*, C2b*>, const C1*>);
+static_assert(std::is_same_v<VectorCommonType<C2a*, const C2b*>, const C1*>);
+static_assert(std::is_same_v<VectorCommonType<const C2a*, const C2b*>, const C1*>);
+
+static_assert(CanReinterpretSlice<const C0*, C0*>, "apply const");
+static_assert(!CanReinterpretSlice<C0*, const C0*>, "remove const");
+static_assert(CanReinterpretSlice<C0*, C1*>, "up cast");
+static_assert(CanReinterpretSlice<const C0*, const C1*>, "up cast");
+static_assert(CanReinterpretSlice<const C0*, C1*>, "up cast, apply const");
+static_assert(!CanReinterpretSlice<C0*, const C1*>, "up cast, remove const");
+static_assert(!CanReinterpretSlice<C1*, C0*>, "down cast");
+static_assert(!CanReinterpretSlice<const C1*, const C0*>, "down cast");
+static_assert(!CanReinterpretSlice<const C1*, C0*>, "down cast, apply const");
+static_assert(!CanReinterpretSlice<C1*, const C0*>, "down cast, remove const");
+static_assert(!CanReinterpretSlice<const C1*, C0*>, "down cast, apply const");
+static_assert(!CanReinterpretSlice<C1*, const C0*>, "down cast, remove const");
+static_assert(!CanReinterpretSlice<C2a*, C2b*>, "sideways cast");
+static_assert(!CanReinterpretSlice<const C2a*, const C2b*>, "sideways cast");
+static_assert(!CanReinterpretSlice<const C2a*, C2b*>, "sideways cast, apply const");
+static_assert(!CanReinterpretSlice<C2a*, const C2b*>, "sideways cast, remove const");
+
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorTest
+////////////////////////////////////////////////////////////////////////////////
 TEST(TintVectorTest, SmallArray_Empty) {
     Vector<int, 2> vec;
     EXPECT_EQ(vec.Length(), 0u);
@@ -61,59 +106,12 @@
 }
 
 TEST(TintVectorTest, Empty_NoSmallArray) {
-    Vector<int> vec;
+    Vector<int, 0> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 0u);
 }
 
-TEST(TintVectorTest, SmallArray_ConstructLength_NoSpill) {
-    Vector<int, 2> vec(2);
-    EXPECT_EQ(vec.Length(), 2u);
-    EXPECT_EQ(vec.Capacity(), 2u);
-    EXPECT_EQ(vec[0], 0);
-    EXPECT_EQ(vec[1], 0);
-    EXPECT_TRUE(AllInternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLength_WithSpill) {
-    Vector<int, 2> vec(3);
-    EXPECT_EQ(vec.Length(), 3u);
-    EXPECT_EQ(vec.Capacity(), 3u);
-    EXPECT_EQ(vec[0], 0);
-    EXPECT_EQ(vec[1], 0);
-    EXPECT_EQ(vec[2], 0);
-    EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLengthValue_NoSpill) {
-    Vector<std::string, 2> vec(2, "abc");
-    EXPECT_EQ(vec.Length(), 2u);
-    EXPECT_EQ(vec.Capacity(), 2u);
-    EXPECT_EQ(vec[0], "abc");
-    EXPECT_EQ(vec[1], "abc");
-    EXPECT_TRUE(AllInternallyHeld(vec));
-}
-
-TEST(TintVectorTest, SmallArray_ConstructLengthValue_WithSpill) {
-    Vector<std::string, 2> vec(3, "abc");
-    EXPECT_EQ(vec.Length(), 3u);
-    EXPECT_EQ(vec.Capacity(), 3u);
-    EXPECT_EQ(vec[0], "abc");
-    EXPECT_EQ(vec[1], "abc");
-    EXPECT_EQ(vec[2], "abc");
-    EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, ConstructLength_NoSmallArray) {
-    Vector<int> vec(2);
-    EXPECT_EQ(vec.Length(), 2u);
-    EXPECT_EQ(vec.Capacity(), 2u);
-    EXPECT_EQ(vec[0], 0);
-    EXPECT_EQ(vec[1], 0);
-    EXPECT_TRUE(AllExternallyHeld(vec));
-}
-
-TEST(TintVectorTest, ConstructInitializerList_NoSpill) {
+TEST(TintVectorTest, InitializerList_NoSpill) {
     Vector<std::string, 2> vec{"one", "two"};
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_EQ(vec.Capacity(), 2u);
@@ -122,7 +120,7 @@
     EXPECT_TRUE(AllInternallyHeld(vec));
 }
 
-TEST(TintVectorTest, ConstructInitializerList_WithSpill) {
+TEST(TintVectorTest, InitializerList_WithSpill) {
     Vector<std::string, 2> vec{"one", "two", "three"};
     EXPECT_EQ(vec.Length(), 3u);
     EXPECT_EQ(vec.Capacity(), 3u);
@@ -132,8 +130,8 @@
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
-TEST(TintVectorTest, ConstructInitializerList_NoSmallArray) {
-    Vector<std::string> vec{"one", "two"};
+TEST(TintVectorTest, InitializerList_NoSmallArray) {
+    Vector<std::string, 0> vec{"one", "two"};
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_EQ(vec.Capacity(), 2u);
     EXPECT_EQ(vec[0], "one");
@@ -141,9 +139,180 @@
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N2) {
+TEST(TintVectorTest, InferTN_1CString) {
+    auto vec = Vector{"one"};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const char*>);
+    static_assert(decltype(vec)::static_length == 1u);
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 1u);
+    EXPECT_STREQ(vec[0], "one");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_2CStrings) {
+    auto vec = Vector{"one", "two"};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const char*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_STREQ(vec[0], "one");
+    EXPECT_STREQ(vec[1], "two");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_IntFloat) {
+    auto vec = Vector{1, 2.0f};
+    static_assert(std::is_same_v<decltype(vec)::value_type, float>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], 1.0f);
+    EXPECT_EQ(vec[1], 2.0f);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_IntDoubleIntDouble) {
+    auto vec = Vector{1, 2.0, 3, 4.0};
+    static_assert(std::is_same_v<decltype(vec)::value_type, double>);
+    static_assert(decltype(vec)::static_length == 4u);
+    EXPECT_EQ(vec.Length(), 4u);
+    EXPECT_EQ(vec.Capacity(), 4u);
+    EXPECT_EQ(vec[0], 1.0);
+    EXPECT_EQ(vec[1], 2.0);
+    EXPECT_EQ(vec[2], 3.0);
+    EXPECT_EQ(vec[3], 4.0);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0) {
+    C0 c0;
+    auto vec = Vector{&c0};
+    static_assert(std::is_same_v<decltype(vec)::value_type, C0*>);
+    static_assert(decltype(vec)::static_length == 1u);
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 1u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0) {
+    const C0 c0;
+    auto vec = Vector{&c0};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+    static_assert(decltype(vec)::static_length == 1u);
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 1u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0C1) {
+    C0 c0;
+    C1 c1;
+    auto vec = Vector{&c0, &c1};
+    static_assert(std::is_same_v<decltype(vec)::value_type, C0*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_EQ(vec[1], &c1);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0C1) {
+    const C0 c0;
+    C1 c1;
+    auto vec = Vector{&c0, &c1};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_EQ(vec[1], &c1);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C0ConstC1) {
+    C0 c0;
+    const C1 c1;
+    auto vec = Vector{&c0, &c1};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_EQ(vec[1], &c1);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC0ConstC1) {
+    const C0 c0;
+    const C1 c1;
+    auto vec = Vector{&c0, &c1};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C0*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c0);
+    EXPECT_EQ(vec[1], &c1);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C2aC2b) {
+    C2a c2a;
+    C2b c2b;
+    auto vec = Vector{&c2a, &c2b};
+    static_assert(std::is_same_v<decltype(vec)::value_type, C1*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c2a);
+    EXPECT_EQ(vec[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC2aC2b) {
+    const C2a c2a;
+    C2b c2b;
+    auto vec = Vector{&c2a, &c2b};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c2a);
+    EXPECT_EQ(vec[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_C2aConstC2b) {
+    C2a c2a;
+    const C2b c2b;
+    auto vec = Vector{&c2a, &c2b};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c2a);
+    EXPECT_EQ(vec[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, InferTN_ConstC2aConstC2b) {
+    const C2a c2a;
+    const C2b c2b;
+    auto vec = Vector{&c2a, &c2b};
+    static_assert(std::is_same_v<decltype(vec)::value_type, const C1*>);
+    static_assert(decltype(vec)::static_length == 2u);
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], &c2a);
+    EXPECT_EQ(vec[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N2) {
     Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 2> vec_b{vec_a};
+    Vector<std::string, 2> vec_b(vec_a);
     EXPECT_EQ(vec_b.Length(), 2u);
     EXPECT_EQ(vec_b.Capacity(), 2u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -151,9 +320,9 @@
     EXPECT_TRUE(AllInternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N2) {
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N2) {
     Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 2> vec_b{vec_a};
+    Vector<std::string, 2> vec_b(vec_a);
     EXPECT_EQ(vec_b.Length(), 3u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -162,30 +331,9 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N2) {
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N1) {
     Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 2> vec_b{std::move(vec_a)};
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 2u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N2) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 2> vec_b{std::move(vec_a)};
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N1) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 1> vec_b{vec_a};
+    Vector<std::string, 1> vec_b(vec_a);
     EXPECT_EQ(vec_b.Length(), 2u);
     EXPECT_EQ(vec_b.Capacity(), 2u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -193,9 +341,9 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N1) {
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N1) {
     Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 1> vec_b{vec_a};
+    Vector<std::string, 1> vec_b(vec_a);
     EXPECT_EQ(vec_b.Length(), 3u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -204,9 +352,111 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N1) {
+TEST(TintVectorTest, CopyVector_NoSpill_N2_to_N3) {
     Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 1> vec_b{std::move(vec_a)};
+    Vector<std::string, 3> vec_b(vec_a);
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, CopyVector_WithSpill_N2_to_N3) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 3> vec_b(vec_a);
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcast_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<C0*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcast_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<C0*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveAddConst_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<const C1*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveAddConst_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<const C1*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcastAndAddConst_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<const C0*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, CopyVector_NoMoveUpcastAndAddConst_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<const C0*, 2> vec_b(vec_a);  // No move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N2) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 2> vec_b(std::move(vec_a));
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 2u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N2) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 2> vec_b(std::move(vec_a));
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N1) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 1> vec_b(std::move(vec_a));
     EXPECT_EQ(vec_b.Length(), 2u);
     EXPECT_EQ(vec_b.Capacity(), 2u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -214,9 +464,9 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N1) {
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N1) {
     Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 1> vec_b{std::move(vec_a)};
+    Vector<std::string, 1> vec_b(std::move(vec_a));
     EXPECT_EQ(vec_b.Length(), 3u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -225,9 +475,9 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, CopyCtor_NoSpill_N2_to_N3) {
+TEST(TintVectorTest, MoveVector_NoSpill_N2_to_N3) {
     Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 3> vec_b{vec_a};
+    Vector<std::string, 3> vec_b(std::move(vec_a));
     EXPECT_EQ(vec_b.Length(), 2u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -235,30 +485,9 @@
     EXPECT_TRUE(AllInternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, CopyCtor_WithSpill_N2_to_N3) {
+TEST(TintVectorTest, MoveVector_WithSpill_N2_to_N3) {
     Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 3> vec_b{vec_a};
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_NoSpill_N2_to_N3) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 3> vec_b{std::move(vec_a)};
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveCtor_WithSpill_N2_to_N3) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 3> vec_b{std::move(vec_a)};
+    Vector<std::string, 3> vec_b(std::move(vec_a));
     EXPECT_EQ(vec_b.Length(), 3u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
     EXPECT_EQ(vec_b[0], "hello");
@@ -267,6 +496,66 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
+TEST(TintVectorTest, MoveVector_Upcast_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<C0*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_Upcast_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<C0*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorTest, MoveVector_AddConst_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<const C1*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_AddConst_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<const C1*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorTest, MoveVector_UpcastAndAddConst_NoSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 2> vec_a{&c2a, &c2b};
+    Vector<const C0*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorTest, MoveVector_UpcastAndAddConst_WithSpill) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    Vector<const C0*, 2> vec_b(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
 TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N2) {
     Vector<std::string, 2> vec_a{"hello", "world"};
     Vector<std::string, 2> vec_b;
@@ -290,29 +579,6 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N2) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 2> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 2u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N2) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 2> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
 TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N1) {
     Vector<std::string, 2> vec_a{"hello", "world"};
     Vector<std::string, 1> vec_b;
@@ -336,29 +602,6 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N1) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 1> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 2u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_SpillSpill_N2_to_N1) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 1> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
 TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N3) {
     Vector<std::string, 2> vec_a{"hello", "world"};
     Vector<std::string, 3> vec_b;
@@ -382,32 +625,9 @@
     EXPECT_TRUE(AllInternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N3) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string, 3> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N3) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string, 3> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
 TEST(TintVectorTest, CopyAssign_NoSpill_N2_to_N0) {
     Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string> vec_b;
+    Vector<std::string, 0> vec_b;
     vec_b = vec_a;
     EXPECT_EQ(vec_b.Length(), 2u);
     EXPECT_EQ(vec_b.Capacity(), 2u);
@@ -418,7 +638,7 @@
 
 TEST(TintVectorTest, CopyAssign_WithSpill_N2_to_N0) {
     Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string> vec_b;
+    Vector<std::string, 0> vec_b;
     vec_b = vec_a;
     EXPECT_EQ(vec_b.Length(), 3u);
     EXPECT_EQ(vec_b.Capacity(), 3u);
@@ -428,29 +648,6 @@
     EXPECT_TRUE(AllExternallyHeld(vec_b));
 }
 
-TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N0) {
-    Vector<std::string, 2> vec_a{"hello", "world"};
-    Vector<std::string> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 2u);
-    EXPECT_EQ(vec_b.Capacity(), 2u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
-TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N0) {
-    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
-    Vector<std::string> vec_b;
-    vec_b = std::move(vec_a);
-    EXPECT_EQ(vec_b.Length(), 3u);
-    EXPECT_EQ(vec_b.Capacity(), 3u);
-    EXPECT_EQ(vec_b[0], "hello");
-    EXPECT_EQ(vec_b[1], "world");
-    EXPECT_EQ(vec_b[2], "spill");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));
-}
-
 TEST(TintVectorTest, CopyAssign_Self_NoSpill) {
     Vector<std::string, 2> vec{"hello", "world"};
     auto* vec_ptr = &vec;  // Used to avoid -Wself-assign-overloaded
@@ -473,6 +670,98 @@
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N2) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 2> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 2u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N2) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 2> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N1) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 1> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 2u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_SpillSpill_N2_to_N1) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 1> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N3) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 3> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N3) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 3> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_NoSpill_N2_to_N0) {
+    Vector<std::string, 2> vec_a{"hello", "world"};
+    Vector<std::string, 0> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 2u);
+    EXPECT_EQ(vec_b.Capacity(), 2u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
+TEST(TintVectorTest, MoveAssign_WithSpill_N2_to_N0) {
+    Vector<std::string, 2> vec_a{"hello", "world", "spill"};
+    Vector<std::string, 0> vec_b;
+    vec_b = std::move(vec_a);
+    EXPECT_EQ(vec_b.Length(), 3u);
+    EXPECT_EQ(vec_b.Capacity(), 3u);
+    EXPECT_EQ(vec_b[0], "hello");
+    EXPECT_EQ(vec_b[1], "world");
+    EXPECT_EQ(vec_b[2], "spill");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));
+}
+
 TEST(TintVectorTest, MoveAssign_Self_NoSpill) {
     Vector<std::string, 2> vec{"hello", "world"};
     auto* vec_ptr = &vec;  // Used to avoid -Wself-move
@@ -541,7 +830,7 @@
     EXPECT_EQ(vec[1], "world");
 }
 
-TEST(TintVectorTest, SmallArray_Reserve_NoSpill) {
+TEST(TintVectorTest, Reserve_NoSpill) {
     Vector<std::string, 2> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 2u);
@@ -562,7 +851,7 @@
     EXPECT_TRUE(AllInternallyHeld(vec));
 }
 
-TEST(TintVectorTest, SmallArray_Reserve_WithSpill) {
+TEST(TintVectorTest, Reserve_WithSpill) {
     Vector<std::string, 1> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 1u);
@@ -584,7 +873,7 @@
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
-TEST(TintVectorTest, SmallArray_Resize_NoSpill) {
+TEST(TintVectorTest, ResizeZero_NoSpill) {
     Vector<std::string, 2> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 2u);
@@ -614,7 +903,7 @@
     EXPECT_TRUE(AllInternallyHeld(vec));
 }
 
-TEST(TintVectorTest, SmallArray_Resize_WithSpill) {
+TEST(TintVectorTest, ResizeZero_WithSpill) {
     Vector<std::string, 1> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 1u);
@@ -644,8 +933,68 @@
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
+TEST(TintVectorTest, ResizeValue_NoSpill) {
+    Vector<std::string, 2> vec;
+    EXPECT_EQ(vec.Length(), 0u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    vec.Resize(1, "meow");
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "meow");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+    vec[0] = "hello";
+    vec.Resize(2, "woof");
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_EQ(vec[1], "woof");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+    vec[1] = "world";
+    vec.Resize(1, "quack");
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+    vec.Resize(2, "hiss");
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_EQ(vec[1], "hiss");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+}
+
+TEST(TintVectorTest, ResizeValue_WithSpill) {
+    Vector<std::string, 1> vec;
+    EXPECT_EQ(vec.Length(), 0u);
+    EXPECT_EQ(vec.Capacity(), 1u);
+    vec.Resize(1, "meow");
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 1u);
+    EXPECT_EQ(vec[0], "meow");
+    EXPECT_TRUE(AllInternallyHeld(vec));
+    vec[0] = "hello";
+    vec.Resize(2, "woof");
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_EQ(vec[1], "woof");
+    EXPECT_TRUE(AllExternallyHeld(vec));
+    vec[1] = "world";
+    vec.Resize(1, "quack");
+    EXPECT_EQ(vec.Length(), 1u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_TRUE(AllExternallyHeld(vec));
+    vec.Resize(2, "hiss");
+    EXPECT_EQ(vec.Length(), 2u);
+    EXPECT_EQ(vec.Capacity(), 2u);
+    EXPECT_EQ(vec[0], "hello");
+    EXPECT_EQ(vec[1], "hiss");
+    EXPECT_TRUE(AllExternallyHeld(vec));
+}
+
 TEST(TintVectorTest, Reserve_NoSmallArray) {
-    Vector<std::string> vec;
+    Vector<std::string, 0> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 0u);
     vec.Reserve(1);
@@ -667,7 +1016,7 @@
 }
 
 TEST(TintVectorTest, Resize_NoSmallArray) {
-    Vector<std::string> vec;
+    Vector<std::string, 0> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_EQ(vec.Capacity(), 0u);
     vec.Resize(1);
@@ -925,60 +1274,67 @@
 }
 
 TEST(TintVectorTest, PushPop_StringNoSpill) {
+    const std::string hello = "hello";
+    const std::string world = "world";
+
     Vector<std::string, 2> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("hello");
+    vec.Push(hello);
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("world");
+    vec.Push(world);
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    EXPECT_EQ(vec.Pop(), "world");
+    EXPECT_EQ(vec.Pop(), world);
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    EXPECT_EQ(vec.Pop(), "hello");
+    EXPECT_EQ(vec.Pop(), hello);
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 }
 
 TEST(TintVectorTest, PushPop_StringWithSpill) {
+    const std::string hello = "hello";
+    const std::string world = "world";
+
     Vector<std::string, 1> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("hello");
+    vec.Push(hello);
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("world");
+    vec.Push(world);
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_TRUE(AllExternallyHeld(vec));
 
-    EXPECT_EQ(vec.Pop(), "world");
+    EXPECT_EQ(vec.Pop(), world);
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllExternallyHeld(vec));
 
-    EXPECT_EQ(vec.Pop(), "hello");
+    EXPECT_EQ(vec.Pop(), hello);
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllExternallyHeld(vec));
 }
 
 TEST(TintVectorTest, PushPop_StringMoveNoSpill) {
+    std::string hello = "hello";
+    std::string world = "world";
+
     Vector<std::string, 2> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    std::string hello = "hello";
     vec.Push(std::move(hello));
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    std::string world = "world";
     vec.Push(std::move(world));
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_TRUE(AllInternallyHeld(vec));
@@ -993,15 +1349,18 @@
 }
 
 TEST(TintVectorTest, PushPop_StringMoveWithSpill) {
+    std::string hello = "hello";
+    std::string world = "world";
+
     Vector<std::string, 1> vec;
     EXPECT_EQ(vec.Length(), 0u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("hello");
+    vec.Push(std::move(hello));
     EXPECT_EQ(vec.Length(), 1u);
     EXPECT_TRUE(AllInternallyHeld(vec));
 
-    vec.Push("world");
+    vec.Push(std::move(world));
     EXPECT_EQ(vec.Length(), 2u);
     EXPECT_TRUE(AllExternallyHeld(vec));
 
@@ -1131,42 +1490,195 @@
     EXPECT_EQ(vec.end(), &vec[0] + 3);
 }
 
-TEST(TintVectorRefTest, CtorVectorNoMove) {
-    Vector<std::string, 1> vec_a{"one", "two"};
-    VectorRef<std::string> vec_ref(vec_a);  // No move
-    Vector<std::string, 2> vec_b(std::move(vec_ref));
-    EXPECT_EQ(vec_b[0], "one");
-    EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
-}
-
-TEST(TintVectorRefTest, CtorVectorMove) {
-    Vector<std::string, 1> vec_a{"one", "two"};
-    VectorRef<std::string> vec_ref(std::move(vec_a));  // Move
-    Vector<std::string, 2> vec_b(std::move(vec_ref));
-    EXPECT_EQ(vec_b[0], "one");
-    EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Move, no copy
-}
-
-TEST(TintVectorRefTest, CopyCtor) {
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorRefTest
+////////////////////////////////////////////////////////////////////////////////
+TEST(TintVectorRefTest, CopyVectorRef) {
     Vector<std::string, 1> vec_a{"one", "two"};
     VectorRef<std::string> vec_ref_a(std::move(vec_a));
     VectorRef<std::string> vec_ref_b(vec_ref_a);  // No move
     Vector<std::string, 2> vec_b(std::move(vec_ref_b));
     EXPECT_EQ(vec_b[0], "one");
     EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
 }
 
-TEST(TintVectorRefTest, MoveCtor) {
+TEST(TintVectorRefTest, CopyVectorRef_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<C0*> vec_ref_b(vec_ref_a);  // No-move. Up-cast
+    Vector<C0*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVectorRef_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<const C1*> vec_ref_b(vec_ref_a);  // No-move. Up-cast
+    Vector<const C1*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVectorRef_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<const C0*> vec_ref_b(vec_ref_a);  // No-move. Up-cast
+    Vector<const C0*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, MoveVectorRef) {
     Vector<std::string, 1> vec_a{"one", "two"};
     VectorRef<std::string> vec_ref_a(std::move(vec_a));  // Move
     VectorRef<std::string> vec_ref_b(std::move(vec_ref_a));
     Vector<std::string, 2> vec_b(std::move(vec_ref_b));
     EXPECT_EQ(vec_b[0], "one");
     EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Move, no copy
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<C0*> vec_ref_b(std::move(vec_ref_a));  // Moved. Up-cast
+    Vector<C0*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<const C1*> vec_ref_b(std::move(vec_ref_a));  // Moved. Up-cast
+    Vector<const C1*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVectorRef_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(std::move(vec_a));
+    VectorRef<const C0*> vec_ref_b(std::move(vec_ref_a));  // Moved. Up-cast
+    Vector<const C0*, 2> vec_b(std::move(vec_ref_b));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, CopyVector) {
+    Vector<std::string, 1> vec_a{"one", "two"};
+    VectorRef<std::string> vec_ref(vec_a);  // No move
+    Vector<std::string, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], "one");
+    EXPECT_EQ(vec_b[1], "two");
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C0*> vec_ref(vec_a);  // No move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<C0*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<const C1*> vec_ref(vec_a);  // No move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<const C1*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, CopyVector_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<const C0*> vec_ref(vec_a);  // No move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<const C0*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorRefTest, MoveVector) {
+    Vector<std::string, 1> vec_a{"one", "two"};
+    VectorRef<std::string> vec_ref(std::move(vec_a));  // Move
+    Vector<std::string, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], "one");
+    EXPECT_EQ(vec_b[1], "two");
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C0*> vec_ref(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<C0*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<const C1*> vec_ref(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<const C1*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
+}
+
+TEST(TintVectorRefTest, MoveVector_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<const C0*> vec_ref(std::move(vec_a));  // Move
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<const C0*, 2> vec_b(std::move(vec_ref));
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllExternallyHeld(vec_b));  // Moved, not copied
 }
 
 TEST(TintVectorRefTest, Index) {
@@ -1243,42 +1755,130 @@
     EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
 }
 
-TEST(TintVectorConstRefTest, CtorVectorNoMove) {
+////////////////////////////////////////////////////////////////////////////////
+// TintVectorConstRefTest
+////////////////////////////////////////////////////////////////////////////////
+TEST(TintVectorConstRefTest, CopyVectorConstRef) {
     Vector<std::string, 1> vec_a{"one", "two"};
-    ConstVectorRef<std::string> vec_ref(vec_a);  // No move
-    Vector<std::string, 2> vec_b(std::move(vec_ref));
+    ConstVectorRef<std::string> vec_ref_a(vec_a);
+    ConstVectorRef<std::string> vec_ref_b(vec_ref_a);
+    Vector<std::string, 2> vec_b(vec_ref_b);
     EXPECT_EQ(vec_b[0], "one");
     EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
 }
 
-TEST(TintVectorConstRefTest, CtorVectorMove) {
-    Vector<std::string, 1> vec_a{"one", "two"};
-    ConstVectorRef<std::string> vec_ref(std::move(vec_a));  // Move
-    Vector<std::string, 2> vec_b(std::move(vec_ref));
-    EXPECT_EQ(vec_b[0], "one");
-    EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
+TEST(TintVectorConstRefTest, CopyVectorConstRef_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    ConstVectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<C0*> vec_ref_b(vec_ref_a);  // Up-cast
+    Vector<C0*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
 }
 
-TEST(TintVectorConstRefTest, CopyCtor) {
-    Vector<std::string, 1> vec_a{"one", "two"};
-    ConstVectorRef<std::string> vec_ref_a(std::move(vec_a));
-    ConstVectorRef<std::string> vec_ref_b(vec_ref_a);  // No move
-    Vector<std::string, 2> vec_b(std::move(vec_ref_b));
-    EXPECT_EQ(vec_b[0], "one");
-    EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
+TEST(TintVectorConstRefTest, CopyVectorConstRef_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    ConstVectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<const C1*> vec_ref_b(vec_ref_a);  // Up-cast
+    Vector<const C1*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
 }
 
-TEST(TintVectorConstRefTest, MoveCtor) {
+TEST(TintVectorConstRefTest, CopyVectorConstRef_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    ConstVectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<const C0*> vec_ref_b(vec_ref_a);  // Up-cast
+    Vector<const C0*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector) {
     Vector<std::string, 1> vec_a{"one", "two"};
-    ConstVectorRef<std::string> vec_ref_a(std::move(vec_a));  // Move
-    ConstVectorRef<std::string> vec_ref_b(std::move(vec_ref_a));
-    Vector<std::string, 2> vec_b(std::move(vec_ref_b));
+    ConstVectorRef<std::string> vec_ref(vec_a);
+    Vector<std::string, 2> vec_b(vec_ref);
     EXPECT_EQ(vec_b[0], "one");
     EXPECT_EQ(vec_b[1], "two");
-    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copy, no move
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    ConstVectorRef<C0*> vec_ref(vec_a);
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<C0*, 2> vec_b(vec_ref);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVector_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    ConstVectorRef<const C1*> vec_ref(vec_a);
+    EXPECT_EQ(vec_ref[0], &c2a);
+    EXPECT_EQ(vec_ref[1], &c2b);
+    Vector<const C1*, 2> vec_b(vec_ref);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_Upcast) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<C0*> vec_ref_b(vec_ref_a);
+    EXPECT_EQ(vec_ref_b[0], &c2a);
+    EXPECT_EQ(vec_ref_b[1], &c2b);
+    Vector<C0*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_AddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<const C1*> vec_ref_b(vec_ref_a);
+    EXPECT_EQ(vec_ref_b[0], &c2a);
+    EXPECT_EQ(vec_ref_b[1], &c2b);
+    Vector<const C1*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
+}
+
+TEST(TintVectorConstRefTest, CopyVectorRef_UpcastAndAddConst) {
+    C2a c2a;
+    C2b c2b;
+    Vector<C1*, 1> vec_a{&c2a, &c2b};
+    VectorRef<C1*> vec_ref_a(vec_a);
+    ConstVectorRef<const C0*> vec_ref_b(vec_ref_a);
+    EXPECT_EQ(vec_ref_b[0], &c2a);
+    EXPECT_EQ(vec_ref_b[1], &c2b);
+    Vector<const C0*, 2> vec_b(vec_ref_b);
+    EXPECT_EQ(vec_b[0], &c2a);
+    EXPECT_EQ(vec_b[1], &c2b);
+    EXPECT_TRUE(AllInternallyHeld(vec_b));  // Copied, not moved
 }
 
 TEST(TintVectorConstRefTest, Index) {
@@ -1357,3 +1957,8 @@
 
 }  // namespace
 }  // namespace tint::utils
+
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C0);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C1);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C2a);
+TINT_INSTANTIATE_TYPEINFO(tint::utils::C2b);