tint/resolver: Clean up 'let' / 'override' resolving

Split the logic out of Resolver::Variable().

Primes the resolver for the introduction of 'const'

Bug: tint:1580
Change-Id: Id67280ed5c8c73a69c62728fb5a81a08f13628a8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93785
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 8d3cfdd..dcd49fb 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -312,6 +312,20 @@
 }
 
 sem::Variable* Resolver::Variable(const ast::Variable* v, bool is_global) {
+    return Switch(
+        v,  //
+        [&](const ast::Var* var) { return Var(var, is_global); },
+        [&](const ast::Let* let) { return Let(let, is_global); },
+        [&](const ast::Override* override) { return Override(override); },
+        [&](Default) {
+            TINT_ICE(Resolver, diagnostics_)
+                << "Resolver::GlobalVariable() called with a unknown variable type: "
+                << v->TypeInfo().name;
+            return nullptr;
+        });
+}
+
+sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) {
     const sem::Type* ty = nullptr;
 
     // If the variable has a declared type, resolve it.
@@ -322,8 +336,58 @@
         }
     }
 
-    auto* as_let = v->As<ast::Let>();
-    auto* as_override = v->As<ast::Override>();
+    if (!v->constructor) {
+        AddError("'let' declaration must have an initializer", v->source);
+        return nullptr;
+    }
+
+    auto* rhs = Materialize(Expression(v->constructor), ty);
+    if (!rhs) {
+        return nullptr;
+    }
+
+    // If the variable has no declared type, infer it from the RHS
+    if (!ty) {
+        ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
+    }
+
+    if (rhs &&
+        !validator_.VariableConstructorOrCast(v, ast::StorageClass::kNone, ty, rhs->Type())) {
+        return nullptr;
+    }
+
+    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+                                      v->source)) {
+        AddNote("while instantiating 'let' " + builder_->Symbols().NameFor(v->symbol), v->source);
+        return nullptr;
+    }
+
+    sem::Variable* sem = nullptr;
+    if (is_global) {
+        sem = builder_->create<sem::GlobalVariable>(
+            v, ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+            rhs ? rhs->ConstantValue() : sem::Constant{}, sem::BindingPoint{});
+    } else {
+        sem = builder_->create<sem::LocalVariable>(v, ty, ast::StorageClass::kNone,
+                                                   ast::Access::kUndefined, current_statement_,
+                                                   rhs ? rhs->ConstantValue() : sem::Constant{});
+    }
+
+    sem->SetConstructor(rhs);
+    builder_->Sem().Add(v, sem);
+    return sem;
+}
+
+sem::Variable* Resolver::Override(const ast::Override* v) {
+    const sem::Type* ty = nullptr;
+
+    // If the variable has a declared type, resolve it.
+    if (v->type) {
+        ty = Type(v->type);
+        if (!ty) {
+            return nullptr;
+        }
+    }
 
     const sem::Expression* rhs = nullptr;
 
@@ -338,9 +402,6 @@
         if (!ty) {
             ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
         }
-    } else if (as_let) {
-        AddError("'let' declaration must have an initializer", v->source);
-        return nullptr;
     } else if (!ty) {
         AddError("'override' declaration requires a type or initializer", v->source);
         return nullptr;
@@ -353,28 +414,17 @@
 
     if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
                                       v->source)) {
-        AddNote("while instantiating variable " + builder_->Symbols().NameFor(v->symbol),
+        AddNote("while instantiating 'override' " + builder_->Symbols().NameFor(v->symbol),
                 v->source);
         return nullptr;
     }
 
-    sem::Variable* sem = nullptr;
-    if (is_global) {
-        bool has_const_val = rhs && !as_override;
-        auto* global = builder_->create<sem::GlobalVariable>(
-            v, ty, ast::StorageClass::kNone, ast::Access::kUndefined,
-            has_const_val ? rhs->ConstantValue() : sem::Constant{}, sem::BindingPoint{});
+    auto* sem = builder_->create<sem::GlobalVariable>(v, ty, ast::StorageClass::kNone,
+                                                      ast::Access::kUndefined, sem::Constant{},
+                                                      sem::BindingPoint{});
 
-        if (as_override) {
-            if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
-                global->SetConstantId(static_cast<uint16_t>(id->value));
-            }
-        }
-        sem = global;
-    } else {
-        sem = builder_->create<sem::LocalVariable>(
-            v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, current_statement_,
-            (rhs && as_let) ? rhs->ConstantValue() : sem::Constant{});
+    if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
+        sem->SetConstantId(static_cast<uint16_t>(id->value));
     }
 
     sem->SetConstructor(rhs);
@@ -567,10 +617,7 @@
 }
 
 sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
-    auto* as_var = v->As<ast::Var>();
-
-    auto* sem = As<sem::GlobalVariable>(as_var ? Var(as_var, /* is_global */ true)
-                                               : Variable(v, /* is_global */ true));
+    auto* sem = As<sem::GlobalVariable>(Variable(v, /* is_global */ true));
     if (!sem) {
         return nullptr;
     }
@@ -2477,10 +2524,7 @@
     return StatementScope(stmt, sem, [&] {
         Mark(stmt->variable);
 
-        auto* variable = Switch(
-            stmt->variable,  //
-            [&](const ast::Var* var) { return Var(var, /* is_global */ false); },
-            [&](Default) { return Variable(stmt->variable, /* is_global */ false); });
+        auto* variable = Variable(stmt->variable, /* is_global */ false);
         if (!variable) {
             return false;
         }
@@ -2501,10 +2545,7 @@
             sem->Behaviors() = ctor->Behaviors();
         }
 
-        return Switch(
-            stmt->variable,  //
-            [&](const ast::Var*) { return validator_.Var(variable); },
-            [&](Default) { return validator_.Variable(variable); });
+        return validator_.Variable(variable);
     });
 }