[spirv-reader] use stack of statement lists

This is preparation for emitting nested control flow.

Bug: tint:3
Change-Id: I90fc7edba8cb9937f722e6f5e94c7f222d34c403
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/21801
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 7970f84..ff4d80b 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -367,10 +367,22 @@
       type_mgr_(ir_context_.get_type_mgr()),
       fail_stream_(pi->fail_stream()),
       namer_(pi->namer()),
-      function_(function) {}
+      function_(function) {
+  statements_stack_.emplace_back(ast::StatementList{});
+}
 
 FunctionEmitter::~FunctionEmitter() = default;
 
+const ast::StatementList& FunctionEmitter::ast_body() {
+  assert(!statements_stack_.empty());
+  return statements_stack_[0];
+}
+
+void FunctionEmitter::AddStatement(std::unique_ptr<ast::Statement> statement) {
+  assert(!statements_stack_.empty());
+  statements_stack_.back().emplace_back(std::move(statement));
+}
+
 bool FunctionEmitter::Emit() {
   if (failed()) {
     return false;
@@ -389,7 +401,15 @@
   }
 
   // Set the body of the AST function node.
-  parser_impl_.get_module().functions().back()->set_body(std::move(ast_body_));
+  if (statements_stack_.size() != 1) {
+    return Fail() << "internal error: statement-list stack should have 1 "
+                     "element but has "
+                  << statements_stack_.size();
+  }
+  ast::StatementList body(std::move(statements_stack_[0]));
+  parser_impl_.get_module().functions().back()->set_body(std::move(body));
+  // Maintain the invariant by repopulating the one and only element.
+  statements_stack_[0] = ast::StatementList{};
 
   return success();
 }
@@ -1363,7 +1383,7 @@
     // TODO(dneto): Add the initializer via Variable::set_constructor.
     auto var_decl_stmt =
         std::make_unique<ast::VariableDeclStatement>(std::move(var));
-    ast_body_.emplace_back(std::move(var_decl_stmt));
+    AddStatement(std::move(var_decl_stmt));
     // Save this as an already-named value.
     identifier_values_.insert(inst.result_id());
   }
@@ -1445,7 +1465,7 @@
   }
   ast_const->set_constructor(std::move(ast_expr.expr));
   ast_const->set_is_const(true);
-  ast_body_.emplace_back(
+  AddStatement(
       std::make_unique<ast::VariableDeclStatement>(std::move(ast_const)));
   // Save this as an already-named value.
   identifier_values_.insert(inst.result_id());
@@ -1476,7 +1496,7 @@
       // TODO(dneto): Order of evaluation?
       auto lhs = MakeExpression(inst.GetSingleWordInOperand(0));
       auto rhs = MakeExpression(inst.GetSingleWordInOperand(1));
-      ast_body_.emplace_back(std::make_unique<ast::AssignmentStatement>(
+      AddStatement(std::make_unique<ast::AssignmentStatement>(
           std::move(lhs.expr), std::move(rhs.expr)));
       return success();
     }
diff --git a/src/reader/spirv/function.h b/src/reader/spirv/function.h
index 36490d4..9174259 100644
--- a/src/reader/spirv/function.h
+++ b/src/reader/spirv/function.h
@@ -172,8 +172,10 @@
   /// @returns true if emission has failed.
   bool failed() const { return !success(); }
 
+  /// Returns the body of the function.  It is the bottom of the statement
+  /// stack.
   /// @returns the body of the function.
-  const ast::StatementList& ast_body() { return ast_body_; }
+  const ast::StatementList& ast_body();
 
   /// Records failure.
   /// @returns a FailStream on which to emit diagnostics.
@@ -188,7 +190,8 @@
   /// @returns true if emission has not yet failed.
   bool EmitFunctionDeclaration();
 
-  /// Emits the function body, populating |ast_body_|
+  /// Emits the function body, populating the bottom entry of the statements
+  /// stack.
   /// @returns false if emission failed.
   bool EmitBody();
 
@@ -343,6 +346,9 @@
   /// or nullptr
   BlockInfo* HeaderIfBreakable(const Construct* c);
 
+  /// Appends a new statement to the top of the statement stack.
+  void AddStatement(std::unique_ptr<ast::Statement> statement);
+
   ParserImpl& parser_impl_;
   ast::Module& ast_module_;
   spvtools::opt::IRContext& ir_context_;
@@ -352,7 +358,12 @@
   FailStream& fail_stream_;
   Namer& namer_;
   const spvtools::opt::Function& function_;
-  ast::StatementList ast_body_;
+
+  // A stack of statement lists. Each list is contained in a construct in
+  // the next deeper element of stack. The 0th entry represents the statements
+  // for the entire function.  This stack is never empty.
+  std::vector<ast::StatementList> statements_stack_;
+
   // The set of IDs that have already had an identifier name generated for it.
   std::unordered_set<uint32_t> identifier_values_;
   // Mapping from SPIR-V ID that is used at most once, to its AST expression.