[tint][utils] Vector improvements

* Move elements instead of copying them when std::move()ing a vector
  that has not spilled to the heap. This also means you can move a
  vector when the elements have no copy constructor.
* Fix the const Vector& -> VectorRef constructor.

Change-Id: I3455f6fab91551f172bb6b6d7595cdd5cc67490d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161002
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/utils/containers/vector.h b/src/tint/utils/containers/vector.h
index deae6fe..eefb3ab 100644
--- a/src/tint/utils/containers/vector.h
+++ b/src/tint/utils/containers/vector.h
@@ -343,7 +343,7 @@
 
     /// Move constructor
     /// @param other the vector to move
-    Vector(Vector&& other) { MoveOrCopy(VectorRef<T>(std::move(other))); }
+    Vector(Vector&& other) { Move(std::move(other)); }
 
     /// Copy constructor (differing N length)
     /// @param other the vector to copy
@@ -356,7 +356,7 @@
     /// @param other the vector to move
     template <size_t N2>
     Vector(Vector<T, N2>&& other) {
-        MoveOrCopy(VectorRef<T>(std::move(other)));
+        Move(std::move(other));
     }
 
     /// Copy constructor with covariance / const conversion
@@ -378,7 +378,7 @@
               ReinterpretMode MODE,
               typename = std::enable_if_t<CanReinterpretSlice<MODE, T, U>>>
     Vector(Vector<U, N2>&& other) {  // NOLINT(runtime/explicit)
-        MoveOrCopy(VectorRef<T>(std::move(other)));
+        Move(std::move(other));
     }
 
     /// Move constructor from a mutable vector reference
@@ -391,7 +391,16 @@
 
     /// Copy constructor from an immutable slice
     /// @param other the slice to copy
-    Vector(const Slice<T>& other) { Copy(other); }  // NOLINT(runtime/explicit)
+    Vector(const Slice<T>& other) {  // NOLINT(runtime/explicit)
+        Copy(other);
+    }
+
+    /// Copy constructor from an immutable slice
+    /// @param other the slice to copy
+    template <typename U>
+    Vector(const Slice<U>& other) {  // NOLINT(runtime/explicit)
+        Copy(other);
+    }
 
     /// Destructor
     ~Vector() { ClearAndFree(); }
@@ -411,7 +420,7 @@
     /// @returns this vector so calls can be chained
     Vector& operator=(Vector&& other) {
         if (&other != this) {
-            MoveOrCopy(VectorRef<T>(std::move(other)));
+            Move(std::move(other));
         }
         return *this;
     }
@@ -430,7 +439,7 @@
     /// @returns this vector so calls can be chained
     template <size_t N2>
     Vector& operator=(Vector<T, N2>&& other) {
-        MoveOrCopy(VectorRef<T>(std::move(other)));
+        Move(std::move(other));
         return *this;
     }
 
@@ -824,6 +833,7 @@
     /// Moves 'other' to this vector, if possible, otherwise performs a copy.
     void MoveOrCopy(VectorRef<T>&& other) {
         if (other.can_move_) {
+            // Just steal the slice.
             ClearAndFree();
             impl_.slice = other.slice_;
             other.slice_ = {};
@@ -833,8 +843,9 @@
     }
 
     /// Copies all the elements from `other` to this vector, replacing the content of this vector.
-    /// @param other the
-    void Copy(const tint::Slice<T>& other) {
+    /// @param other the slice to copy
+    template <typename U>
+    void Copy(const tint::Slice<U>& other) {
         if (impl_.slice.cap < other.len) {
             ClearAndFree();
             impl_.Allocate(other.len);
@@ -848,6 +859,41 @@
         }
     }
 
+    /// Moves all the elements from `other` to this vector, replacing the content of this vector.
+    /// @param other the vector to move
+    template <typename U, size_t N2>
+    void Move(Vector<U, N2>&& other) {
+        auto& other_slice = other.impl_.slice;
+        if constexpr (std::is_same_v<T, U>) {
+            if (other.impl_.CanMove()) {
+                // Just steal the slice.
+                ClearAndFree();
+                impl_.slice = other_slice;
+                other_slice = {};
+                return;
+            }
+        }
+
+        // Can't steal the slice, so we have to move the elements instead.
+
+        // Ensure we have capacity for all the elements
+        if (impl_.slice.cap < other_slice.len) {
+            ClearAndFree();
+            impl_.Allocate(other_slice.len);
+        } else {
+            Clear();
+        }
+
+        // Move each of the elements.
+        impl_.slice.len = other_slice.len;
+        for (size_t i = 0; i < impl_.slice.len; i++) {
+            new (&impl_.slice.data[i]) T{std::move(other_slice.data[i])};
+        }
+
+        // Clear other
+        other.Clear();
+    }
+
     /// Clears the vector, then frees the slice data.
     void ClearAndFree() {
         Clear();
@@ -1049,8 +1095,8 @@
     template <typename U,
               size_t N,
               typename = std::enable_if_t<CanReinterpretSlice<ReinterpretMode::kSafe, T, U>>>
-    VectorRef(Vector<U, N>& vector)  // NOLINT(runtime/explicit)
-        : slice_(vector.impl_.slice.template Reinterpret<T>()) {}
+    VectorRef(const Vector<U, N>& vector)  // NOLINT(runtime/explicit)
+        : slice_(const_cast<tint::Slice<U>&>(vector.impl_.slice).template Reinterpret<T>()) {}
 
     /// Constructor from a moved Vector with covariance / const conversion
     /// @param vector the vector to create a reference of