tint: Refactor sem::Constant to be less memory-hungry

Change sem::Constant to be an interface to the constant data. Implement
this so that zero-initialized data doesn't need to allocate the full
size of the type.

This also makes usage a lot cleaner (no more flattened-list of
elements!), and gives us a clear path for supporting constant
structures if/when we want to support them.

Bug: chromium:1339558
Bug: chromium:1339561
Bug: chromium:1339580
Bug: chromium:1339597
Change-Id: Ifcd456f69aee18d5b84befa896d7b0189d68c2dd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94942
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index e6cad77..9ef9a30 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -365,13 +365,13 @@
 
     sem::Variable* sem = nullptr;
     if (is_global) {
-        sem = builder_->create<sem::GlobalVariable>(v, ty, ast::StorageClass::kNone,
-                                                    ast::Access::kUndefined, sem::Constant{},
-                                                    sem::BindingPoint{});
+        sem = builder_->create<sem::GlobalVariable>(
+            v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
+            sem::BindingPoint{});
     } else {
         sem = builder_->create<sem::LocalVariable>(v, ty, ast::StorageClass::kNone,
                                                    ast::Access::kUndefined, current_statement_,
-                                                   sem::Constant{});
+                                                   /* constant_value */ nullptr);
     }
 
     sem->SetConstructor(rhs);
@@ -419,9 +419,9 @@
         return nullptr;
     }
 
-    auto* sem = builder_->create<sem::GlobalVariable>(v, ty, ast::StorageClass::kNone,
-                                                      ast::Access::kUndefined, sem::Constant{},
-                                                      sem::BindingPoint{});
+    auto* sem = builder_->create<sem::GlobalVariable>(
+        v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
+        sem::BindingPoint{});
 
     if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
         sem->SetConstantId(static_cast<uint16_t>(id->value));
@@ -564,11 +564,11 @@
             binding_point = {bp.group->value, bp.binding->value};
         }
         sem = builder_->create<sem::GlobalVariable>(var, var_ty, storage_class, access,
-                                                    sem::Constant{}, binding_point);
+                                                    /* constant_value */ nullptr, binding_point);
 
     } else {
-        sem = builder_->create<sem::LocalVariable>(var, var_ty, storage_class, access,
-                                                   current_statement_, sem::Constant{});
+        sem = builder_->create<sem::LocalVariable>(
+            var, var_ty, storage_class, access, current_statement_, /* constant_value */ nullptr);
     }
 
     sem->SetConstructor(rhs);
@@ -916,7 +916,7 @@
             return false;
         }
 
-        sem::Constant value;
+        const sem::Constant* value = nullptr;
 
         if (auto* user = args[i]->As<sem::VariableUser>()) {
             // We have an variable of a module-scope constant.
@@ -950,12 +950,12 @@
             continue;
         }
         // validator_.Validate and set the default value for this dimension.
-        if (value.Element<AInt>(0).value < 1) {
+        if (value->As<AInt>() < 1) {
             AddError("workgroup_size argument must be at least 1", values[i]->source);
             return false;
         }
 
-        ws[i].value = value.Element<uint32_t>(0);
+        ws[i].value = value->As<uint32_t>();
     }
 
     current_function_->SetWorkgroupSize(std::move(ws));
@@ -1266,7 +1266,8 @@
             [&](const ast::UnaryOpExpression* unary) -> sem::Expression* { return UnaryOp(unary); },
             [&](const ast::PhonyExpression*) -> sem::Expression* {
                 return builder_->create<sem::Expression>(expr, builder_->create<sem::Void>(),
-                                                         current_statement_, sem::Constant{},
+                                                         current_statement_,
+                                                         /* constant_value */ nullptr,
                                                          /* has_side_effects */ false);
             },
             [&](Default) {
@@ -1309,13 +1310,14 @@
                 << ") returned invalid value";
             return nullptr;
         }
-        auto materialized_val = ConvertValue(std::move(expr_val), target_ty, decl->source);
+        auto materialized_val = ConvertValue(expr_val, target_ty, decl->source);
         if (!materialized_val) {
+            // ConvertValue() has already failed and raised an diagnostic error.
             return nullptr;
         }
-        if (!materialized_val->IsValid()) {
+        if (!materialized_val.Get()) {
             TINT_ICE(Resolver, builder_->Diagnostics())
-                << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val.Type())
+                << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val->Type())
                 << " -> " << builder_->FriendlyName(target_ty) << ") returned invalid value";
             return nullptr;
         }
@@ -1678,9 +1680,9 @@
     }
 
     // If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
-    sem::Constant constant;
+    const sem::Constant* constant = nullptr;
     if (builtin.const_eval_fn) {
-        std::vector<sem::Constant> values(args.size());
+        std::vector<const sem::Constant*> values(args.size());
         bool is_const = true;  // all arguments have constant values
         for (size_t i = 0; i < values.size(); i++) {
             if (auto v = args[i]->ConstantValue()) {
@@ -1757,7 +1759,7 @@
     // effects.
     bool has_side_effects = true;
     auto* call = builder_->create<sem::Call>(expr, target, std::move(args), current_statement_,
-                                             sem::Constant{}, has_side_effects);
+                                             /* constant_value */ nullptr, has_side_effects);
 
     target->AddCallSite(call);
 
@@ -2226,21 +2228,21 @@
             return nullptr;
         }
 
-        auto count_val = count_sem->ConstantValue();
+        auto* count_val = count_sem->ConstantValue();
         if (!count_val) {
             AddError("array size must evaluate to a constant integer expression",
                      count_expr->source);
             return nullptr;
         }
 
-        if (auto* ty = count_val.Type(); !ty->is_integer_scalar()) {
+        if (auto* ty = count_val->Type(); !ty->is_integer_scalar()) {
             AddError("array size must evaluate to a constant integer expression, but is type '" +
                          builder_->FriendlyName(ty) + "'",
                      count_expr->source);
             return nullptr;
         }
 
-        count = count_val.Element<AInt>(0).value;
+        count = count_val->As<AInt>();
         if (count < 1) {
             AddError("array size (" + std::to_string(count) + ") must be greater than 0",
                      count_expr->source);