Add semantic::Statement

Add Stmt() accessor on all semantic::Expressions so the owning statement can be retrieved.

Change-Id: I5d584335a6d137fdeab0b8d74a161fcae9b46080
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41545
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 4d79363..03c2274 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -391,6 +391,7 @@
     "src/semantic/sem_intrinsic.cc",
     "src/semantic/sem_member_accessor_expression.cc",
     "src/semantic/sem_node.cc",
+    "src/semantic/sem_statement.cc",
     "src/semantic/sem_variable.cc",
     "src/semantic/type_mappings.h",
     "src/source.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e4b81d4..a42d8e3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -205,6 +205,7 @@
   semantic/sem_info.cc
   semantic/sem_intrinsic.cc
   semantic/sem_node.cc
+  semantic/sem_statement.cc
   semantic/sem_variable.cc
   semantic/type_mappings.h
   source.cc
diff --git a/src/semantic/call.h b/src/semantic/call.h
index 0ef874f..a4931fb 100644
--- a/src/semantic/call.h
+++ b/src/semantic/call.h
@@ -27,7 +27,8 @@
  public:
   /// Constructor
   /// @param target the call target
-  explicit Call(const CallTarget* target);
+  /// @param statement the statement that owns this expression
+  explicit Call(const CallTarget* target, Statement* statement);
 
   /// Destructor
   ~Call() override;
diff --git a/src/semantic/expression.h b/src/semantic/expression.h
index 76052be..57ed61c 100644
--- a/src/semantic/expression.h
+++ b/src/semantic/expression.h
@@ -20,10 +20,11 @@
 namespace tint {
 
 // Forward declarations
+namespace semantic {
+class Statement;
+}  // namespace semantic
 namespace type {
-
 class Type;
-
 }  // namespace type
 
 namespace semantic {
@@ -33,13 +34,18 @@
  public:
   /// Constructor
   /// @param type the resolved type of the expression
-  explicit Expression(type::Type* type);
+  /// @param statement the statement that owns this expression
+  explicit Expression(type::Type* type, Statement* statement);
 
   /// @return the resolved type of the expression
   type::Type* Type() const { return type_; }
 
+  /// @return the statement that owns this expression
+  Statement* Stmt() const { return statement_; }
+
  private:
   type::Type* const type_;
+  Statement* const statement_;
 };
 
 }  // namespace semantic
diff --git a/src/semantic/member_accessor_expression.h b/src/semantic/member_accessor_expression.h
index 7b21f97..f46fe6e 100644
--- a/src/semantic/member_accessor_expression.h
+++ b/src/semantic/member_accessor_expression.h
@@ -27,8 +27,11 @@
  public:
   /// Constructor
   /// @param type the resolved type of the expression
+  /// @param statement the statement that owns this expression
   /// @param is_swizzle true if this member access is for a vector swizzle
-  MemberAccessorExpression(type::Type* type, bool is_swizzle);
+  MemberAccessorExpression(type::Type* type,
+                           Statement* statement,
+                           bool is_swizzle);
 
   /// @return true if this member access is for a vector swizzle
   bool IsSwizzle() const { return is_swizzle_; }
diff --git a/src/semantic/sem_call.cc b/src/semantic/sem_call.cc
index 632f3a0..8f0a41f 100644
--- a/src/semantic/sem_call.cc
+++ b/src/semantic/sem_call.cc
@@ -19,8 +19,8 @@
 namespace tint {
 namespace semantic {
 
-Call::Call(const CallTarget* target)
-    : Base(target->ReturnType()), target_(target) {}
+Call::Call(const CallTarget* target, Statement* statement)
+    : Base(target->ReturnType(), statement), target_(target) {}
 
 Call::~Call() = default;
 
diff --git a/src/semantic/sem_expression.cc b/src/semantic/sem_expression.cc
index 52d13f7..36cc8c4 100644
--- a/src/semantic/sem_expression.cc
+++ b/src/semantic/sem_expression.cc
@@ -21,7 +21,8 @@
 namespace tint {
 namespace semantic {
 
-Expression::Expression(type::Type* type) : type_(type->UnwrapIfNeeded()) {}
+Expression::Expression(type::Type* type, Statement* statement)
+    : type_(type->UnwrapIfNeeded()), statement_(statement) {}
 
 }  // namespace semantic
 }  // namespace tint
diff --git a/src/semantic/sem_member_accessor_expression.cc b/src/semantic/sem_member_accessor_expression.cc
index 1145a23..e85c7bf 100644
--- a/src/semantic/sem_member_accessor_expression.cc
+++ b/src/semantic/sem_member_accessor_expression.cc
@@ -20,8 +20,9 @@
 namespace semantic {
 
 MemberAccessorExpression::MemberAccessorExpression(type::Type* type,
+                                                   Statement* statement,
                                                    bool is_swizzle)
-    : Base(type), is_swizzle_(is_swizzle) {}
+    : Base(type, statement), is_swizzle_(is_swizzle) {}
 
 }  // namespace semantic
 }  // namespace tint
diff --git a/src/semantic/sem_statement.cc b/src/semantic/sem_statement.cc
new file mode 100644
index 0000000..af25872
--- /dev/null
+++ b/src/semantic/sem_statement.cc
@@ -0,0 +1,27 @@
+// 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/semantic/statement.h"
+
+#include "src/type/type.h"
+
+TINT_INSTANTIATE_CLASS_ID(tint::semantic::Statement);
+
+namespace tint {
+namespace semantic {
+
+Statement::Statement(ast::Statement* declaration) : declaration_(declaration) {}
+
+}  // namespace semantic
+}  // namespace tint
diff --git a/src/semantic/statement.h b/src/semantic/statement.h
new file mode 100644
index 0000000..0782bcb
--- /dev/null
+++ b/src/semantic/statement.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef SRC_SEMANTIC_STATEMENT_H_
+#define SRC_SEMANTIC_STATEMENT_H_
+
+#include "src/semantic/node.h"
+
+namespace tint {
+
+// Forward declarations
+namespace ast {
+class Statement;
+}  // namespace ast
+
+namespace semantic {
+
+/// Statement holds the semantic information for a statement.
+class Statement : public Castable<Statement, Node> {
+ public:
+  /// Constructor
+  /// @param declaration the AST node for this statement
+  explicit Statement(ast::Statement* declaration);
+
+  /// @return the AST node for this statement
+  ast::Statement* Declaration() const { return declaration_; }
+
+ private:
+  ast::Statement* const declaration_;
+};
+
+}  // namespace semantic
+}  // namespace tint
+
+#endif  // SRC_SEMANTIC_STATEMENT_H_
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index a127caa..1bb2d4c 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -47,6 +47,7 @@
 #include "src/semantic/function.h"
 #include "src/semantic/intrinsic.h"
 #include "src/semantic/member_accessor_expression.h"
+#include "src/semantic/statement.h"
 #include "src/semantic/variable.h"
 #include "src/type/array_type.h"
 #include "src/type/bool_type.h"
@@ -69,6 +70,23 @@
 
 using IntrinsicType = tint::semantic::IntrinsicType;
 
+// Helper class that temporarily assigns a value to a reference for the scope of
+// the object. Once the ScopedAssignment is destructed, the original value is
+// restored.
+template <typename T>
+class ScopedAssignment {
+ public:
+  ScopedAssignment(T& ref, T val) : ref_(ref) {
+    old_value_ = ref;
+    ref = val;
+  }
+  ~ScopedAssignment() { ref_ = old_value_; }
+
+ private:
+  T& ref_;
+  T old_value_;
+};
+
 }  // namespace
 
 TypeDeterminer::TypeDeterminer(ProgramBuilder* builder)
@@ -171,9 +189,11 @@
 }
 
 bool TypeDeterminer::DetermineFunction(ast::Function* func) {
-  current_function_ = function_infos_.Create<FunctionInfo>(func);
-  symbol_to_function_[func->symbol()] = current_function_;
-  function_to_info_.emplace(func, current_function_);
+  auto* func_info = function_infos_.Create<FunctionInfo>(func);
+  symbol_to_function_[func->symbol()] = func_info;
+  function_to_info_.emplace(func, func_info);
+
+  ScopedAssignment<FunctionInfo*> sa(current_function_, func_info);
 
   variable_stack_.push_scope();
   for (auto* param : func->params()) {
@@ -185,8 +205,6 @@
   }
   variable_stack_.pop_scope();
 
-  current_function_ = nullptr;
-
   return true;
 }
 
@@ -234,6 +252,10 @@
 }
 
 bool TypeDeterminer::DetermineResultType(ast::Statement* stmt) {
+  auto* sem_statement = builder_->create<semantic::Statement>(stmt);
+
+  ScopedAssignment<semantic::Statement*> sa(current_statement_, sem_statement);
+
   if (auto* a = stmt->As<ast::AssignmentStatement>()) {
     return DetermineResultType(a->lhs()) && DetermineResultType(a->rhs());
   }
@@ -451,7 +473,8 @@
     }
 
     auto* function = iter->second;
-    function_calls_.emplace(call, function);
+    function_calls_.emplace(call,
+                            FunctionCallInfo{function, current_statement_});
     SetType(call, function->declaration->return_type());
   }
 
@@ -501,12 +524,14 @@
     }
     auto* intrinsic = builder_->create<semantic::Intrinsic>(intrinsic_type,
                                                             ret_ty, parameters);
-    builder_->Sem().Add(call, builder_->create<semantic::Call>(intrinsic));
+    builder_->Sem().Add(
+        call, builder_->create<semantic::Call>(intrinsic, current_statement_));
     SetType(call, ret_ty);
     return false;
   }
 
-  builder_->Sem().Add(call, builder_->create<semantic::Call>(result.intrinsic));
+  builder_->Sem().Add(call, builder_->create<semantic::Call>(
+                                result.intrinsic, current_statement_));
   SetType(call, result.intrinsic->ReturnType());
   return true;
 }
@@ -791,9 +816,9 @@
     return false;
   }
 
-  builder_->Sem().Add(
-      expr,
-      builder_->create<semantic::MemberAccessorExpression>(ret, is_swizzle));
+  builder_->Sem().Add(expr,
+                      builder_->create<semantic::MemberAccessorExpression>(
+                          ret, current_statement_, is_swizzle));
   SetType(expr, ret);
 
   return true;
@@ -890,16 +915,16 @@
 }
 
 type::Type* TypeDeterminer::TypeOf(ast::Expression* expr) {
-  auto it = expr_types_.find(expr);
-  if (it != expr_types_.end()) {
-    return it->second;
+  auto it = expr_info_.find(expr);
+  if (it != expr_info_.end()) {
+    return it->second.type;
   }
   return nullptr;
 }
 
 void TypeDeterminer::SetType(ast::Expression* expr, type::Type* type) {
-  assert(expr_types_.count(expr) == 0);
-  expr_types_.emplace(expr, type);
+  assert(expr_info_.count(expr) == 0);
+  expr_info_.emplace(expr, ExpressionInfo{type, current_statement_});
 }
 
 void TypeDeterminer::CreateSemanticNodes() const {
@@ -938,20 +963,21 @@
   // Create semantic nodes for all ast::CallExpressions
   for (auto it : function_calls_) {
     auto* call = it.first;
-    auto* func_info = it.second;
-    auto* sem_func = func_info_to_sem_func.at(func_info);
-    sem.Add(call, builder_->create<semantic::Call>(sem_func));
+    auto info = it.second;
+    auto* sem_func = func_info_to_sem_func.at(info.function);
+    sem.Add(call, builder_->create<semantic::Call>(sem_func, info.statement));
   }
 
   // Create semantic nodes for all remaining expression types
-  for (auto it : expr_types_) {
+  for (auto it : expr_info_) {
     auto* expr = it.first;
-    auto* type = it.second;
+    auto& info = it.second;
     if (sem.Get(expr)) {
       // Expression has already been assigned a semantic node
       continue;
     }
-    sem.Add(expr, builder_->create<semantic::Expression>(type));
+    sem.Add(expr,
+            builder_->create<semantic::Expression>(info.type, info.statement));
   }
 }
 
diff --git a/src/type_determiner.h b/src/type_determiner.h
index bd30a91..7f8ecc2 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -30,8 +30,9 @@
 #include "src/type/storage_texture_type.h"
 
 namespace tint {
-namespace ast {
 
+// Forward declarations
+namespace ast {
 class ArrayAccessorExpression;
 class BinaryExpression;
 class BitcastExpression;
@@ -42,8 +43,10 @@
 class MemberAccessorExpression;
 class UnaryOpExpression;
 class Variable;
-
 }  // namespace ast
+namespace semantic {
+class Statement;
+}  // namespace semantic
 
 /// Determines types for all items in the given tint program
 class TypeDeterminer {
@@ -95,7 +98,7 @@
     std::unordered_set<T> set;
   };
 
-  /// Structure holding semantic information about a function.
+  /// Structure holding semantic information about a variable.
   /// Used to build the semantic::Function nodes at the end of resolving.
   struct VariableInfo {
     explicit VariableInfo(ast::Variable* decl);
@@ -117,6 +120,21 @@
     UniqueVector<Symbol> ancestor_entry_points;
   };
 
+  /// Structure holding semantic information about an expression.
+  /// Used to build the semantic::Expression nodes at the end of resolving.
+  struct ExpressionInfo {
+    type::Type* type;
+    semantic::Statement* statement;
+  };
+
+  /// Structure holding semantic information about a call expression to an
+  /// ast::Function.
+  /// Used to build the semantic::Call nodes at the end of resolving.
+  struct FunctionCallInfo {
+    FunctionInfo* function;
+    semantic::Statement* statement;
+  };
+
   /// Determines type information for the program, without creating final the
   /// semantic nodes.
   /// @returns true if the determination was successful
@@ -203,9 +221,10 @@
   std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
   std::unordered_map<ast::Function*, FunctionInfo*> function_to_info_;
   std::unordered_map<ast::Variable*, VariableInfo*> variable_to_info_;
-  std::unordered_map<ast::CallExpression*, FunctionInfo*> function_calls_;
-  std::unordered_map<ast::Expression*, type::Type*> expr_types_;
+  std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
+  std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
   FunctionInfo* current_function_ = nullptr;
+  semantic::Statement* current_statement_ = nullptr;
   BlockAllocator<VariableInfo> variable_infos_;
   BlockAllocator<FunctionInfo> function_infos_;
 
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 8dc1575..9f871ba 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -55,6 +55,7 @@
 #include "src/semantic/call.h"
 #include "src/semantic/expression.h"
 #include "src/semantic/function.h"
+#include "src/semantic/statement.h"
 #include "src/semantic/variable.h"
 #include "src/type/access_control_type.h"
 #include "src/type/alias_type.h"
@@ -105,6 +106,11 @@
 
   TypeDeterminer* td() const { return td_.get(); }
 
+  ast::Statement* StmtOf(ast::Expression* expr) {
+    auto* sem_stmt = Sem().Get(expr)->Stmt();
+    return sem_stmt ? sem_stmt->Declaration() : nullptr;
+  }
+
  private:
   std::unique_ptr<TypeDeterminer> td_;
 };
@@ -149,14 +155,17 @@
 
   EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(lhs), assign);
+  EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_Case) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
   auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
+      assign,
   });
   ast::CaseSelectorList lit;
   lit.push_back(create<ast::SintLiteral>(ty.i32(), 3));
@@ -169,14 +178,17 @@
   ASSERT_NE(TypeOf(rhs), nullptr);
   EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(lhs), assign);
+  EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_Block) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
   auto* block = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
+      assign,
   });
   WrapInFunction(block);
 
@@ -186,16 +198,20 @@
   ASSERT_NE(TypeOf(rhs), nullptr);
   EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(lhs), assign);
+  EXPECT_EQ(StmtOf(rhs), assign);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_Else) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
   auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
+      assign,
   });
-  auto* stmt = create<ast::ElseStatement>(Expr(3), body);
+  auto* cond = Expr(3);
+  auto* stmt = create<ast::ElseStatement>(cond, body);
   WrapInFunction(stmt);
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
@@ -206,6 +222,9 @@
   EXPECT_TRUE(TypeOf(stmt->condition())->Is<type::I32>());
   EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(lhs), assign);
+  EXPECT_EQ(StmtOf(rhs), assign);
+  EXPECT_EQ(StmtOf(cond), stmt);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_If) {
@@ -216,16 +235,17 @@
       create<ast::AssignmentStatement>(else_lhs, else_rhs),
   });
 
-  auto* else_stmt = create<ast::ElseStatement>(Expr(3), else_body);
+  auto* else_cond = Expr(3);
+  auto* else_stmt = create<ast::ElseStatement>(else_cond, else_body);
 
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
-  });
-  auto* stmt = create<ast::IfStatement>(Expr(3), body,
-                                        ast::ElseStatementList{else_stmt});
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
+  auto* body = create<ast::BlockStatement>(ast::StatementList{assign});
+  auto* cond = Expr(3);
+  auto* stmt =
+      create<ast::IfStatement>(cond, body, ast::ElseStatementList{else_stmt});
   WrapInFunction(stmt);
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
@@ -240,6 +260,10 @@
   EXPECT_TRUE(TypeOf(else_rhs)->Is<type::F32>());
   EXPECT_TRUE(TypeOf(lhs)->Is<type::I32>());
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(lhs), assign);
+  EXPECT_EQ(StmtOf(rhs), assign);
+  EXPECT_EQ(StmtOf(cond), stmt);
+  EXPECT_EQ(StmtOf(else_cond), else_stmt);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_Loop) {
@@ -332,6 +356,7 @@
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(expr), call);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_Call_undeclared) {
@@ -376,14 +401,15 @@
 }
 
 TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScope) {
-  auto* var = Global("my_var", ast::StorageClass::kNone, ty.i32(), Expr(2),
-                     ast::VariableDecorationList{});
-  auto* init = var->constructor();
+  auto* init = Expr(2);
+  Global("my_var", ast::StorageClass::kNone, ty.i32(), init,
+         ast::VariableDecorationList{});
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
 
   ASSERT_NE(TypeOf(init), nullptr);
   EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
+  EXPECT_EQ(StmtOf(init), nullptr);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
@@ -438,6 +464,10 @@
   EXPECT_TRUE(TypeOf(bar_i32_init)->UnwrapAll()->Is<type::I32>());
   ASSERT_NE(TypeOf(bar_f32_init), nullptr);
   EXPECT_TRUE(TypeOf(bar_f32_init)->UnwrapAll()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(foo_i32_init), foo_i32_decl);
+  EXPECT_EQ(StmtOf(bar_i32_init), bar_i32_decl);
+  EXPECT_EQ(StmtOf(foo_f32_init), foo_f32_decl);
+  EXPECT_EQ(StmtOf(bar_f32_init), bar_f32_decl);
 }
 
 TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
@@ -480,6 +510,9 @@
   EXPECT_TRUE(TypeOf(fn_i32_init)->Is<type::I32>());
   ASSERT_NE(TypeOf(fn_f32_init), nullptr);
   EXPECT_TRUE(TypeOf(fn_f32_init)->UnwrapAll()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(fn_i32_init), fn_i32_decl);
+  EXPECT_EQ(StmtOf(mod_init), nullptr);
+  EXPECT_EQ(StmtOf(fn_f32_init), fn_f32_decl);
 }
 
 TEST_F(TypeDeterminerTest, Expr_Error_Unknown) {
@@ -708,59 +741,77 @@
 }
 
 TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable_Const) {
-  auto* my_var = Expr("my_var");
-
+  auto* my_var_a = Expr("my_var");
+  auto* my_var_b = Expr("my_var");
   auto* var = Const("my_var", ast::StorageClass::kNone, ty.f32());
+  auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
 
   Func("my_func", ast::VariableList{}, ty.f32(),
        ast::StatementList{
            create<ast::VariableDeclStatement>(var),
-           create<ast::AssignmentStatement>(my_var, Expr("my_var")),
+           assign,
        },
        ast::FunctionDecorationList{});
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
 
-  ASSERT_NE(TypeOf(my_var), nullptr);
-  EXPECT_TRUE(TypeOf(my_var)->Is<type::F32>());
+  ASSERT_NE(TypeOf(my_var_a), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_a)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_a), assign);
+  ASSERT_NE(TypeOf(my_var_b), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_b)->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_b), assign);
 }
 
 TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable) {
-  auto* my_var = Expr("my_var");
+  auto* my_var_a = Expr("my_var");
+  auto* my_var_b = Expr("my_var");
+  auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
 
   Func("my_func", ast::VariableList{}, ty.f32(),
        ast::StatementList{
            create<ast::VariableDeclStatement>(
                Var("my_var", ast::StorageClass::kNone, ty.f32())),
-           create<ast::AssignmentStatement>(my_var, Expr("my_var")),
+           assign,
        },
        ast::FunctionDecorationList{});
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
 
-  ASSERT_NE(TypeOf(my_var), nullptr);
-  EXPECT_TRUE(TypeOf(my_var)->Is<type::Pointer>());
-  EXPECT_TRUE(TypeOf(my_var)->As<type::Pointer>()->type()->Is<type::F32>());
+  ASSERT_NE(TypeOf(my_var_a), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
+  EXPECT_TRUE(TypeOf(my_var_a)->As<type::Pointer>()->type()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_a), assign);
+  ASSERT_NE(TypeOf(my_var_b), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_b)->Is<type::Pointer>());
+  EXPECT_TRUE(TypeOf(my_var_b)->As<type::Pointer>()->type()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_b), assign);
 }
 
 TEST_F(TypeDeterminerTest, Expr_Identifier_Function_Ptr) {
-  type::Pointer ptr(ty.f32(), ast::StorageClass::kFunction);
-
-  auto* my_var = Expr("my_var");
+  auto* my_var_a = Expr("my_var");
+  auto* my_var_b = Expr("my_var");
+  auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
 
   Func("my_func", ast::VariableList{}, ty.f32(),
        ast::StatementList{
            create<ast::VariableDeclStatement>(
-               Var("my_var", ast::StorageClass::kNone, &ptr)),
-           create<ast::AssignmentStatement>(my_var, Expr("my_var")),
+               Var("my_var", ast::StorageClass::kNone,
+                   ty.pointer<f32>(ast::StorageClass::kFunction))),
+           assign,
        },
        ast::FunctionDecorationList{});
 
   EXPECT_TRUE(td()->Determine()) << td()->error();
 
-  ASSERT_NE(TypeOf(my_var), nullptr);
-  EXPECT_TRUE(TypeOf(my_var)->Is<type::Pointer>());
-  EXPECT_TRUE(TypeOf(my_var)->As<type::Pointer>()->type()->Is<type::F32>());
+  ASSERT_NE(TypeOf(my_var_a), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
+  EXPECT_TRUE(TypeOf(my_var_a)->As<type::Pointer>()->type()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_a), assign);
+  ASSERT_NE(TypeOf(my_var_b), nullptr);
+  EXPECT_TRUE(TypeOf(my_var_b)->Is<type::Pointer>());
+  EXPECT_TRUE(TypeOf(my_var_b)->As<type::Pointer>()->type()->Is<type::F32>());
+  EXPECT_EQ(StmtOf(my_var_b), assign);
 }
 
 TEST_F(TypeDeterminerTest, Expr_Call_Function) {
diff --git a/src/writer/append_vector.cc b/src/writer/append_vector.cc
index 5b62095..3e3d841 100644
--- a/src/writer/append_vector.cc
+++ b/src/writer/append_vector.cc
@@ -52,9 +52,12 @@
     packed_el_ty = vector_sem->Type();
   }
 
+  auto* statement = vector_sem->Stmt();
+
   // Cast scalar to the vector element type
   auto* scalar_cast = b->Construct(packed_el_ty, scalar);
-  b->Sem().Add(scalar_cast, b->create<semantic::Expression>(packed_el_ty));
+  b->Sem().Add(scalar_cast,
+               b->create<semantic::Expression>(packed_el_ty, statement));
 
   auto* packed_ty = b->create<type::Vector>(packed_el_ty, packed_size);
 
@@ -73,7 +76,8 @@
   }
 
   auto* constructor = b->Construct(packed_ty, std::move(packed));
-  b->Sem().Add(constructor, b->create<semantic::Expression>(packed_ty));
+  b->Sem().Add(constructor,
+               b->create<semantic::Expression>(packed_ty, statement));
 
   return constructor;
 }
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 6ee2728..4d9c78b 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -984,7 +984,8 @@
   auto emit_vector_appended_with_i32_zero = [&](tint::ast::Expression* vector) {
     auto* i32 = builder_.create<type::I32>();
     auto* zero = builder_.Expr(0);
-    builder_.Sem().Add(zero, builder_.create<semantic::Expression>(i32));
+    auto* stmt = builder_.Sem().Get(vector)->Stmt();
+    builder_.Sem().Add(zero, builder_.create<semantic::Expression>(i32, stmt));
     auto* packed = AppendVector(&builder_, vector, zero);
     return EmitExpression(pre, out, packed);
   };