Resolver: Check that initializers and assignments are valid

This performs very basic type verification for assignments and variable initializers.

Pulls part of the validation logic out of the Validator into the Resolver.
Involves fixing up a bunch of broken tests.

Bug: tint:642
Fixed: tint:631
Change-Id: Ifbdc139ff7eeab810856e0ba9e3c380c6555ec20
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45281
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 509bc57..324c224 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -467,6 +467,7 @@
     inspector/inspector_test.cc
     intrinsic_table_test.cc
     program_test.cc
+    resolver/assignment_validation_test.cc
     resolver/decoration_validation_test.cc
     resolver/host_shareable_validation_test.cc
     resolver/intrinsic_test.cc
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 4363815..97f9f55 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -653,7 +653,7 @@
     if (vector_type_memo_.find(std::tie(type, count)) ==
         vector_type_memo_.end()) {
       vector_type_memo_[std::tie(type, count)] =
-          create<type::Vector>(ty.u32(), count);
+          create<type::Vector>(type, count);
     }
     return vector_type_memo_[std::tie(type, count)];
   }
diff --git a/src/resolver/assignment_validation_test.cc b/src/resolver/assignment_validation_test.cc
new file mode 100644
index 0000000..ce975ee
--- /dev/null
+++ b/src/resolver/assignment_validation_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/resolver/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/resolver/resolver_test_helper.h"
+
+namespace tint {
+namespace resolver {
+namespace {
+
+using ResolverAssignmentValidationTest = ResolverTest;
+
+TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypes) {
+  // {
+  //  var a : i32 = 2;
+  //  a = 2.3;
+  // }
+
+  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+  auto* lhs = Expr("a");
+  auto* rhs = Expr(2.3f);
+
+  auto* assign = create<ast::AssignmentStatement>(Source{{12, 34}}, lhs, rhs);
+  WrapInFunction(var, assign);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')");
+}
+
+TEST_F(ResolverAssignmentValidationTest,
+       AssignThroughPointerWrongeStoreType_Fail) {
+  // var a : f32;
+  // const b : ptr<function,f32> = a;
+  // b = 2;
+  const auto priv = ast::StorageClass::kFunction;
+  auto* var_a = Var("a", ty.f32(), priv);
+  auto* var_b = Const("b", ty.pointer<float>(priv), Expr("a"), {});
+
+  auto* lhs = Expr("a");
+  auto* rhs = Expr(2);
+
+  auto* assign = create<ast::AssignmentStatement>(Source{{12, 34}}, lhs, rhs);
+  WrapInFunction(var_a, var_b, assign);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(12:34 error: invalid assignment: cannot assign value of type 'i32' to a variable of type 'f32')");
+}
+
+TEST_F(ResolverAssignmentValidationTest,
+       AssignCompatibleTypesInBlockStatement_Pass) {
+  // {
+  //  var a : i32 = 2;
+  //  a = 2
+  // }
+  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+  auto* lhs = Expr("a");
+  auto* rhs = Expr(2);
+
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::AssignmentStatement>(Source{{12, 34}}, lhs, rhs),
+  });
+  WrapInFunction(var, body);
+
+  ASSERT_TRUE(r()->Resolve());
+}
+
+TEST_F(ResolverAssignmentValidationTest,
+       AssignIncompatibleTypesInBlockStatement_Fail) {
+  // {
+  //  var a : i32 = 2;
+  //  a = 2.3;
+  // }
+
+  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+  auto* lhs = Expr("a");
+  auto* rhs = Expr(2.3f);
+
+  auto* block = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::AssignmentStatement>(Source{{12, 34}}, lhs, rhs),
+  });
+  WrapInFunction(var, block);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')");
+}
+
+TEST_F(ResolverAssignmentValidationTest,
+       AssignIncompatibleTypesInNestedBlockStatement_Fail) {
+  // {
+  //  {
+  //   var a : i32 = 2;
+  //   a = 2.3;
+  //  }
+  // }
+
+  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+  auto* lhs = Expr("a");
+  auto* rhs = Expr(2.3f);
+
+  auto* inner_block = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::AssignmentStatement>(Source{{12, 34}}, lhs, rhs),
+  });
+
+  auto* outer_block = create<ast::BlockStatement>(ast::StatementList{
+      inner_block,
+  });
+
+  WrapInFunction(var, outer_block);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(12:34 error: invalid assignment: cannot assign value of type 'f32' to a variable of type 'i32')");
+}
+
+}  // namespace
+}  // namespace resolver
+}  // namespace tint
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 9443613..d4eb5b1 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -157,6 +157,21 @@
   return false;
 }
 
+bool Resolver::IsValidAssignment(type::Type* lhs, type::Type* rhs) {
+  // TODO(crbug.com/tint/659): This is a rough approximation, and is missing
+  // checks for writability of pointer storage class, access control, etc.
+  // This will need to be fixed after WGSL agrees the behavior of pointers /
+  // references.
+  // Check:
+  if (lhs->UnwrapIfNeeded() != rhs->UnwrapIfNeeded()) {
+    // Try RHS dereference
+    if (lhs->UnwrapIfNeeded() != rhs->UnwrapAll()) {
+      return false;
+    }
+  }
+  return true;
+}
+
 bool Resolver::ResolveInternal() {
   for (auto* ty : builder_->Types()) {
     if (auto* str = ty->As<type::Struct>()) {
@@ -251,7 +266,23 @@
   ScopedAssignment<semantic::Statement*> sa(current_statement_, sem_statement);
 
   if (auto* a = stmt->As<ast::AssignmentStatement>()) {
-    return Expression(a->lhs()) && Expression(a->rhs());
+    if (!Expression(a->lhs()) || !Expression(a->rhs())) {
+      return false;
+    }
+    // TODO(crbug.com/tint/659): This logic needs updating once pointers are
+    // pinned down in the WGSL spec.
+    auto* lhs_type = TypeOf(a->lhs())->UnwrapAll();
+    auto* rhs_type = TypeOf(a->rhs());
+    if (!IsValidAssignment(lhs_type, rhs_type)) {
+      diagnostics_.add_error(
+          "invalid assignment: cannot assign value of type '" +
+              rhs_type->FriendlyName(builder_->Symbols()) +
+              "' to a variable of type '" +
+              lhs_type->FriendlyName(builder_->Symbols()) + "'",
+          stmt->source());
+      return false;
+    }
+    return true;
   }
   if (auto* b = stmt->As<ast::BlockStatement>()) {
     return BlockStatement(b);
@@ -1156,16 +1187,15 @@
     if (!Expression(ctor)) {
       return false;
     }
-    if (auto* sce = ctor->As<ast::ScalarConstructorExpression>()) {
-      auto* lhs_type = stmt->variable()->type()->UnwrapAliasIfNeeded();
-      auto* rhs_type = sce->literal()->type()->UnwrapAliasIfNeeded();
-
-      if (lhs_type != rhs_type) {
-        diagnostics_.add_error(
-            "constructor expression type does not match variable type",
-            stmt->source());
-        return false;
-      }
+    auto* lhs_type = stmt->variable()->type();
+    auto* rhs_type = TypeOf(ctor);
+    if (!IsValidAssignment(lhs_type, rhs_type)) {
+      diagnostics_.add_error(
+          "variable of type '" + lhs_type->FriendlyName(builder_->Symbols()) +
+              "' cannot be initialized with a value of type '" +
+              rhs_type->FriendlyName(builder_->Symbols()) + "'",
+          stmt->source());
+      return false;
     }
   }
 
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index ea29d2e..00102ee 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -77,6 +77,13 @@
   /// @returns true if the given type is host-shareable
   static bool IsHostShareable(type::Type* type);
 
+  /// @param lhs the assignment store type (non-pointer)
+  /// @param rhs the assignment source type (non-pointer or pointer with
+  /// auto-deref)
+  /// @returns true an expression of type `rhs` can be assigned to a variable,
+  /// structure member or array element of type `lhs`
+  static bool IsValidAssignment(type::Type* lhs, type::Type* rhs);
+
  private:
   /// Structure holding semantic information about a variable.
   /// Used to build the semantic::Variable nodes at the end of resolving.
@@ -191,7 +198,6 @@
   // AST and Type traversal methods
   // Each return true on success, false on failure.
   bool ArrayAccessor(ast::ArrayAccessorExpression*);
-  bool ValidateBinary(ast::BinaryExpression* expr);
   bool Binary(ast::BinaryExpression*);
   bool Bitcast(ast::BitcastExpression*);
   bool BlockStatement(const ast::BlockStatement*);
@@ -215,6 +221,10 @@
   bool UnaryOp(ast::UnaryOpExpression*);
   bool VariableDeclStatement(const ast::VariableDeclStatement*);
 
+  // AST and Type validation methods
+  // Each return true on success, false on failure.
+  bool ValidateBinary(ast::BinaryExpression* expr);
+
   /// @returns the semantic information for the array `arr`, building it if it
   /// hasn't been constructed already. If an error is raised, nullptr is
   /// returned.
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index bcaa8bc..664a465 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -76,77 +76,75 @@
 }
 
 TEST_F(ResolverTest, Stmt_Assign) {
-  auto* lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  WrapInFunction(assign);
+  WrapInFunction(v, assign);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
 
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
   EXPECT_EQ(StmtOf(lhs), assign);
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(ResolverTest, Stmt_Case) {
-  auto* lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      assign,
-  });
+  auto* body = Block(assign);
   ast::CaseSelectorList lit;
   lit.push_back(create<ast::SintLiteral>(ty.i32(), 3));
   auto* cse = create<ast::CaseStatement>(lit, body);
-  WrapInFunction(cse);
+  WrapInFunction(v, cse);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
   EXPECT_EQ(StmtOf(lhs), assign);
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(ResolverTest, Stmt_Block) {
-  auto* lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  auto* block = create<ast::BlockStatement>(ast::StatementList{
-      assign,
-  });
-  WrapInFunction(block);
+  auto* block = Block(assign);
+  WrapInFunction(v, block);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
   EXPECT_EQ(StmtOf(lhs), assign);
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(ResolverTest, Stmt_Else) {
-  auto* lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      assign,
-  });
+  auto* body = Block(assign);
   auto* cond = Expr(3);
   auto* stmt = create<ast::ElseStatement>(cond, body);
-  WrapInFunction(stmt);
+  WrapInFunction(v, stmt);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -154,7 +152,7 @@
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
   EXPECT_TRUE(TypeOf(stmt->condition())->Is<type::I32>());
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
   EXPECT_EQ(StmtOf(lhs), assign);
   EXPECT_EQ(StmtOf(rhs), assign);
@@ -162,25 +160,24 @@
 }
 
 TEST_F(ResolverTest, Stmt_If) {
-  auto* else_lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* else_lhs = Expr("v");
   auto* else_rhs = Expr(2.3f);
 
-  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(else_lhs, else_rhs),
-  });
+  auto* else_body = Block(create<ast::AssignmentStatement>(else_lhs, else_rhs));
 
   auto* else_cond = Expr(3);
   auto* else_stmt = create<ast::ElseStatement>(else_cond, else_body);
 
-  auto* lhs = Expr(2);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  auto* body = create<ast::BlockStatement>(ast::StatementList{assign});
+  auto* body = Block(assign);
   auto* cond = Expr(true);
   auto* stmt =
       create<ast::IfStatement>(cond, body, ast::ElseStatementList{else_stmt});
-  WrapInFunction(stmt);
+  WrapInFunction(v, stmt);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -190,9 +187,9 @@
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
   EXPECT_TRUE(TypeOf(stmt->condition())->Is<type::Bool>());
-  EXPECT_TRUE(TypeOf(else_lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(else_lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(else_rhs)->Is<type::F32>());
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
   EXPECT_EQ(StmtOf(lhs), assign);
   EXPECT_EQ(StmtOf(rhs), assign);
@@ -201,22 +198,19 @@
 }
 
 TEST_F(ResolverTest, Stmt_Loop) {
-  auto* body_lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* body_lhs = Expr("v");
   auto* body_rhs = Expr(2.3f);
 
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(body_lhs, body_rhs),
-  });
-  auto* continuing_lhs = Expr(2);
+  auto* body = Block(create<ast::AssignmentStatement>(body_lhs, body_rhs));
+  auto* continuing_lhs = Expr("v");
   auto* continuing_rhs = Expr(2.3f);
 
-  auto* continuing = create<ast::BlockStatement>(
-
-      ast::StatementList{
-          create<ast::AssignmentStatement>(continuing_lhs, continuing_rhs),
-      });
+  auto* continuing = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(continuing_lhs, continuing_rhs),
+  });
   auto* stmt = create<ast::LoopStatement>(body, continuing);
-  WrapInFunction(stmt);
+  WrapInFunction(v, stmt);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -224,9 +218,9 @@
   ASSERT_NE(TypeOf(body_rhs), nullptr);
   ASSERT_NE(TypeOf(continuing_lhs), nullptr);
   ASSERT_NE(TypeOf(continuing_rhs), nullptr);
-  EXPECT_TRUE(TypeOf(body_lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(body_lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(body_rhs)->Is<type::F32>());
-  EXPECT_TRUE(TypeOf(continuing_lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(continuing_lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(continuing_rhs)->Is<type::F32>());
 }
 
@@ -250,12 +244,11 @@
 }
 
 TEST_F(ResolverTest, Stmt_Switch) {
-  auto* lhs = Expr(2);
+  auto* v = Var("v", ty.f32(), ast::StorageClass::kFunction);
+  auto* lhs = Expr("v");
   auto* rhs = Expr(2.3f);
 
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
-  });
+  auto* body = Block(create<ast::AssignmentStatement>(lhs, rhs));
   ast::CaseSelectorList lit;
   lit.push_back(create<ast::SintLiteral>(ty.i32(), 3));
 
@@ -263,7 +256,7 @@
   cases.push_back(create<ast::CaseStatement>(lit, body));
 
   auto* stmt = create<ast::SwitchStatement>(Expr(2), cases);
-  WrapInFunction(stmt);
+  WrapInFunction(v, stmt);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -272,7 +265,7 @@
   ASSERT_NE(TypeOf(rhs), nullptr);
 
   EXPECT_TRUE(TypeOf(stmt->condition())->Is<type::I32>());
-  EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
+  EXPECT_TRUE(TypeOf(lhs)->UnwrapAll()->Is<type::F32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
 }
 
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
index 8cc4093..659eb0f 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -157,7 +157,7 @@
   EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
       r()->error(),
-      R"(3:3 error: constructor expression type does not match variable type)");
+      R"(3:3 error: variable of type 'i32' cannot be initialized with a value of type 'u32')");
 }
 
 TEST_F(ResolverValidationTest,
@@ -174,7 +174,7 @@
   EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
       r()->error(),
-      R"(3:3 error: constructor expression type does not match variable type)");
+      R"(3:3 error: variable of type 'MyInt' cannot be initialized with a value of type 'u32')");
 }
 
 TEST_F(ResolverValidationTest, Expr_Error_Unknown) {
diff --git a/src/transform/bound_array_accessors_test.cc b/src/transform/bound_array_accessors_test.cc
index 344a75e..4f82af4 100644
--- a/src/transform/bound_array_accessors_test.cc
+++ b/src/transform/bound_array_accessors_test.cc
@@ -24,22 +24,22 @@
 
 TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<storage> a : array<f32, 3>;
 
 const c : u32 = 1u;
 
 fn f() -> void {
-  const b : ptr<function, f32> = a[c];
+  const b : ptr<storage, f32> = a[c];
 }
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<storage> a : array<f32, 3>;
 
 const c : u32 = 1u;
 
 fn f() -> void {
-  const b : ptr<function, f32> = a[min(u32(c), 2u)];
+  const b : ptr<storage, f32> = a[min(u32(c), 2u)];
 }
 )";
 
diff --git a/src/transform/hlsl_test.cc b/src/transform/hlsl_test.cc
index 5f6d7d6..fb59a5d 100644
--- a/src/transform/hlsl_test.cc
+++ b/src/transform/hlsl_test.cc
@@ -30,7 +30,7 @@
   var f1 : f32 = 2.0;
   var f2 : f32 = 3.0;
   var f3 : f32 = 4.0;
-  var i : i32 = array<f32, 4>(f0, f1, f2, f3)[2];
+  var i : f32 = array<f32, 4>(f0, f1, f2, f3)[2];
 }
 )";
 
@@ -42,7 +42,7 @@
   var f2 : f32 = 3.0;
   var f3 : f32 = 4.0;
   const tint_symbol_1 : array<f32, 4> = array<f32, 4>(f0, f1, f2, f3);
-  var i : i32 = tint_symbol_1[2];
+  var i : f32 = tint_symbol_1[2];
 }
 )";
 
@@ -55,7 +55,7 @@
   auto* src = R"(
 [[stage(vertex)]]
 fn main() -> void {
-  var i : i32 = array<array<f32, 2>, 2>(array<f32, 2>(1.0, 2.0), array<f32, 2>(3.0, 4.0))[0][1];
+  var i : f32 = array<array<f32, 2>, 2>(array<f32, 2>(1.0, 2.0), array<f32, 2>(3.0, 4.0))[0][1];
 }
 )";
 
@@ -65,7 +65,7 @@
   const tint_symbol_1 : array<f32, 2> = array<f32, 2>(1.0, 2.0);
   const tint_symbol_2 : array<f32, 2> = array<f32, 2>(3.0, 4.0);
   const tint_symbol_3 : array<array<f32, 2>, 2> = array<array<f32, 2>, 2>(tint_symbol_1, tint_symbol_2);
-  var i : i32 = tint_symbol_3[0][1];
+  var i : f32 = tint_symbol_3[0][1];
 }
 )";
 
diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc
index c0b6e19..a6a1699 100644
--- a/src/transform/vertex_pulling_test.cc
+++ b/src/transform/vertex_pulling_test.cc
@@ -298,7 +298,7 @@
 TEST_F(VertexPullingTest, TwoAttributesSameBuffer) {
   auto* src = R"(
 [[location(0)]] var<in> var_a : f32;
-[[location(1)]] var<in> var_b : array<f32, 4>;
+[[location(1)]] var<in> var_b : vec4<f32>;
 
 [[stage(vertex)]]
 fn main() -> void {}
@@ -316,7 +316,7 @@
 
 var<private> var_a : f32;
 
-var<private> var_b : array<f32, 4>;
+var<private> var_b : vec4<f32>;
 
 [[stage(vertex)]]
 fn main() -> void {
@@ -346,9 +346,9 @@
 
 TEST_F(VertexPullingTest, FloatVectorAttributes) {
   auto* src = R"(
-[[location(0)]] var<in> var_a : array<f32, 2>;
-[[location(1)]] var<in> var_b : array<f32, 3>;
-[[location(2)]] var<in> var_c : array<f32, 4>;
+[[location(0)]] var<in> var_a : vec2<f32>;
+[[location(1)]] var<in> var_b : vec3<f32>;
+[[location(2)]] var<in> var_c : vec4<f32>;
 
 [[stage(vertex)]]
 fn main() -> void {}
@@ -368,11 +368,11 @@
   _tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-var<private> var_a : array<f32, 2>;
+var<private> var_a : vec2<f32>;
 
-var<private> var_b : array<f32, 3>;
+var<private> var_b : vec3<f32>;
 
-var<private> var_c : array<f32, 4>;
+var<private> var_c : vec4<f32>;
 
 [[stage(vertex)]]
 fn main() -> void {
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index eaf131a..25d4da0 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -484,16 +484,7 @@
     return false;
   }
   auto* lhs_result_type = program_->Sem().Get(lhs)->Type()->UnwrapIfNeeded();
-  if (auto* lhs_reference_type = As<type::Pointer>(lhs_result_type)) {
-    auto* lhs_store_type = lhs_reference_type->type()->UnwrapIfNeeded();
-    if (lhs_store_type != rhs_result_type) {
-      add_error(assign->source(), "v-000x",
-                "invalid assignment: can't assign value of type '" +
-                    rhs_result_type->type_name() + "' to '" +
-                    lhs_store_type->type_name() + "'");
-      return false;
-    }
-  } else {
+  if (!Is<type::Pointer>(lhs_result_type)) {
     if (!ValidateBadAssignmentToIdentifier(assign)) {
       return false;
     }
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index cc39b22..245a261 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -139,157 +139,6 @@
   EXPECT_TRUE(v.ValidateAssign(assign)) << v.error();
 }
 
-TEST_F(ValidatorTest, AssignIncompatibleTypes_Fail) {
-  // {
-  //  var a :i32 = 2;
-  //  a = 2.3;
-  // }
-
-  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
-  RegisterVariable(var);
-
-  auto* lhs = Expr("a");
-  auto* rhs = Expr(2.3f);
-
-  auto* assign = create<ast::AssignmentStatement>(
-      Source{Source::Location{12, 34}}, lhs, rhs);
-  WrapInFunction(assign);
-
-  ValidatorImpl& v = Build();
-
-  ASSERT_NE(TypeOf(lhs), nullptr);
-  ASSERT_NE(TypeOf(rhs), nullptr);
-
-  EXPECT_FALSE(v.ValidateAssign(assign));
-  ASSERT_TRUE(v.has_error());
-  // TODO(sarahM0): figure out what should be the error number.
-  EXPECT_EQ(v.error(),
-            "12:34 v-000x: invalid assignment: can't assign value of type "
-            "'__f32' to '__i32'");
-}
-
-TEST_F(ValidatorTest, AssignThroughPointerWrongeStoreType_Fail) {
-  // var a :f32;
-  // const b : ptr<function,f32> = a;
-  // b = 2;
-  const auto priv = ast::StorageClass::kFunction;
-  auto* var_a = Var("a", ty.f32(), priv, Expr(2), {});
-  auto* var_b = Const("b", ty.pointer<float>(priv), Expr("a"), {});
-  RegisterVariable(var_a);
-  RegisterVariable(var_b);
-
-  auto* lhs = Expr("a");
-  auto* rhs = Expr(2);
-
-  auto* assign = create<ast::AssignmentStatement>(
-      Source{Source::Location{12, 34}}, lhs, rhs);
-  WrapInFunction(assign);
-
-  ValidatorImpl& v = Build();
-
-  ASSERT_NE(TypeOf(lhs), nullptr);
-  ASSERT_NE(TypeOf(rhs), nullptr);
-
-  EXPECT_FALSE(v.ValidateAssign(assign));
-  EXPECT_EQ(v.error(),
-            "12:34 v-000x: invalid assignment: can't assign value of type "
-            "'__i32' to '__f32'");
-}
-
-TEST_F(ValidatorTest, AssignCompatibleTypesInBlockStatement_Pass) {
-  // {
-  //  var a :i32 = 2;
-  //  a = 2
-  // }
-  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
-
-  auto* lhs = Expr("a");
-  auto* rhs = Expr(2);
-
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(var),
-      create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
-                                       rhs),
-  });
-  WrapInFunction(body);
-
-  ValidatorImpl& v = Build();
-
-  ASSERT_NE(TypeOf(lhs), nullptr);
-  ASSERT_NE(TypeOf(rhs), nullptr);
-
-  EXPECT_TRUE(v.ValidateStatements(body)) << v.error();
-}
-
-TEST_F(ValidatorTest, AssignIncompatibleTypesInBlockStatement_Fail) {
-  // {
-  //  var a :i32 = 2;
-  //  a = 2.3;
-  // }
-
-  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
-
-  auto* lhs = Expr("a");
-  auto* rhs = Expr(2.3f);
-
-  auto* block = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(var),
-      create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
-                                       rhs),
-  });
-  WrapInFunction(block);
-
-  ValidatorImpl& v = Build();
-
-  ASSERT_NE(TypeOf(lhs), nullptr);
-  ASSERT_NE(TypeOf(rhs), nullptr);
-
-  EXPECT_FALSE(v.ValidateStatements(block));
-  ASSERT_TRUE(v.has_error());
-  // TODO(sarahM0): figure out what should be the error number.
-  EXPECT_EQ(v.error(),
-            "12:34 v-000x: invalid assignment: can't assign value of type "
-            "'__f32' to '__i32'");
-}
-
-TEST_F(ValidatorTest, AssignIncompatibleTypesInNestedBlockStatement_Fail) {
-  // {
-  //  {
-  //   var a :i32 = 2;
-  //   a = 2.3;
-  //  }
-  // }
-
-  auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
-
-  auto* lhs = Expr("a");
-  auto* rhs = Expr(2.3f);
-
-  auto* inner_block = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(var),
-      create<ast::AssignmentStatement>(Source{Source::Location{12, 34}}, lhs,
-                                       rhs),
-  });
-
-  auto* outer_block = create<ast::BlockStatement>(ast::StatementList{
-      inner_block,
-  });
-
-  WrapInFunction(outer_block);
-
-  ValidatorImpl& v = Build();
-
-  ASSERT_NE(TypeOf(lhs), nullptr);
-  ASSERT_NE(TypeOf(rhs), nullptr);
-
-  EXPECT_FALSE(v.ValidateStatements(outer_block));
-  ASSERT_TRUE(v.has_error());
-  // TODO(sarahM0): figure out what should be the error number.
-  EXPECT_EQ(v.error(),
-            "12:34 v-000x: invalid assignment: can't assign value of type "
-            "'__f32' to '__i32'");
-}
-
 TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
   // var<in> global_var: f32;
   auto* var = Global(Source{Source::Location{12, 34}}, "global_var", ty.f32(),
diff --git a/src/writer/spirv/builder_function_variable_test.cc b/src/writer/spirv/builder_function_variable_test.cc
index fc9cd26..18c1227 100644
--- a/src/writer/spirv/builder_function_variable_test.cc
+++ b/src/writer/spirv/builder_function_variable_test.cc
@@ -172,7 +172,7 @@
 TEST_F(BuilderTest, FunctionVar_Const) {
   auto* init = vec3<f32>(1.f, 1.f, 3.f);
 
-  auto* v = Const("var", ty.f32(), init);
+  auto* v = Const("var", ty.vec3<f32>(), init);
 
   WrapInFunction(v);
 
diff --git a/src/writer/spirv/builder_ident_expression_test.cc b/src/writer/spirv/builder_ident_expression_test.cc
index bacfd58..55f2a12 100644
--- a/src/writer/spirv/builder_ident_expression_test.cc
+++ b/src/writer/spirv/builder_ident_expression_test.cc
@@ -69,7 +69,7 @@
 TEST_F(BuilderTest, IdentifierExpression_FunctionConst) {
   auto* init = vec3<f32>(1.f, 1.f, 3.f);
 
-  auto* v = Const("var", ty.f32(), init);
+  auto* v = Const("var", ty.vec3<f32>(), init);
 
   auto* expr = Expr("var");
   WrapInFunction(v, expr);
diff --git a/test/BUILD.gn b/test/BUILD.gn
index f60bdc0..15508c4 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -169,6 +169,7 @@
     "../src/intrinsic_table_test.cc",
     "../src/program_builder_test.cc",
     "../src/program_test.cc",
+    "../src/resolver/assignment_validation_test.cc",
     "../src/resolver/decoration_validation_test.cc",
     "../src/resolver/host_shareable_validation_test.cc",
     "../src/resolver/intrinsic_test.cc",