Convert CaseStatement to use BlockStatement.

This CL converts the CaseStatement class to using a BlockStatement
internally. All usages have been updated execept for the two readers.

Bug: tint:130
Change-Id: Idb9114230f7d9eb0f2208af635316edb0a0e8f99
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/25721
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
diff --git a/src/ast/block_statement.h b/src/ast/block_statement.h
index f1be730..eb408de 100644
--- a/src/ast/block_statement.h
+++ b/src/ast/block_statement.h
@@ -42,13 +42,20 @@
     statements_.push_back(std::move(stmt));
   }
 
+  /// @returns true if the block is empty
+  bool empty() const { return statements_.empty(); }
   /// @returns the number of statements directly in the block
   size_t size() const { return statements_.size(); }
 
+  /// @returns the last statement in the block or nullptr if block empty
+  const ast::Statement* last() const {
+    return statements_.empty() ? nullptr : statements_.back().get();
+  }
+
   /// Retrieves the statement at |idx|
   /// @param idx the index. The index is not bounds checked.
   /// @returns the statement at |idx|
-  ast::Statement* get(size_t idx) { return statements_[idx].get(); }
+  const ast::Statement* get(size_t idx) const { return statements_[idx].get(); }
 
   /// Retrieves the statement at |idx|
   /// @param idx the index. The index is not bounds checked.
diff --git a/src/ast/case_statement.cc b/src/ast/case_statement.cc
index bb63225..f56c8ef 100644
--- a/src/ast/case_statement.cc
+++ b/src/ast/case_statement.cc
@@ -17,17 +17,19 @@
 namespace tint {
 namespace ast {
 
-CaseStatement::CaseStatement() : Statement() {}
+CaseStatement::CaseStatement()
+    : Statement(), body_(std::make_unique<BlockStatement>()) {}
 
-CaseStatement::CaseStatement(StatementList body)
+CaseStatement::CaseStatement(std::unique_ptr<BlockStatement> body)
     : Statement(), body_(std::move(body)) {}
 
-CaseStatement::CaseStatement(CaseSelectorList selectors, StatementList body)
+CaseStatement::CaseStatement(CaseSelectorList selectors,
+                             std::unique_ptr<BlockStatement> body)
     : Statement(), selectors_(std::move(selectors)), body_(std::move(body)) {}
 
 CaseStatement::CaseStatement(const Source& source,
                              CaseSelectorList selectors,
-                             StatementList body)
+                             std::unique_ptr<BlockStatement> body)
     : Statement(source),
       selectors_(std::move(selectors)),
       body_(std::move(body)) {}
@@ -36,16 +38,18 @@
 
 CaseStatement::~CaseStatement() = default;
 
+void CaseStatement::set_body(StatementList body) {
+  for (auto& stmt : body) {
+    body_->append(std::move(stmt));
+  }
+}
+
 bool CaseStatement::IsCase() const {
   return true;
 }
 
 bool CaseStatement::IsValid() const {
-  for (const auto& stmt : body_) {
-    if (stmt == nullptr || !stmt->IsValid())
-      return false;
-  }
-  return true;
+  return body_ != nullptr && body_->IsValid();
 }
 
 void CaseStatement::to_str(std::ostream& out, size_t indent) const {
@@ -66,8 +70,11 @@
     out << "{" << std::endl;
   }
 
-  for (const auto& stmt : body_)
-    stmt->to_str(out, indent + 2);
+  if (body_ != nullptr) {
+    for (const auto& stmt : *body_) {
+      stmt->to_str(out, indent + 2);
+    }
+  }
 
   make_indent(out, indent);
   out << "}" << std::endl;
diff --git a/src/ast/case_statement.h b/src/ast/case_statement.h
index 9693bb5..ac1a9c1 100644
--- a/src/ast/case_statement.h
+++ b/src/ast/case_statement.h
@@ -19,6 +19,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/ast/block_statement.h"
 #include "src/ast/expression.h"
 #include "src/ast/int_literal.h"
 #include "src/ast/statement.h"
@@ -37,18 +38,19 @@
   /// Constructor
   /// Creates a default case statement
   /// @param body the case body
-  explicit CaseStatement(StatementList body);
+  explicit CaseStatement(std::unique_ptr<BlockStatement> body);
   /// Constructor
   /// @param selectors the case selectors
   /// @param body the case body
-  CaseStatement(CaseSelectorList selectors, StatementList body);
+  CaseStatement(CaseSelectorList selectors,
+                std::unique_ptr<BlockStatement> body);
   /// Constructor
   /// @param source the source information
   /// @param selectors the case selectors
   /// @param body the case body
   CaseStatement(const Source& source,
                 CaseSelectorList selectors,
-                StatementList body);
+                std::unique_ptr<BlockStatement> body);
   /// Move constructor
   CaseStatement(CaseStatement&&);
   ~CaseStatement() override;
@@ -65,9 +67,14 @@
 
   /// Sets the case body
   /// @param body the case body
-  void set_body(StatementList body) { body_ = std::move(body); }
+  void set_body(StatementList body);
+  /// Sets the case body
+  /// @param body the case body
+  void set_body(std::unique_ptr<BlockStatement> body) {
+    body_ = std::move(body);
+  }
   /// @returns the case body
-  const StatementList& body() const { return body_; }
+  const BlockStatement* body() const { return body_.get(); }
 
   /// @returns true if this is a case statement
   bool IsCase() const override;
@@ -84,7 +91,7 @@
   CaseStatement(const CaseStatement&) = delete;
 
   CaseSelectorList selectors_;
-  StatementList body_;
+  std::unique_ptr<BlockStatement> body_;
 };
 
 /// A list of unique case statements
diff --git a/src/ast/case_statement_test.cc b/src/ast/case_statement_test.cc
index 6d68db8..b834c59 100644
--- a/src/ast/case_statement_test.cc
+++ b/src/ast/case_statement_test.cc
@@ -34,17 +34,17 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
 
   auto* int_ptr = b.back().get();
-  auto* discard_ptr = stmts[0].get();
+  auto* discard_ptr = body->get(0);
 
-  CaseStatement c(std::move(b), std::move(stmts));
+  CaseStatement c(std::move(b), std::move(body));
   ASSERT_EQ(c.selectors().size(), 1u);
   EXPECT_EQ(c.selectors()[0].get(), int_ptr);
-  ASSERT_EQ(c.body().size(), 1u);
-  EXPECT_EQ(c.body()[0].get(), discard_ptr);
+  ASSERT_EQ(c.body()->size(), 1u);
+  EXPECT_EQ(c.body()->get(0), discard_ptr);
 }
 
 TEST_F(CaseStatementTest, Creation_u32) {
@@ -53,17 +53,17 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<UintLiteral>(&u32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
 
   auto* int_ptr = b.back().get();
-  auto* discard_ptr = stmts[0].get();
+  auto* discard_ptr = body->get(0);
 
-  CaseStatement c(std::move(b), std::move(stmts));
+  CaseStatement c(std::move(b), std::move(body));
   ASSERT_EQ(c.selectors().size(), 1u);
   EXPECT_EQ(c.selectors()[0].get(), int_ptr);
-  ASSERT_EQ(c.body().size(), 1u);
-  EXPECT_EQ(c.body()[0].get(), discard_ptr);
+  ASSERT_EQ(c.body()->size(), 1u);
+  EXPECT_EQ(c.body()->get(0), discard_ptr);
 }
 
 TEST_F(CaseStatementTest, Creation_WithSource) {
@@ -71,21 +71,21 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
 
-  CaseStatement c(Source{20, 2}, std::move(b), std::move(stmts));
+  CaseStatement c(Source{20, 2}, std::move(b), std::move(body));
   auto src = c.source();
   EXPECT_EQ(src.line, 20u);
   EXPECT_EQ(src.column, 2u);
 }
 
 TEST_F(CaseStatementTest, IsDefault_WithoutSelectors) {
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
 
   CaseStatement c;
-  c.set_body(std::move(stmts));
+  c.set_body(std::move(body));
   EXPECT_TRUE(c.IsDefault());
 }
 
@@ -114,11 +114,11 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
-  stmts.push_back(nullptr);
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
+  body->append(nullptr);
 
-  CaseStatement c(std::move(b), std::move(stmts));
+  CaseStatement c(std::move(b), std::move(body));
   EXPECT_FALSE(c.IsValid());
 }
 
@@ -127,10 +127,10 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<IfStatement>());
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<IfStatement>());
 
-  CaseStatement c({std::move(b)}, std::move(stmts));
+  CaseStatement c({std::move(b)}, std::move(body));
   EXPECT_FALSE(c.IsValid());
 }
 
@@ -139,9 +139,9 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, -2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
-  CaseStatement c({std::move(b)}, std::move(stmts));
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
+  CaseStatement c({std::move(b)}, std::move(body));
 
   std::ostringstream out;
   c.to_str(out, 2);
@@ -156,9 +156,9 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<UintLiteral>(&u32, 2));
 
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
-  CaseStatement c({std::move(b)}, std::move(stmts));
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
+  CaseStatement c({std::move(b)}, std::move(body));
 
   std::ostringstream out;
   c.to_str(out, 2);
@@ -174,9 +174,10 @@
   CaseSelectorList b;
   b.push_back(std::make_unique<SintLiteral>(&i32, 1));
   b.push_back(std::make_unique<SintLiteral>(&i32, 2));
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
-  CaseStatement c(std::move(b), std::move(stmts));
+
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
+  CaseStatement c(std::move(b), std::move(body));
 
   std::ostringstream out;
   c.to_str(out, 2);
@@ -187,9 +188,9 @@
 }
 
 TEST_F(CaseStatementTest, ToStr_WithoutSelectors) {
-  StatementList stmts;
-  stmts.push_back(std::make_unique<DiscardStatement>());
-  CaseStatement c(CaseSelectorList{}, std::move(stmts));
+  auto body = std::make_unique<BlockStatement>();
+  body->append(std::make_unique<DiscardStatement>());
+  CaseStatement c(CaseSelectorList{}, std::move(body));
 
   std::ostringstream out;
   c.to_str(out, 2);
diff --git a/src/ast/function.cc b/src/ast/function.cc
index 6fe5a32..7f9ae61 100644
--- a/src/ast/function.cc
+++ b/src/ast/function.cc
@@ -200,8 +200,11 @@
   make_indent(out, indent);
   out << "{" << std::endl;
 
-  for (const auto& stmt : *body_)
-    stmt->to_str(out, indent + 2);
+  if (body_ != nullptr) {
+    for (const auto& stmt : *body_) {
+      stmt->to_str(out, indent + 2);
+    }
+  }
 
   make_indent(out, indent);
   out << "}" << std::endl;
diff --git a/src/ast/switch_statement_test.cc b/src/ast/switch_statement_test.cc
index 8141157..dd3eeac 100644
--- a/src/ast/switch_statement_test.cc
+++ b/src/ast/switch_statement_test.cc
@@ -36,8 +36,8 @@
 
   auto ident = std::make_unique<IdentifierExpression>("ident");
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
 
   auto* ident_ptr = ident.get();
   auto* case_ptr = body[0].get();
@@ -70,8 +70,8 @@
 
   auto ident = std::make_unique<IdentifierExpression>("ident");
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
 
   SwitchStatement stmt(std::move(ident), std::move(body));
   EXPECT_TRUE(stmt.IsValid());
@@ -84,8 +84,8 @@
   lit.push_back(std::make_unique<SintLiteral>(&i32, 2));
 
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
 
   SwitchStatement stmt;
   stmt.set_body(std::move(body));
@@ -100,8 +100,8 @@
 
   auto ident = std::make_unique<IdentifierExpression>("");
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
 
   SwitchStatement stmt(std::move(ident), std::move(body));
   EXPECT_FALSE(stmt.IsValid());
@@ -115,8 +115,8 @@
 
   auto ident = std::make_unique<IdentifierExpression>("ident");
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
   body.push_back(nullptr);
 
   SwitchStatement stmt(std::move(ident), std::move(body));
@@ -126,8 +126,8 @@
 TEST_F(SwitchStatementTest, IsValid_Invalid_BodyStatement) {
   auto ident = std::make_unique<IdentifierExpression>("ident");
 
-  StatementList case_body;
-  case_body.push_back(nullptr);
+  auto case_body = std::make_unique<ast::BlockStatement>();
+  case_body->append(nullptr);
 
   CaseStatementList body;
   body.push_back(std::make_unique<CaseStatement>(CaseSelectorList{},
@@ -159,8 +159,8 @@
 
   auto ident = std::make_unique<IdentifierExpression>("ident");
   CaseStatementList body;
-  body.push_back(
-      std::make_unique<CaseStatement>(std::move(lit), StatementList()));
+  body.push_back(std::make_unique<CaseStatement>(
+      std::move(lit), std::make_unique<ast::BlockStatement>()));
 
   SwitchStatement stmt(std::move(ident), std::move(body));
   std::ostringstream out;
diff --git a/src/reader/wgsl/parser_impl_switch_body_test.cc b/src/reader/wgsl/parser_impl_switch_body_test.cc
index 3a68dd5..b5d3cd1 100644
--- a/src/reader/wgsl/parser_impl_switch_body_test.cc
+++ b/src/reader/wgsl/parser_impl_switch_body_test.cc
@@ -29,8 +29,8 @@
   ASSERT_NE(e, nullptr);
   ASSERT_TRUE(e->IsCase());
   EXPECT_FALSE(e->IsDefault());
-  ASSERT_EQ(e->body().size(), 1u);
-  EXPECT_TRUE(e->body()[0]->IsAssign());
+  ASSERT_EQ(e->body()->size(), 1u);
+  EXPECT_TRUE(e->body()->get(0)->IsAssign());
 }
 
 TEST_F(ParserImplTest, SwitchBody_Case_InvalidConstLiteral) {
@@ -96,8 +96,8 @@
   ASSERT_NE(e, nullptr);
   ASSERT_TRUE(e->IsCase());
   EXPECT_TRUE(e->IsDefault());
-  ASSERT_EQ(e->body().size(), 1u);
-  EXPECT_TRUE(e->body()[0]->IsAssign());
+  ASSERT_EQ(e->body()->size(), 1u);
+  EXPECT_TRUE(e->body()->get(0)->IsAssign());
 }
 
 TEST_F(ParserImplTest, SwitchBody_Default_MissingColon) {
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index c7f9bcc..a626c90 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -145,9 +145,9 @@
       std::make_unique<ast::FloatLiteral>(&f32, 2.3f));
   auto* rhs_ptr = rhs.get();
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::AssignmentStatement>(std::move(lhs),
-                                                            std::move(rhs)));
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::AssignmentStatement>(std::move(lhs),
+                                                          std::move(rhs)));
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 3));
@@ -339,9 +339,9 @@
       std::make_unique<ast::FloatLiteral>(&f32, 2.3f));
   auto* rhs_ptr = rhs.get();
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::AssignmentStatement>(std::move(lhs),
-                                                            std::move(rhs)));
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::AssignmentStatement>(std::move(lhs),
+                                                          std::move(rhs)));
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 3));
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index d7718a3..1937bb6 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -31,12 +31,12 @@
 namespace hlsl {
 namespace {
 
-bool last_is_break_or_fallthrough(const ast::StatementList& stmts) {
-  if (stmts.empty()) {
+bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
+  if (stmts->empty()) {
     return false;
   }
 
-  return stmts.back()->IsBreak() || stmts.back()->IsFallthrough();
+  return stmts->last()->IsBreak() || stmts->last()->IsFallthrough();
 }
 
 }  // namespace
@@ -207,7 +207,7 @@
 
   increment_indent();
 
-  for (const auto& s : stmt->body()) {
+  for (const auto& s : *(stmt->body())) {
     if (!EmitStatement(s.get())) {
       return false;
     }
diff --git a/src/writer/hlsl/generator_impl_case_test.cc b/src/writer/hlsl/generator_impl_case_test.cc
index 17803db..acc0c8b 100644
--- a/src/writer/hlsl/generator_impl_case_test.cc
+++ b/src/writer/hlsl/generator_impl_case_test.cc
@@ -34,8 +34,8 @@
 TEST_F(HlslGeneratorImplTest, Emit_Case) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -55,11 +55,9 @@
 TEST_F(HlslGeneratorImplTest, Emit_Case_BreaksByDefault) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
-  ast::CaseStatement c(std::move(lit), std::move(body));
+  ast::CaseStatement c(std::move(lit), std::make_unique<ast::BlockStatement>());
 
   ast::Module m;
   GeneratorImpl g(&m);
@@ -75,8 +73,8 @@
 TEST_F(HlslGeneratorImplTest, Emit_Case_WithFallthrough) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::FallthroughStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::FallthroughStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -96,8 +94,8 @@
 TEST_F(HlslGeneratorImplTest, Emit_Case_MultipleSelectors) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -119,9 +117,8 @@
 TEST_F(HlslGeneratorImplTest, Emit_Case_Default) {
   ast::CaseStatement c;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
-
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
   c.set_body(std::move(body));
 
   ast::Module m;
diff --git a/src/writer/hlsl/generator_impl_switch_test.cc b/src/writer/hlsl/generator_impl_switch_test.cc
index 1c1719f..8648893 100644
--- a/src/writer/hlsl/generator_impl_switch_test.cc
+++ b/src/writer/hlsl/generator_impl_switch_test.cc
@@ -33,16 +33,16 @@
 
 TEST_F(HlslGeneratorImplTest, Emit_Switch) {
   auto def = std::make_unique<ast::CaseStatement>();
-  ast::StatementList def_body;
-  def_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto def_body = std::make_unique<ast::BlockStatement>();
+  def_body->append(std::make_unique<ast::BreakStatement>());
   def->set_body(std::move(def_body));
 
   ast::type::I32Type i32;
   ast::CaseSelectorList case_val;
   case_val.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
 
-  ast::StatementList case_body;
-  case_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto case_body = std::make_unique<ast::BlockStatement>();
+  case_body->append(std::make_unique<ast::BreakStatement>());
 
   auto case_stmt = std::make_unique<ast::CaseStatement>(std::move(case_val),
                                                         std::move(case_body));
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 79fd9ab..db4af7f 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -66,12 +66,12 @@
 const char kTintStructInVarPrefix[] = "tint_in";
 const char kTintStructOutVarPrefix[] = "tint_out";
 
-bool last_is_break_or_fallthrough(const ast::StatementList& stmts) {
-  if (stmts.empty()) {
+bool last_is_break_or_fallthrough(const ast::BlockStatement* stmts) {
+  if (stmts->empty()) {
     return false;
   }
 
-  return stmts.back()->IsBreak() || stmts.back()->IsFallthrough();
+  return stmts->last()->IsBreak() || stmts->last()->IsFallthrough();
 }
 
 uint32_t adjust_for_alignment(uint32_t count, uint32_t alignment) {
@@ -755,7 +755,7 @@
 
   increment_indent();
 
-  for (const auto& s : stmt->body()) {
+  for (const auto& s : *(stmt->body())) {
     if (!EmitStatement(s.get())) {
       return false;
     }
diff --git a/src/writer/msl/generator_impl_case_test.cc b/src/writer/msl/generator_impl_case_test.cc
index 9054873..cd43294 100644
--- a/src/writer/msl/generator_impl_case_test.cc
+++ b/src/writer/msl/generator_impl_case_test.cc
@@ -34,8 +34,8 @@
 TEST_F(MslGeneratorImplTest, Emit_Case) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -55,11 +55,9 @@
 TEST_F(MslGeneratorImplTest, Emit_Case_BreaksByDefault) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
-  ast::CaseStatement c(std::move(lit), std::move(body));
+  ast::CaseStatement c(std::move(lit), std::make_unique<ast::BlockStatement>());
 
   ast::Module m;
   GeneratorImpl g(&m);
@@ -75,8 +73,8 @@
 TEST_F(MslGeneratorImplTest, Emit_Case_WithFallthrough) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::FallthroughStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::FallthroughStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -96,8 +94,8 @@
 TEST_F(MslGeneratorImplTest, Emit_Case_MultipleSelectors) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -119,9 +117,8 @@
 TEST_F(MslGeneratorImplTest, Emit_Case_Default) {
   ast::CaseStatement c;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
-
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
   c.set_body(std::move(body));
 
   ast::Module m;
diff --git a/src/writer/msl/generator_impl_switch_test.cc b/src/writer/msl/generator_impl_switch_test.cc
index caffd1d..60b0482 100644
--- a/src/writer/msl/generator_impl_switch_test.cc
+++ b/src/writer/msl/generator_impl_switch_test.cc
@@ -33,16 +33,16 @@
 
 TEST_F(MslGeneratorImplTest, Emit_Switch) {
   auto def = std::make_unique<ast::CaseStatement>();
-  ast::StatementList def_body;
-  def_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto def_body = std::make_unique<ast::BlockStatement>();
+  def_body->append(std::make_unique<ast::BreakStatement>());
   def->set_body(std::move(def_body));
 
   ast::type::I32Type i32;
   ast::CaseSelectorList case_val;
   case_val.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
 
-  ast::StatementList case_body;
-  case_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto case_body = std::make_unique<ast::BlockStatement>();
+  case_body->append(std::make_unique<ast::BreakStatement>());
 
   auto case_stmt = std::make_unique<ast::CaseStatement>(std::move(case_val),
                                                         std::move(case_body));
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 0040481..5fd41f8 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -93,13 +93,23 @@
   return model;
 }
 
-bool LastIsFallthrough(const ast::StatementList& stmts) {
-  return !stmts.empty() && stmts.back()->IsFallthrough();
+bool LastIsFallthrough(const ast::BlockStatement* stmts) {
+  return !stmts->empty() && stmts->last()->IsFallthrough();
 }
 
 // A terminator is anything which will case a SPIR-V terminator to be emitted.
 // This means things like breaks, fallthroughs and continues which all emit an
 // OpBranch or return for the OpReturn emission.
+bool LastIsTerminator(const ast::BlockStatement* stmts) {
+  if (stmts->empty()) {
+    return false;
+  }
+
+  auto* last = stmts->last();
+  return last->IsBreak() || last->IsContinue() || last->IsDiscard() ||
+         last->IsReturn() || last->IsFallthrough();
+}
+
 bool LastIsTerminator(const ast::StatementList& stmts) {
   if (stmts.empty()) {
     return false;
@@ -1339,7 +1349,7 @@
   return result_id;
 }
 
-bool Builder::GenerateBlockStatement(ast::BlockStatement* stmt) {
+bool Builder::GenerateBlockStatement(const ast::BlockStatement* stmt) {
   scope_stack_.push_scope();
   for (const auto& block_stmt : *stmt) {
     if (!GenerateStatement(block_stmt.get())) {
@@ -1720,7 +1730,7 @@
     }
 
     GenerateLabel(case_ids[i]);
-    if (!GenerateStatementList(item->body())) {
+    if (!GenerateBlockStatement(item->body())) {
       return false;
     }
 
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 1e62b3a..e787556 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -170,7 +170,7 @@
   /// Generates a block statement
   /// @param stmt the statement to generate
   /// @returns true if the statement was successfully generated
-  bool GenerateBlockStatement(ast::BlockStatement* stmt);
+  bool GenerateBlockStatement(const ast::BlockStatement* stmt);
   /// Generates a break statement
   /// @param stmt the statement to generate
   /// @returns true if the statement was successfully generated
diff --git a/src/writer/spirv/builder_switch_test.cc b/src/writer/spirv/builder_switch_test.cc
index d320235..4cd7c53 100644
--- a/src/writer/spirv/builder_switch_test.cc
+++ b/src/writer/spirv/builder_switch_test.cc
@@ -85,14 +85,14 @@
   auto a =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &i32);
 
-  ast::StatementList case_1_body;
-  case_1_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_1_body = std::make_unique<ast::BlockStatement>();
+  case_1_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
 
-  ast::StatementList case_2_body;
-  case_2_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_2_body = std::make_unique<ast::BlockStatement>();
+  case_2_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 2))));
@@ -170,8 +170,8 @@
   auto a =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &i32);
 
-  ast::StatementList default_body;
-  default_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto default_body = std::make_unique<ast::BlockStatement>();
+  default_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
@@ -239,20 +239,20 @@
   auto a =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &i32);
 
-  ast::StatementList case_1_body;
-  case_1_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_1_body = std::make_unique<ast::BlockStatement>();
+  case_1_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
 
-  ast::StatementList case_2_body;
-  case_2_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_2_body = std::make_unique<ast::BlockStatement>();
+  case_2_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 2))));
 
-  ast::StatementList default_body;
-  default_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto default_body = std::make_unique<ast::BlockStatement>();
+  default_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 3))));
@@ -340,21 +340,21 @@
   auto a =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &i32);
 
-  ast::StatementList case_1_body;
-  case_1_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_1_body = std::make_unique<ast::BlockStatement>();
+  case_1_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
-  case_1_body.push_back(std::make_unique<ast::FallthroughStatement>());
+  case_1_body->append(std::make_unique<ast::FallthroughStatement>());
 
-  ast::StatementList case_2_body;
-  case_2_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_2_body = std::make_unique<ast::BlockStatement>();
+  case_2_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 2))));
 
-  ast::StatementList default_body;
-  default_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto default_body = std::make_unique<ast::BlockStatement>();
+  default_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 3))));
@@ -437,12 +437,12 @@
   auto a =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &i32);
 
-  ast::StatementList case_1_body;
-  case_1_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  auto case_1_body = std::make_unique<ast::BlockStatement>();
+  case_1_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
-  case_1_body.push_back(std::make_unique<ast::FallthroughStatement>());
+  case_1_body->append(std::make_unique<ast::FallthroughStatement>());
 
   ast::CaseSelectorList selector_1;
   selector_1.push_back(std::make_unique<ast::SintLiteral>(&i32, 1));
@@ -492,13 +492,13 @@
   ast::StatementList if_body;
   if_body.push_back(std::make_unique<ast::BreakStatement>());
 
-  ast::StatementList case_1_body;
-  case_1_body.push_back(std::make_unique<ast::IfStatement>(
+  auto case_1_body = std::make_unique<ast::BlockStatement>();
+  case_1_body->append(std::make_unique<ast::IfStatement>(
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::BoolLiteral>(&bool_type, true)),
       std::move(if_body)));
 
-  case_1_body.push_back(std::make_unique<ast::AssignmentStatement>(
+  case_1_body->append(std::make_unique<ast::AssignmentStatement>(
       std::make_unique<ast::IdentifierExpression>("v"),
       std::make_unique<ast::ScalarConstructorExpression>(
           std::make_unique<ast::SintLiteral>(&i32, 1))));
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 3fdabee..ac8ba76 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -600,7 +600,7 @@
   return true;
 }
 
-bool GeneratorImpl::EmitBlock(ast::BlockStatement* stmt) {
+bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
   out_ << "{" << std::endl;
   increment_indent();
 
@@ -617,7 +617,8 @@
   return true;
 }
 
-bool GeneratorImpl::EmitIndentedBlockAndNewline(ast::BlockStatement* stmt) {
+bool GeneratorImpl::EmitIndentedBlockAndNewline(
+    const ast::BlockStatement* stmt) {
   make_indent();
   const bool result = EmitBlock(stmt);
   if (result) {
@@ -626,7 +627,7 @@
   return result;
 }
 
-bool GeneratorImpl::EmitBlockAndNewline(ast::BlockStatement* stmt) {
+bool GeneratorImpl::EmitBlockAndNewline(const ast::BlockStatement* stmt) {
   const bool result = EmitBlock(stmt);
   if (result) {
     out_ << std::endl;
@@ -736,7 +737,7 @@
   make_indent();
 
   if (stmt->IsDefault()) {
-    out_ << "default:";
+    out_ << "default";
   } else {
     out_ << "case ";
 
@@ -752,10 +753,10 @@
       }
     }
 
-    out_ << ":";
   }
+  out_ << ": ";
 
-  return EmitStatementBlockAndNewline(stmt->body());
+  return EmitBlockAndNewline(stmt->body());
 }
 
 bool GeneratorImpl::EmitContinue(ast::ContinueStatement*) {
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index 50e3141..2f0a500 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -70,15 +70,15 @@
   /// Handles a block statement
   /// @param stmt the statement to emit
   /// @returns true if the statement was emitted successfully
-  bool EmitBlock(ast::BlockStatement* stmt);
+  bool EmitBlock(const ast::BlockStatement* stmt);
   /// Handles a block statement with a newline at the end
   /// @param stmt the statement to emit
   /// @returns true if the statement was emitted successfully
-  bool EmitIndentedBlockAndNewline(ast::BlockStatement* stmt);
+  bool EmitIndentedBlockAndNewline(const ast::BlockStatement* stmt);
   /// Handles a block statement with a newline at the end
   /// @param stmt the statement to emit
   /// @returns true if the statement was emitted successfully
-  bool EmitBlockAndNewline(ast::BlockStatement* stmt);
+  bool EmitBlockAndNewline(const ast::BlockStatement* stmt);
   /// Handles a break statement
   /// @param stmt the statement to emit
   /// @returns true if the statement was emitted successfully
diff --git a/src/writer/wgsl/generator_impl_case_test.cc b/src/writer/wgsl/generator_impl_case_test.cc
index 511e2bd..d389403 100644
--- a/src/writer/wgsl/generator_impl_case_test.cc
+++ b/src/writer/wgsl/generator_impl_case_test.cc
@@ -32,8 +32,8 @@
 TEST_F(WgslGeneratorImplTest, Emit_Case) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -52,8 +52,8 @@
 TEST_F(WgslGeneratorImplTest, Emit_Case_MultipleSelectors) {
   ast::type::I32Type i32;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
 
   ast::CaseSelectorList lit;
   lit.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
@@ -73,9 +73,8 @@
 TEST_F(WgslGeneratorImplTest, Emit_Case_Default) {
   ast::CaseStatement c;
 
-  ast::StatementList body;
-  body.push_back(std::make_unique<ast::BreakStatement>());
-
+  auto body = std::make_unique<ast::BlockStatement>();
+  body->append(std::make_unique<ast::BreakStatement>());
   c.set_body(std::move(body));
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_switch_test.cc b/src/writer/wgsl/generator_impl_switch_test.cc
index 090380e..ceecb80 100644
--- a/src/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/writer/wgsl/generator_impl_switch_test.cc
@@ -32,16 +32,16 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_Switch) {
   auto def = std::make_unique<ast::CaseStatement>();
-  ast::StatementList def_body;
-  def_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto def_body = std::make_unique<ast::BlockStatement>();
+  def_body->append(std::make_unique<ast::BreakStatement>());
   def->set_body(std::move(def_body));
 
   ast::type::I32Type i32;
   ast::CaseSelectorList case_val;
   case_val.push_back(std::make_unique<ast::SintLiteral>(&i32, 5));
 
-  ast::StatementList case_body;
-  case_body.push_back(std::make_unique<ast::BreakStatement>());
+  auto case_body = std::make_unique<ast::BlockStatement>();
+  case_body->append(std::make_unique<ast::BreakStatement>());
 
   auto case_stmt = std::make_unique<ast::CaseStatement>(std::move(case_val),
                                                         std::move(case_body));