[tint][utils] Add AlignedStorage

Move the 3 copies out to a shared definition.

Change-Id: I98626c03a56aed5602bf19c317eb114a69703a6c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/173980
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/utils/containers/hashmap_base.h b/src/tint/utils/containers/hashmap_base.h
index 2a86e3a..ba78fd0 100644
--- a/src/tint/utils/containers/hashmap_base.h
+++ b/src/tint/utils/containers/hashmap_base.h
@@ -38,6 +38,7 @@
 #include "src/tint/utils/ice/ice.h"
 #include "src/tint/utils/math/hash.h"
 #include "src/tint/utils/math/math.h"
+#include "src/tint/utils/memory/aligned_storage.h"
 #include "src/tint/utils/traits/traits.h"
 
 namespace tint {
@@ -418,21 +419,14 @@
   protected:
     /// Node holds an Entry in a linked list.
     struct Node {
-        /// A structure that has the same size and alignment as Entry.
-        /// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
-        struct alignas(alignof(ENTRY)) Storage {
-            /// Byte array of length sizeof(ENTRY)
-            uint8_t data[sizeof(ENTRY)];
-        };
-
         /// Destructs the entry.
         void Destroy() { Entry().~ENTRY(); }
 
         /// @returns the storage reinterpreted as an `Entry&`
-        ENTRY& Entry() { return *Bitcast<ENTRY*>(&storage.data[0]); }
+        ENTRY& Entry() { return storage.Get(); }
 
         /// @returns the storage reinterpreted as a `const Entry&`
-        const ENTRY& Entry() const { return *Bitcast<const ENTRY*>(&storage.data[0]); }
+        const ENTRY& Entry() const { return storage.Get(); }
 
         /// @returns a reference to the Entry's HashmapKey
         const HashmapBase::Key& Key() const { return HashmapBase::Entry::KeyOf(Entry()); }
@@ -450,7 +444,7 @@
         /// storage is a buffer that has the same size and alignment as Entry.
         /// The storage holds a constructed Entry when linked in the slots, and is destructed when
         /// removed from slots.
-        Storage storage;
+        AlignedStorage<ENTRY> storage;
 
         /// next is the next Node in the slot, or in the free list.
         Node* next;
diff --git a/src/tint/utils/containers/vector.h b/src/tint/utils/containers/vector.h
index c7edd0e..cd806c1 100644
--- a/src/tint/utils/containers/vector.h
+++ b/src/tint/utils/containers/vector.h
@@ -41,6 +41,7 @@
 #include "src/tint/utils/ice/ice.h"
 #include "src/tint/utils/macros/compiler.h"
 #include "src/tint/utils/math/hash.h"
+#include "src/tint/utils/memory/aligned_storage.h"
 #include "src/tint/utils/memory/bitcast.h"
 
 #ifndef TINT_VECTOR_MUTATION_CHECKS_ENABLED
@@ -912,26 +913,18 @@
     constexpr static bool HasSmallArray = N > 0;
 
     /// A structure that has the same size and alignment as T.
-    /// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
-    struct alignas(alignof(T)) TStorage {
-        /// @returns the storage reinterpreted as a T*
-        T* Get() { return Bitcast<T*>(&data[0]); }
-        /// @returns the storage reinterpreted as a T*
-        const T* Get() const { return Bitcast<const T*>(&data[0]); }
-        /// Byte array of length sizeof(T)
-        uint8_t data[sizeof(T)];
-    };
+    using TStorage = AlignedStorage<T>;
 
     /// The internal structure for the vector with a small array.
     struct ImplWithSmallArray {
         TStorage small_arr[N];
-        tint::Slice<T> slice = {small_arr[0].Get(), 0, N};
+        tint::Slice<T> slice = {&small_arr[0].Get(), 0, N};
 
         /// Allocates a new vector of `T` either from #small_arr, or from the heap, then assigns the
         /// pointer it to #slice.data, and updates #slice.cap.
         void Allocate(size_t new_cap) {
             if (new_cap < N) {
-                slice.data = small_arr[0].Get();
+                slice.data = &small_arr[0].Get();
                 slice.cap = N;
             } else {
                 slice.data = Bitcast<T*>(new TStorage[new_cap]);
@@ -941,14 +934,14 @@
 
         /// Frees `data`, if isn't a pointer to #small_arr
         void Free(T* data) const {
-            if (data != small_arr[0].Get()) {
+            if (data != &small_arr[0].Get()) {
                 delete[] Bitcast<TStorage*>(data);
             }
         }
 
         /// Indicates whether the slice structure can be std::move()d.
         /// @returns true if #slice.data does not point to #small_arr
-        bool CanMove() const { return slice.data != small_arr[0].Get(); }
+        bool CanMove() const { return slice.data != &small_arr[0].Get(); }
     };
 
     /// The internal structure for the vector without a small array.
diff --git a/src/tint/utils/memory/BUILD.bazel b/src/tint/utils/memory/BUILD.bazel
index 0bd13c9..6cb1b67 100644
--- a/src/tint/utils/memory/BUILD.bazel
+++ b/src/tint/utils/memory/BUILD.bazel
@@ -42,6 +42,7 @@
     "memory.cc",
   ],
   hdrs = [
+    "aligned_storage.h",
     "bitcast.h",
     "block_allocator.h",
     "bump_allocator.h",
diff --git a/src/tint/utils/memory/BUILD.cmake b/src/tint/utils/memory/BUILD.cmake
index 3151d3f..1d69077 100644
--- a/src/tint/utils/memory/BUILD.cmake
+++ b/src/tint/utils/memory/BUILD.cmake
@@ -39,6 +39,7 @@
 # Kind:      lib
 ################################################################################
 tint_add_target(tint_utils_memory lib
+  utils/memory/aligned_storage.h
   utils/memory/bitcast.h
   utils/memory/block_allocator.h
   utils/memory/bump_allocator.h
diff --git a/src/tint/utils/memory/BUILD.gn b/src/tint/utils/memory/BUILD.gn
index e5b44f2..0ab12e1 100644
--- a/src/tint/utils/memory/BUILD.gn
+++ b/src/tint/utils/memory/BUILD.gn
@@ -44,6 +44,7 @@
 
 libtint_source_set("memory") {
   sources = [
+    "aligned_storage.h",
     "bitcast.h",
     "block_allocator.h",
     "bump_allocator.h",
diff --git a/src/tint/utils/memory/aligned_storage.h b/src/tint/utils/memory/aligned_storage.h
new file mode 100644
index 0000000..c532c4f
--- /dev/null
+++ b/src/tint/utils/memory/aligned_storage.h
@@ -0,0 +1,53 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
+#define SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
+
+#include <cstddef>
+
+#include "src/tint/utils/memory/bitcast.h"
+
+namespace tint {
+
+/// A structure that has the same size and alignment as Entry.
+/// Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
+template <typename T>
+struct alignas(alignof(T)) AlignedStorage {
+    /// Byte array of length sizeof(T)
+    std::byte data[sizeof(T)];
+
+    /// @returns a pointer to aligned storage, reinterpreted as T&
+    T& Get() { return *Bitcast<T*>(&data[0]); }
+
+    /// @returns a pointer to aligned storage, reinterpreted as T&
+    const T& Get() const { return *Bitcast<const T*>(&data[0]); }
+};
+
+}  // namespace tint
+
+#endif  // SRC_TINT_UTILS_MEMORY_ALIGNED_STORAGE_H_
diff --git a/src/tint/utils/rtti/switch.h b/src/tint/utils/rtti/switch.h
index 24173d0..2436f5f 100644
--- a/src/tint/utils/rtti/switch.h
+++ b/src/tint/utils/rtti/switch.h
@@ -33,7 +33,7 @@
 
 #include "src/tint/utils/ice/ice.h"
 #include "src/tint/utils/macros/defer.h"
-#include "src/tint/utils/memory/bitcast.h"
+#include "src/tint/utils/memory/aligned_storage.h"
 #include "src/tint/utils/rtti/castable.h"
 #include "src/tint/utils/rtti/ignore.h"
 
@@ -300,13 +300,8 @@
         }
     }
 
-    // Replacement for std::aligned_storage as this is broken on earlier versions of MSVC.
-    using ReturnTypeOrU8 = std::conditional_t<kHasReturnType, ReturnType, uint8_t>;
-    struct alignas(alignof(ReturnTypeOrU8)) ReturnStorage {
-        uint8_t data[sizeof(ReturnTypeOrU8)];
-    };
-    ReturnStorage return_storage;
-    auto* result = tint::Bitcast<ReturnTypeOrU8*>(&return_storage);
+    AlignedStorage<std::conditional_t<kHasReturnType, ReturnType, uint8_t>> return_storage;
+    auto* result = &return_storage.Get();
 
     const tint::TypeInfo& type_info = object->TypeInfo();