sem: Add constructor field to sem::Variable

Produces a direct SEM -> SEM pointer, reducing AST <-> SEM hopping.

Change-Id: I233b4c47d4e55b5f2c6e14ed08699a302b8fb64d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71321
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 5d4575d..e490815 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -413,6 +413,8 @@
         }
       }
 
+      global->SetConstructor(rhs);
+
       builder_->Sem().Add(var, global);
       return global;
     }
@@ -421,6 +423,7 @@
           var, var_ty, storage_class, access, current_statement_,
           (rhs && var->is_const) ? rhs->ConstantValue() : sem::Constant{});
       builder_->Sem().Add(var, local);
+      local->SetConstructor(rhs);
       return local;
     }
     case VariableKind::kParameter: {
diff --git a/src/resolver/var_let_test.cc b/src/resolver/var_let_test.cc
index 57d7a14..631537b 100644
--- a/src/resolver/var_let_test.cc
+++ b/src/resolver/var_let_test.cc
@@ -26,7 +26,7 @@
 struct ResolverVarLetTest : public resolver::TestHelper,
                             public testing::Test {};
 
-TEST_F(ResolverVarLetTest, TypeOfVar) {
+TEST_F(ResolverVarLetTest, VarDeclWithoutConstructor) {
   // struct S { i : i32; }
   // alias A = S;
   // fn F(){
@@ -74,9 +74,80 @@
   EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
   EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
   EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+
+  EXPECT_EQ(Sem().Get(i)->Constructor(), nullptr);
+  EXPECT_EQ(Sem().Get(u)->Constructor(), nullptr);
+  EXPECT_EQ(Sem().Get(f)->Constructor(), nullptr);
+  EXPECT_EQ(Sem().Get(b)->Constructor(), nullptr);
+  EXPECT_EQ(Sem().Get(s)->Constructor(), nullptr);
+  EXPECT_EQ(Sem().Get(a)->Constructor(), nullptr);
 }
 
-TEST_F(ResolverVarLetTest, TypeOfLet) {
+TEST_F(ResolverVarLetTest, VarDeclWithConstructor) {
+  // struct S { i : i32; }
+  // alias A = S;
+  // fn F(){
+  //   var i : i32 = 1;
+  //   var u : u32 = 1u;
+  //   var f : f32 = 1.f;
+  //   var b : bool = true;
+  //   var s : S = S(1);
+  //   var a : A = A(1);
+  // }
+
+  auto* S = Structure("S", {Member("i", ty.i32())});
+  auto* A = Alias("A", ty.Of(S));
+
+  auto* i_c = Expr(1);
+  auto* u_c = Expr(1u);
+  auto* f_c = Expr(1.f);
+  auto* b_c = Expr(true);
+  auto* s_c = Construct(ty.Of(S), Expr(1));
+  auto* a_c = Construct(ty.Of(A), Expr(1));
+
+  auto* i = Var("i", ty.i32(), ast::StorageClass::kNone, i_c);
+  auto* u = Var("u", ty.u32(), ast::StorageClass::kNone, u_c);
+  auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, f_c);
+  auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone, b_c);
+  auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
+  auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
+
+  Func("F", {}, ty.void_(),
+       {
+           Decl(i),
+           Decl(u),
+           Decl(f),
+           Decl(b),
+           Decl(s),
+           Decl(a),
+       });
+
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+  // `var` declarations are always of reference type
+  ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
+  ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
+  ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
+  ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
+  ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
+  ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
+
+  EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+  EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
+  EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
+  EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
+  EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+  EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+
+  EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+  EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+  EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+  EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+  EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+  EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+}
+
+TEST_F(ResolverVarLetTest, LetDecl) {
   // struct S { i : i32; }
   // fn F(){
   //   var v : i32;
@@ -86,21 +157,28 @@
   //   let b : bool = true;
   //   let s : S = S(1);
   //   let a : A = A(1);
-  //   let p : pointer<function, i32> = &V;
+  //   let p : pointer<function, i32> = &v;
   // }
 
   auto* S = Structure("S", {Member("i", ty.i32())});
   auto* A = Alias("A", ty.Of(S));
-
   auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
-  auto* i = Const("i", ty.i32(), Expr(1));
-  auto* u = Const("u", ty.u32(), Expr(1u));
-  auto* f = Const("f", ty.f32(), Expr(1.f));
-  auto* b = Const("b", ty.bool_(), Expr(true));
-  auto* s = Const("s", ty.Of(S), Construct(ty.Of(S), Expr(1)));
-  auto* a = Const("a", ty.Of(A), Construct(ty.Of(A), Expr(1)));
-  auto* p =
-      Const("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
+
+  auto* i_c = Expr(1);
+  auto* u_c = Expr(1u);
+  auto* f_c = Expr(1.f);
+  auto* b_c = Expr(true);
+  auto* s_c = Construct(ty.Of(S), Expr(1));
+  auto* a_c = Construct(ty.Of(A), Expr(1));
+  auto* p_c = AddressOf(v);
+
+  auto* i = Const("i", ty.i32(), i_c);
+  auto* u = Const("u", ty.u32(), u_c);
+  auto* f = Const("f", ty.f32(), f_c);
+  auto* b = Const("b", ty.bool_(), b_c);
+  auto* s = Const("s", ty.Of(S), s_c);
+  auto* a = Const("a", ty.Of(A), a_c);
+  auto* p = Const("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
 
   Func("F", {}, ty.void_(),
        {
@@ -117,14 +195,22 @@
   ASSERT_TRUE(r()->Resolve()) << r()->error();
 
   // `let` declarations are always of the storage type
-  EXPECT_TRUE(TypeOf(i)->Is<sem::I32>());
-  EXPECT_TRUE(TypeOf(u)->Is<sem::U32>());
-  EXPECT_TRUE(TypeOf(f)->Is<sem::F32>());
-  EXPECT_TRUE(TypeOf(b)->Is<sem::Bool>());
-  EXPECT_TRUE(TypeOf(s)->Is<sem::Struct>());
-  EXPECT_TRUE(TypeOf(a)->Is<sem::Struct>());
+  ASSERT_TRUE(TypeOf(i)->Is<sem::I32>());
+  ASSERT_TRUE(TypeOf(u)->Is<sem::U32>());
+  ASSERT_TRUE(TypeOf(f)->Is<sem::F32>());
+  ASSERT_TRUE(TypeOf(b)->Is<sem::Bool>());
+  ASSERT_TRUE(TypeOf(s)->Is<sem::Struct>());
+  ASSERT_TRUE(TypeOf(a)->Is<sem::Struct>());
   ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
-  EXPECT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
+  ASSERT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
+
+  EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+  EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+  EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+  EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+  EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+  EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+  EXPECT_EQ(Sem().Get(p)->Constructor()->Declaration(), p_c);
 }
 
 TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
diff --git a/src/sem/variable.h b/src/sem/variable.h
index ac7dac1..1f66ac7 100644
--- a/src/sem/variable.h
+++ b/src/sem/variable.h
@@ -72,6 +72,16 @@
   /// @return the constant value of this expression
   const Constant& ConstantValue() const { return constant_value_; }
 
+  /// @returns the variable constructor expression, or nullptr if the variable
+  /// does not have one.
+  const Expression* Constructor() const { return constructor_; }
+
+  /// Sets the variable constructor expression.
+  /// @param constructor the constructor expression to assign to this variable.
+  void SetConstructor(const Expression* constructor) {
+    constructor_ = constructor;
+  }
+
   /// @returns the expressions that use the variable
   const std::vector<const VariableUser*>& Users() const { return users_; }
 
@@ -81,9 +91,10 @@
  private:
   const ast::Variable* const declaration_;
   const sem::Type* const type_;
-  ast::StorageClass const storage_class_;
-  ast::Access const access_;
+  const ast::StorageClass storage_class_;
+  const ast::Access access_;
   const Constant constant_value_;
+  const Expression* constructor_ = nullptr;
   std::vector<const VariableUser*> users_;
 };