[tint][core] Don't create throw-away StructMembers

When constructing a builtin-struct check whether the builtin has been created already before building a new struct (just to drop it).

Bug: tint:2129
Change-Id: Ic76718dc4efd41167e832767225f5280f491c5d9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/171805
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/type/builtin_structs.cc b/src/tint/lang/core/type/builtin_structs.cc
index 8f319d5..31af873 100644
--- a/src/tint/lang/core/type/builtin_structs.cc
+++ b/src/tint/lang/core/type/builtin_structs.cc
@@ -70,7 +70,11 @@
 
 Struct* CreateModfResult(Manager& types, SymbolTable& symbols, const Type* ty) {
     auto build = [&](core::BuiltinType name, const Type* t) {
-        return types.Struct(symbols.Register(tint::ToString(name)),
+        auto symbol = symbols.Register(tint::ToString(name));
+        if (auto* existing = types.Find<type::Struct>(symbol)) {
+            return existing;
+        }
+        return types.Struct(symbol,
                             {{symbols.Register("fract"), t}, {symbols.Register("whole"), t}});
     };
     return Switch(
@@ -127,9 +131,12 @@
 
 Struct* CreateFrexpResult(Manager& types, SymbolTable& symbols, const Type* ty) {
     auto build = [&](core::BuiltinType name, const Type* fract_ty, const Type* exp_ty) {
+        auto symbol = symbols.Register(tint::ToString(name));
+        if (auto* existing = types.Find<type::Struct>(symbol)) {
+            return existing;
+        }
         return types.Struct(
-            symbols.Register(tint::ToString(name)),
-            {{symbols.Register("fract"), fract_ty}, {symbols.Register("exp"), exp_ty}});
+            symbol, {{symbols.Register("fract"), fract_ty}, {symbols.Register("exp"), exp_ty}});
     };
     return Switch(
         ty,  //
@@ -172,11 +179,14 @@
 
 Struct* CreateAtomicCompareExchangeResult(Manager& types, SymbolTable& symbols, const Type* ty) {
     auto build = [&](core::BuiltinType name) {
-        return types.Struct(symbols.Register(tint::ToString(name)),
-                            {
-                                {symbols.Register("old_value"), ty},
-                                {symbols.Register("exchanged"), types.bool_()},
-                            });
+        auto symbol = symbols.Register(tint::ToString(name));
+        if (auto* existing = types.Find<type::Struct>(symbol)) {
+            return existing;
+        }
+        return types.Struct(symbol, {
+                                        {symbols.Register("old_value"), ty},
+                                        {symbols.Register("exchanged"), types.bool_()},
+                                    });
     };
     return Switch(
         ty,  //
diff --git a/src/tint/lang/core/type/manager.cc b/src/tint/lang/core/type/manager.cc
index ff70390..f158770 100644
--- a/src/tint/lang/core/type/manager.cc
+++ b/src/tint/lang/core/type/manager.cc
@@ -42,6 +42,7 @@
 #include "src/tint/lang/core/type/u32.h"
 #include "src/tint/lang/core/type/vector.h"
 #include "src/tint/lang/core/type/void.h"
+#include "src/tint/utils/macros/compiler.h"
 
 namespace tint::core::type {
 
@@ -198,6 +199,11 @@
 }
 
 core::type::Struct* Manager::Struct(Symbol name, VectorRef<const StructMember*> members) {
+    if (auto* existing = Find<type::Struct>(name); TINT_UNLIKELY(existing)) {
+        TINT_ICE() << "attempting to construct two structs named " << name.NameView();
+        return existing;
+    }
+
     uint32_t max_align = 0u;
     for (const auto& m : members) {
         max_align = std::max(max_align, m->Align());
@@ -208,6 +214,11 @@
 }
 
 core::type::Struct* Manager::Struct(Symbol name, VectorRef<StructMemberDesc> md) {
+    if (auto* existing = Find<type::Struct>(name); TINT_UNLIKELY(existing)) {
+        TINT_ICE() << "attempting to construct two structs named " << name.NameView();
+        return existing;
+    }
+
     tint::Vector<const StructMember*, 4> members;
     uint32_t current_size = 0u;
     uint32_t max_align = 0u;
diff --git a/src/tint/lang/core/type/manager.h b/src/tint/lang/core/type/manager.h
index 6c12a6c..f02290b 100644
--- a/src/tint/lang/core/type/manager.h
+++ b/src/tint/lang/core/type/manager.h
@@ -475,18 +475,21 @@
     /// Create a new structure declaration.
     /// @param name the name of the structure
     /// @param members the list of structure members
+    /// @note a structure must not already exist with the same name
     /// @returns the structure type
     core::type::Struct* Struct(Symbol name, VectorRef<const StructMember*> members);
 
     /// Create a new structure declaration.
     /// @param name the name of the structure
     /// @param members the list of structure member descriptors
+    /// @note a structure must not already exist with the same name
     /// @returns the structure type
     core::type::Struct* Struct(Symbol name, VectorRef<StructMemberDesc> members);
 
     /// Create a new structure declaration.
     /// @param name the name of the structure
     /// @param members the list of structure member descriptors
+    /// @note a structure must not already exist with the same name
     /// @returns the structure type
     core::type::Struct* Struct(Symbol name, std::initializer_list<StructMemberDesc> members) {
         return Struct(name, tint::Vector<StructMemberDesc, 4>(members));
diff --git a/src/tint/lang/core/type/struct.cc b/src/tint/lang/core/type/struct.cc
index c916e4b..ff08e35 100644
--- a/src/tint/lang/core/type/struct.cc
+++ b/src/tint/lang/core/type/struct.cc
@@ -43,8 +43,8 @@
 namespace tint::core::type {
 namespace {
 
-core::type::Flags FlagsFrom(VectorRef<const StructMember*> members) {
-    core::type::Flags flags{
+Flags FlagsFrom(VectorRef<const StructMember*> members) {
+    Flags flags{
         Flag::kConstructable,
         Flag::kCreationFixedFootprint,
         Flag::kFixedFootprint,
@@ -65,6 +65,14 @@
 
 }  // namespace
 
+Struct::Struct(Symbol name)
+    : Base(Hash(tint::TypeInfo::Of<Struct>().full_hashcode, name), type::Flags{}),
+      name_(name),
+      members_{},
+      align_(0),
+      size_(0),
+      size_no_padding_(0) {}
+
 Struct::Struct(Symbol name,
                VectorRef<const StructMember*> members,
                uint32_t align,
diff --git a/src/tint/lang/core/type/struct.h b/src/tint/lang/core/type/struct.h
index 228c8b8..eec7dab 100644
--- a/src/tint/lang/core/type/struct.h
+++ b/src/tint/lang/core/type/struct.h
@@ -72,6 +72,12 @@
 class Struct : public Castable<Struct, Type> {
   public:
     /// Constructor
+    /// Note: this constructs an empty structure, which should only be used find a struct with the
+    /// same name in a type::Manager.
+    /// @param name the name of the structure
+    explicit Struct(Symbol name);
+
+    /// Constructor
     /// @param name the name of the structure
     /// @param members the structure members
     /// @param align the byte alignment of the structure