[validation] v-0021: cannot re-assign constants

Bug: tint: 6
Change-Id: Ib5cd57478b35c8dbf04136eb4167a3c3bce1c954
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/26420
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/validator_impl.cc b/src/validator_impl.cc
index ae7bbef..da7908c 100644
--- a/src/validator_impl.cc
+++ b/src/validator_impl.cc
@@ -96,6 +96,9 @@
   if (!a) {
     return false;
   }
+  if (!(ValidateConstant(a))) {
+    return false;
+  }
   if (!(ValidateExpression(a->lhs()) && ValidateExpression(a->rhs()))) {
     return false;
   }
@@ -106,6 +109,25 @@
   return true;
 }
 
+bool ValidatorImpl::ValidateConstant(const ast::AssignmentStatement* assign) {
+  if (!assign) {
+    return false;
+  }
+
+  if (assign->lhs()->IsIdentifier()) {
+    ast::Variable* var;
+    auto* ident = assign->lhs()->AsIdentifier();
+    if (variable_stack_.get(ident->name(), &var)) {
+      if (var->is_const()) {
+        set_error(assign->source(), "v-0021: cannot re-assign a constant: '" +
+                                        ident->name() + "'");
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 bool ValidatorImpl::ValidateResultTypes(const ast::AssignmentStatement* a) {
   if (!a->lhs()->result_type() || !a->rhs()->result_type()) {
     set_error(a->source(), "result_type() is nullptr");
diff --git a/src/validator_impl.h b/src/validator_impl.h
index 1fa0205..d7a7fc3 100644
--- a/src/validator_impl.h
+++ b/src/validator_impl.h
@@ -66,9 +66,9 @@
   /// @returns true if the validation was successful
   bool ValidateStatement(const ast::Statement* stmt);
   /// Validates an assignment
-  /// @param a the assignment to check
+  /// @param assign the assignment to check
   /// @returns true if the validation was successful
-  bool ValidateAssign(const ast::AssignmentStatement* a);
+  bool ValidateAssign(const ast::AssignmentStatement* assign);
   /// Validates v-0001: Only allowed import is "GLSL.std.450"
   /// @param module the modele to check imports
   /// @returns ture if input complies with v-0001 rule
@@ -82,9 +82,13 @@
   /// @return true if idnet was defined
   bool ValidateIdentifier(const ast::IdentifierExpression* ident);
   /// Validates if the input follows type checking rules
-  /// @param 'a' the assignment to check
+  /// @param assign the assignment to check
   /// @returns ture if successful
-  bool ValidateResultTypes(const ast::AssignmentStatement* a);
+  bool ValidateResultTypes(const ast::AssignmentStatement* assign);
+  /// Validate v-0021: Cannot re-assign a constant
+  /// @param assign is the assigment to check if its lhs is a const
+  /// @returns false if lhs of assign is a constant identifier
+  bool ValidateConstant(const ast::AssignmentStatement* assign);
 
  private:
   std::string error_;
diff --git a/src/validator_test.cc b/src/validator_test.cc
index fe485fc..cb5d6db 100644
--- a/src/validator_test.cc
+++ b/src/validator_test.cc
@@ -413,27 +413,36 @@
   EXPECT_TRUE(v.ValidateStatements(outer_body.get())) << v.error();
 }
 
-TEST_F(ValidatorTest, DISABLED_AssignToConstant_Fail) {
-  // v-0021: Cannot re-assign a constant.
-  // const a :i32 = 1;
-  // a = 2;
+TEST_F(ValidatorTest, AssignToConstant_Fail) {
+  // {
+  //  const a :i32 = 2;
+  //  a = 2
+  // }
   ast::type::I32Type i32;
+  auto var =
+      std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &i32);
+  var->set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+  var->set_is_const(true);
 
-  ast::Variable var("a", ast::StorageClass::kPrivate, &i32);
-  var.set_constructor(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  var.set_is_const(true);
   auto lhs = std::make_unique<ast::IdentifierExpression>("a");
-
+  auto* lhs_ptr = lhs.get();
   auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 2));
+  auto* rhs_ptr = rhs.get();
 
-  ast::AssignmentStatement assign(std::move(lhs), std::move(rhs));
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
+  body->append(std::make_unique<ast::AssignmentStatement>(
+      Source{12, 34}, std::move(lhs), std::move(rhs)));
+
+  EXPECT_TRUE(td()->DetermineStatements(body.get())) << td()->error();
+  ASSERT_NE(lhs_ptr->result_type(), nullptr);
+  ASSERT_NE(rhs_ptr->result_type(), nullptr);
 
   tint::ValidatorImpl v;
-  // TODO(SarahM0): Invalidate assignments to a constant.
-  ASSERT_TRUE(v.has_error());
-  EXPECT_EQ(v.error(), "2:1: v-0021: cannot re-assign a constant");
+  EXPECT_FALSE(v.ValidateStatements(body.get()));
+  EXPECT_EQ(v.error(), "12:34: v-0021: cannot re-assign a constant: 'a'");
 }
 
 }  // namespace