wsgl parser: Add ParserImpl::Expect<T>

And use it for the ParserImpl::expect_xxx() methods.

This is the first step towards supporting multiple error messages, as
the caller can now test to see if the specific call errored, instead of
using a global error state.

Also cleans up a bunch of code.

Bug: tint:282

Change-Id: I5e39fc33bd1e16620cee80d27fa728bc2af3387e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32101
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 52aba79..b04a509 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -74,6 +74,9 @@
 namespace wgsl {
 namespace {
 
+template <typename T>
+using Expect = ParserImpl::Expect<T>;
+
 /// Controls the maximum number of times we'll call into the const_expr function
 /// from itself. This is to guard against stack overflow when there is an
 /// excessive number of type constructors inside the const_expr.
@@ -123,27 +126,32 @@
 
 ParserImpl::~ParserImpl() = default;
 
-void ParserImpl::add_error(const Source& source,
-                           const std::string& err,
-                           const std::string& use) {
+ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
+                                                   const std::string& err,
+                                                   const std::string& use) {
   std::stringstream msg;
   msg << err;
   if (!use.empty()) {
     msg << " for " << use;
   }
   add_error(source, msg.str());
+  return Failure::kErrored;
 }
 
-void ParserImpl::add_error(const Token& t, const std::string& err) {
+ParserImpl::Failure::Errored ParserImpl::add_error(const Token& t,
+                                                   const std::string& err) {
   add_error(t.source(), err);
+  return Failure::kErrored;
 }
 
-void ParserImpl::add_error(const Source& source, const std::string& err) {
+ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
+                                                   const std::string& err) {
   diag::Diagnostic diagnostic;
   diagnostic.severity = diag::Severity::Error;
   diagnostic.message = err;
   diagnostic.source = source;
   diags_.add(std::move(diagnostic));
+  return Failure::kErrored;
 }
 
 Token ParserImpl::next() {
@@ -187,12 +195,9 @@
 // translation_unit
 //  : global_decl* EOF
 void ParserImpl::translation_unit() {
-  for (;;) {
-    expect_global_decl();
-    if (has_error())
-      return;
-
-    if (peek().IsEof())
+  while (!peek().IsEof()) {
+    auto decl = expect_global_decl();
+    if (decl.errored)
       break;
   }
 
@@ -206,84 +211,77 @@
 //  | type_alias SEMICOLON
 //  | struct_decl SEMICOLON
 //  | function_decl
-void ParserImpl::expect_global_decl() {
-  auto t = peek();
-  if (t.IsEof()) {
-    return;
-  }
-
-  if (t.IsSemicolon()) {
-    next();  // consume the peek
-    return;
-  }
+Expect<bool> ParserImpl::expect_global_decl() {
+  if (match(Token::Type::kSemicolon) || match(Token::Type::kEOF))
+    return true;
 
   auto decos = decoration_list();
 
   auto gv = global_variable_decl(decos);
-  if (has_error()) {
-    return;
-  }
+  if (has_error())
+    return Failure::kErrored;
+
   if (gv != nullptr) {
     if (!expect("variable declaration", Token::Type::kSemicolon))
-      return;
+      return Failure::kErrored;
 
     module_.AddGlobalVariable(std::move(gv));
-    return;
+    return true;
   }
 
   auto gc = global_constant_decl();
-  if (has_error()) {
-    return;
-  }
+  if (has_error())
+    return Failure::kErrored;
+
   if (gc != nullptr) {
     if (!expect("constant declaration", Token::Type::kSemicolon))
-      return;
+      return Failure::kErrored;
 
     module_.AddGlobalVariable(std::move(gc));
-    return;
+    return true;
   }
 
   auto* ta = type_alias();
-  if (has_error()) {
-    return;
-  }
+  if (has_error())
+    return Failure::kErrored;
+
   if (ta != nullptr) {
     if (!expect("type alias", Token::Type::kSemicolon))
-      return;
+      return Failure::kErrored;
 
     module_.AddConstructedType(ta);
-    return;
+    return true;
   }
 
   auto str = struct_decl(decos);
-  if (has_error()) {
-    return;
-  }
+  if (has_error())
+    return Failure::kErrored;
+
   if (str != nullptr) {
     if (!expect("struct declaration", Token::Type::kSemicolon))
-      return;
+      return Failure::kErrored;
 
     auto* type = ctx_.type_mgr().Get(std::move(str));
     register_constructed(type->AsStruct()->name(), type);
     module_.AddConstructedType(type);
-    return;
+    return true;
   }
 
   auto func = function_decl(decos);
-  if (has_error()) {
-    return;
-  }
+  if (has_error())
+    return Failure::kErrored;
+
   if (func != nullptr) {
     module_.AddFunction(std::move(func));
-    return;
+    return true;
   }
 
-  t = peek();
   if (decos.size() > 0) {
-    add_error(t, "expected declaration after decorations");
+    add_error(peek(), "expected declaration after decorations");
   } else {
-    add_error(t, "invalid token");
+    add_error(peek(), "invalid token");
   }
+  return Failure::kErrored;
 }
 
 // global_variable_decl
@@ -296,17 +294,20 @@
     return nullptr;
 
   auto var_decos = cast_decorations<ast::VariableDecoration>(decos);
-  if (var_decos.size() > 0) {
+  if (var_decos.errored)
+    return nullptr;
+
+  if (var_decos.value.size() > 0) {
     auto dv = std::make_unique<ast::DecoratedVariable>(std::move(var));
-    dv->set_decorations(std::move(var_decos));
+    dv->set_decorations(std::move(var_decos.value));
     var = std::move(dv);
   }
 
   if (match(Token::Type::kEqual)) {
     auto expr = expect_const_expr();
-    if (has_error())
+    if (expr.errored)
       return nullptr;
-    var->set_constructor(std::move(expr));
+    var->set_constructor(std::move(expr.value));
   }
   return var;
 }
@@ -320,21 +321,21 @@
   const char* use = "constant declaration";
 
   auto decl = expect_variable_ident_decl(use);
-  if (has_error())
+  if (decl.errored)
     return nullptr;
 
   auto var = std::make_unique<ast::Variable>(
-      decl.source, decl.name, ast::StorageClass::kNone, decl.type);
+      decl->source, decl->name, ast::StorageClass::kNone, decl->type);
   var->set_is_const(true);
 
   if (!expect(use, Token::Type::kEqual))
     return nullptr;
 
   auto init = expect_const_expr();
-  if (has_error())
+  if (init.errored)
     return nullptr;
 
-  var->set_constructor(std::move(init));
+  var->set_constructor(std::move(init.value));
 
   return var;
 }
@@ -350,10 +351,11 @@
     return nullptr;
 
   auto decl = expect_variable_ident_decl("variable declaration");
-  if (has_error())
+  if (decl.errored)
     return nullptr;
 
-  return std::make_unique<ast::Variable>(decl.source, decl.name, sc, decl.type);
+  return std::make_unique<ast::Variable>(decl->source, decl->name, sc,
+                                         decl->type);
 }
 
 // texture_sampler_types
@@ -426,19 +428,15 @@
     if (!expect(use, Token::Type::kLessThan))
       return nullptr;
 
-    auto format = image_storage_type();
-    if (has_error())
+    auto format = expect_image_storage_type(use);
+    if (format.errored)
       return nullptr;
-    if (format == ast::type::ImageFormat::kNone) {
-      add_error(peek().source(), "invalid format", use);
-      return nullptr;
-    }
 
     if (!expect(use, Token::Type::kGreaterThan))
       return nullptr;
 
     return ctx_.type_mgr().Get(std::make_unique<ast::type::StorageTextureType>(
-        storage_dim, access, format));
+        storage_dim, access, format.value));
   }
 
   return nullptr;
@@ -622,7 +620,8 @@
 //  | RGBA32UINT
 //  | RGBA32SINT
 //  | RGBA32FLOAT
-ast::type::ImageFormat ParserImpl::image_storage_type() {
+Expect<ast::type::ImageFormat> ParserImpl::expect_image_storage_type(
+    const std::string& use) {
   if (match(Token::Type::kFormatR8Unorm))
     return ast::type::ImageFormat::kR8Unorm;
 
@@ -728,31 +727,31 @@
   if (match(Token::Type::kFormatRgba32Float))
     return ast::type::ImageFormat::kRgba32Float;
 
-  return ast::type::ImageFormat::kNone;
+  return add_error(peek().source(), "invalid format", use);
 }
 
 // variable_ident_decl
 //   : IDENT COLON type_decl
-ParserImpl::TypedIdentifier ParserImpl::expect_variable_ident_decl(
+Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
     const std::string& use) {
-  std::string name;
-  Source source;
-  if (!expect_ident(use, &name, &source))
-    return {};
+  auto ident = expect_ident(use);
+  if (ident.errored)
+    return Failure::kErrored;
 
   if (!expect(use, Token::Type::kColon))
-    return {};
+    return Failure::kErrored;
 
   auto t = peek();
   auto* type = type_decl();
   if (has_error())
-    return {};
+    return Failure::kErrored;
+
   if (type == nullptr) {
     add_error(t.source(), "invalid type", use);
-    return {};
+    return Failure::kErrored;
   }
 
-  return {type, name, source};
+  return TypedIdentifier{type, ident.value, ident.source};
 }
 
 // variable_storage_decoration
@@ -764,13 +763,13 @@
   const char* use = "variable decoration";
 
   auto sc = expect_storage_class(use);
-  if (has_error())
-    return sc;
+  if (sc.errored)
+    return ast::StorageClass::kNone;
 
   if (!expect(use, Token::Type::kGreaterThan))
     return ast::StorageClass::kNone;
 
-  return sc;
+  return sc.value;
 }
 
 // type_alias
@@ -784,8 +783,8 @@
 
   const char* use = "type alias";
 
-  std::string name;
-  if (!expect_ident(use, &name))
+  auto name = expect_ident(use);
+  if (name.errored)
     return nullptr;
 
   if (!expect(use, Token::Type::kEqual))
@@ -799,9 +798,9 @@
     return nullptr;
   }
 
-  auto* alias =
-      ctx_.type_mgr().Get(std::make_unique<ast::type::AliasType>(name, type));
-  register_constructed(name, alias);
+  auto* alias = ctx_.type_mgr().Get(
+      std::make_unique<ast::type::AliasType>(name.value, type));
+  register_constructed(name.value, alias);
 
   return alias->AsAlias();
 }
@@ -853,11 +852,12 @@
   if (match(Token::Type::kU32))
     return ctx_.type_mgr().Get(std::make_unique<ast::type::U32Type>());
 
-  if (t.IsVec2() || t.IsVec3() || t.IsVec4())
-    return expect_type_decl_vector(t);
+  if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
+    return expect_type_decl_vector(t).value;
+  }
 
   if (match(Token::Type::kPtr))
-    return expect_type_decl_pointer();
+    return expect_type_decl_pointer().value;
 
   auto decos = decoration_list();
   if (has_error())
@@ -865,7 +865,9 @@
 
   if (match(Token::Type::kArray)) {
     auto array_decos = cast_decorations<ast::ArrayDecoration>(decos);
-    return expect_type_decl_array(std::move(array_decos));
+    if (array_decos.errored)
+      return nullptr;
+    return expect_type_decl_array(std::move(array_decos.value)).value;
   }
 
   expect_decorations_consumed(decos);
@@ -873,7 +875,7 @@
   if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
       t.IsMat3x3() || t.IsMat3x4() || t.IsMat4x2() || t.IsMat4x3() ||
       t.IsMat4x4()) {
-    return expect_type_decl_matrix(t);
+    return expect_type_decl_matrix(t).value;
   }
 
   auto* texture_or_sampler = texture_sampler_types();
@@ -887,35 +889,33 @@
   return nullptr;
 }
 
-ast::type::Type* ParserImpl::expect_type_decl_pointer() {
+Expect<ast::type::Type*> ParserImpl::expect_type_decl_pointer() {
   const char* use = "ptr declaration";
 
   if (!expect(use, Token::Type::kLessThan))
-    return nullptr;
+    return Failure::kErrored;
 
   auto sc = expect_storage_class(use);
-  if (has_error())
-    return nullptr;
+  if (sc.errored)
+    return Failure::kErrored;
 
   if (!expect(use, Token::Type::kComma))
-    return nullptr;
+    return Failure::kErrored;
 
   auto* subtype = type_decl();
   if (has_error())
-    return nullptr;
-  if (subtype == nullptr) {
-    add_error(peek(), "missing type for ptr declaration");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (subtype == nullptr)
+    return add_error(peek().source(), "missing type", use);
 
   if (!expect(use, Token::Type::kGreaterThan))
-    return nullptr;
+    return Failure::kErrored;
 
   return ctx_.type_mgr().Get(
-      std::make_unique<ast::type::PointerType>(subtype, sc));
+      std::make_unique<ast::type::PointerType>(subtype, sc.value));
 }
 
-ast::type::Type* ParserImpl::expect_type_decl_vector(Token t) {
+Expect<ast::type::Type*> ParserImpl::expect_type_decl_vector(Token t) {
   next();  // Consume the peek
 
   uint32_t count = 2;
@@ -927,53 +927,52 @@
   const char* use = "vector";
 
   if (!expect(use, Token::Type::kLessThan))
-    return nullptr;
+    return Failure::kErrored;
 
   auto* subtype = type_decl();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (subtype == nullptr) {
-    add_error(peek().source(), "unable to determine subtype", use);
-    return nullptr;
+    return add_error(peek().source(), "unable to determine subtype", use);
   }
 
   if (!expect(use, Token::Type::kGreaterThan))
-    return nullptr;
+    return Failure::kErrored;
 
   return ctx_.type_mgr().Get(
       std::make_unique<ast::type::VectorType>(subtype, count));
 }
 
-ast::type::Type* ParserImpl::expect_type_decl_array(
+Expect<ast::type::Type*> ParserImpl::expect_type_decl_array(
     ast::ArrayDecorationList decos) {
   const char* use = "array declaration";
 
   if (!expect(use, Token::Type::kLessThan))
-    return nullptr;
+    return Failure::kErrored;
 
   auto* subtype = type_decl();
   if (has_error())
-    return nullptr;
-  if (subtype == nullptr) {
-    add_error(peek(), "invalid type for array declaration");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (subtype == nullptr)
+    return add_error(peek(), "invalid type for array declaration");
 
   uint32_t size = 0;
   if (match(Token::Type::kComma)) {
-    if (!expect_nonzero_positive_sint("array size", &size))
-      return nullptr;
+    auto val = expect_nonzero_positive_sint("array size");
+    if (val.errored)
+      return Failure::kErrored;
+    size = val.value;
   }
 
   if (!expect(use, Token::Type::kGreaterThan))
-    return nullptr;
+    return Failure::kErrored;
 
   auto ty = std::make_unique<ast::type::ArrayType>(subtype, size);
   ty->set_decorations(std::move(decos));
   return ctx_.type_mgr().Get(std::move(ty));
 }
 
-ast::type::Type* ParserImpl::expect_type_decl_matrix(Token t) {
+Expect<ast::type::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
   next();  // Consume the peek
 
   uint32_t rows = 2;
@@ -990,24 +989,18 @@
   }
 
   t = next();
-  if (!t.IsLessThan()) {
-    add_error(t, "missing < for matrix");
-    return nullptr;
-  }
+  if (!t.IsLessThan())
+    return add_error(t, "missing < for matrix");
 
   auto* subtype = type_decl();
   if (has_error())
-    return nullptr;
-  if (subtype == nullptr) {
-    add_error(peek(), "unable to determine subtype for matrix");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (subtype == nullptr)
+    return add_error(peek(), "unable to determine subtype for matrix");
 
   t = next();
-  if (!t.IsGreaterThan()) {
-    add_error(t, "missing > for matrix");
-    return nullptr;
-  }
+  if (!t.IsGreaterThan())
+    return add_error(t, "missing > for matrix");
 
   return ctx_.type_mgr().Get(
       std::make_unique<ast::type::MatrixType>(subtype, rows, columns));
@@ -1023,7 +1016,8 @@
 //  | IMAGE
 //  | PRIVATE
 //  | FUNCTION
-ast::StorageClass ParserImpl::expect_storage_class(const std::string& use) {
+Expect<ast::StorageClass> ParserImpl::expect_storage_class(
+    const std::string& use) {
   if (match(Token::Type::kIn))
     return ast::StorageClass::kInput;
 
@@ -1051,8 +1045,7 @@
   if (match(Token::Type::kFunction))
     return ast::StorageClass::kFunction;
 
-  add_error(peek().source(), "invalid storage class", use);
-  return ast::StorageClass::kNone;
+  return add_error(peek().source(), "invalid storage class", use);
 }
 
 // struct_decl
@@ -1066,45 +1059,49 @@
     return nullptr;
 
   auto struct_decos = cast_decorations<ast::StructDecoration>(decos);
+  if (struct_decos.errored)
+    return nullptr;
 
-  std::string name;
-  if (!expect_ident("struct declaration", &name))
+  auto name = expect_ident("struct declaration");
+  if (name.errored)
     return nullptr;
 
   auto body = expect_struct_body_decl();
-  if (has_error())
+  if (body.errored)
     return nullptr;
 
   return std::make_unique<ast::type::StructType>(
-      name, std::make_unique<ast::Struct>(source, std::move(struct_decos),
-                                          std::move(body)));
+      name.value,
+      std::make_unique<ast::Struct>(source, std::move(struct_decos.value),
+                                    std::move(body.value)));
 }
 
 // struct_body_decl
 //   : BRACKET_LEFT struct_member* BRACKET_RIGHT
-ast::StructMemberList ParserImpl::expect_struct_body_decl() {
-  return expect_brace_block("struct declaration", [&] {
-    ast::StructMemberList members;
+Expect<ast::StructMemberList> ParserImpl::expect_struct_body_decl() {
+  return expect_brace_block(
+      "struct declaration", [&]() -> Expect<ast::StructMemberList> {
+        ast::StructMemberList members;
 
-    while (!peek().IsBraceRight() && !peek().IsEof()) {
-      auto decos = decoration_list();
-      if (has_error())
-        return ast::StructMemberList{};
+        while (!peek().IsBraceRight() && !peek().IsEof()) {
+          auto decos = decoration_list();
+          if (has_error())
+            return Failure::kErrored;
 
-      auto mem = expect_struct_member(decos);
-      if (has_error())
-        return ast::StructMemberList{};
+          auto mem = expect_struct_member(decos);
+          if (mem.errored)
+            return Failure::kErrored;
 
-      members.push_back(std::move(mem));
-    }
+          members.push_back(std::move(mem.value));
+        }
 
-    return members;
-  });
+        return members;
+      });
 }
 
 // struct_member
 //   : struct_member_decoration_decl+ variable_ident_decl SEMICOLON
-std::unique_ptr<ast::StructMember> ParserImpl::expect_struct_member(
+Expect<std::unique_ptr<ast::StructMember>> ParserImpl::expect_struct_member(
     ast::DecorationList& decos) {
   // FUDGE - Abort early if we enter with an error state to avoid accumulating
   // multiple error messages. This is a work around for the unit tests that
@@ -1118,19 +1115,19 @@
   // resynchronize at the ']]'.
   // TODO(ben-clayton) - remove this once resynchronization is implemented.
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
 
   auto decl = expect_variable_ident_decl("struct member");
-  if (has_error())
-    return nullptr;
+  if (decl.errored)
+    return Failure::kErrored;
 
   auto member_decos = cast_decorations<ast::StructMemberDecoration>(decos);
 
   if (!expect("struct member", Token::Type::kSemicolon))
-    return nullptr;
+    return Failure::kErrored;
 
-  return std::make_unique<ast::StructMember>(decl.source, decl.name, decl.type,
-                                             std::move(member_decos));
+  return std::make_unique<ast::StructMember>(
+      decl->source, decl->name, decl->type, std::move(member_decos.value));
 }
 
 // function_decl
@@ -1142,13 +1139,15 @@
     return nullptr;
 
   auto func_decos = cast_decorations<ast::FunctionDecoration>(decos);
-  f->set_decorations(std::move(func_decos));
+  if (func_decos.errored)
+    return nullptr;
+  f->set_decorations(std::move(func_decos.value));
 
   auto body = expect_body_stmt();
-  if (has_error())
+  if (body.errored)
     return nullptr;
 
-  f->set_body(std::move(body));
+  f->set_body(std::move(body.value));
   return f;
 }
 
@@ -1173,13 +1172,13 @@
 
   const char* use = "function declaration";
 
-  std::string name;
-  if (!expect_ident(use, &name))
+  auto name = expect_ident(use);
+  if (name.errored)
     return nullptr;
 
   auto params = expect_paren_block(use, [&] { return expect_param_list(); });
 
-  if (has_error())
+  if (params.errored)
     return nullptr;
 
   auto t = next();
@@ -1196,24 +1195,25 @@
     return nullptr;
   }
 
-  return std::make_unique<ast::Function>(source, name, std::move(params), type);
+  return std::make_unique<ast::Function>(source, name.value,
+                                         std::move(params.value), type);
 }
 
 // param_list
 //   :
 //   | (variable_ident_decl COMMA)* variable_ident_decl
-ast::VariableList ParserImpl::expect_param_list() {
+Expect<ast::VariableList> ParserImpl::expect_param_list() {
   if (!peek().IsIdentifier())  // Empty list
     return ast::VariableList{};
 
   auto decl = expect_variable_ident_decl("parameter");
-  if (has_error())
-    return {};
+  if (decl.errored)
+    return Failure::kErrored;
 
   ast::VariableList ret;
   for (;;) {
     auto var = std::make_unique<ast::Variable>(
-        decl.source, decl.name, ast::StorageClass::kNone, decl.type);
+        decl->source, decl->name, ast::StorageClass::kNone, decl->type);
     // Formal parameters are treated like a const declaration where the
     // initializer value is provided by the call's argument.  The key point is
     // that it's not updatable after intially set.  This is unlike C or GLSL
@@ -1225,8 +1225,8 @@
       break;
 
     decl = expect_variable_ident_decl("parameter");
-    if (has_error())
-      return {};
+    if (decl.errored)
+      return Failure::kErrored;
   }
 
   return ret;
@@ -1236,7 +1236,7 @@
 //   : VERTEX
 //   | FRAGMENT
 //   | COMPUTE
-std::pair<ast::PipelineStage, Source> ParserImpl::expect_pipeline_stage() {
+Expect<ast::PipelineStage> ParserImpl::expect_pipeline_stage() {
   Source source;
   if (match(Token::Type::kVertex, &source))
     return {ast::PipelineStage::kVertex, source};
@@ -1247,57 +1247,53 @@
   if (match(Token::Type::kCompute, &source))
     return {ast::PipelineStage::kCompute, source};
 
-  auto t = peek();
-  add_error(t, "invalid value for stage decoration");
-  return {ast::PipelineStage::kNone, t.source()};
+  return add_error(peek(), "invalid value for stage decoration");
 }
 
-std::pair<ast::Builtin, Source> ParserImpl::expect_builtin() {
-  Source source;
-  std::string ident;
+Expect<ast::Builtin> ParserImpl::expect_builtin() {
+  auto ident = expect_ident("builtin");
+  if (ident.errored)
+    return Failure::kErrored;
 
-  if (!expect_ident("builtin", &ident, &source))
-    return {ast::Builtin::kNone, source};
-
-  ast::Builtin builtin = ident_to_builtin(ident);
+  ast::Builtin builtin = ident_to_builtin(ident.value);
   if (builtin == ast::Builtin::kNone)
-    add_error(source, "invalid value for builtin decoration");
+    return add_error(ident.source, "invalid value for builtin decoration");
 
-  return {builtin, source};
+  return {builtin, ident.source};
 }
 
 // body_stmt
 //   : BRACKET_LEFT statements BRACKET_RIGHT
-std::unique_ptr<ast::BlockStatement> ParserImpl::expect_body_stmt() {
-  return expect_brace_block("", [&] { return statements(); });
+Expect<std::unique_ptr<ast::BlockStatement>> ParserImpl::expect_body_stmt() {
+  return expect_brace_block("", [&] { return expect_statements(); });
 }
 
 // paren_rhs_stmt
 //   : PAREN_LEFT logical_or_expression PAREN_RIGHT
-std::unique_ptr<ast::Expression> ParserImpl::expect_paren_rhs_stmt() {
-  return expect_paren_block("", [&]() -> std::unique_ptr<ast::Expression> {
-    auto expr = logical_or_expression();
-    if (has_error())
-      return nullptr;
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_paren_rhs_stmt() {
+  return expect_paren_block(
+      "", [&]() -> Expect<std::unique_ptr<ast::Expression>> {
+        auto expr = logical_or_expression();
+        if (has_error())
+          return Failure::kErrored;
 
-    if (expr == nullptr) {
-      add_error(peek(), "unable to parse expression");
-      return nullptr;
-    }
-    return expr;
-  });
+        if (expr == nullptr)
+          return add_error(peek(), "unable to parse expression");
+
+        return expr;
+      });
 }
 
 // statements
 //   : statement*
-std::unique_ptr<ast::BlockStatement> ParserImpl::statements() {
+Expect<std::unique_ptr<ast::BlockStatement>> ParserImpl::expect_statements() {
   auto ret = std::make_unique<ast::BlockStatement>();
 
   for (;;) {
     auto stmt = statement();
     if (has_error())
-      return {};
-    if (stmt == nullptr)
+      return Failure::kErrored;
+    if (!stmt)
       break;
 
     ret->append(std::move(stmt));
@@ -1423,10 +1419,9 @@
   t = peek();
   if (t.IsBraceLeft()) {
     auto body = expect_body_stmt();
-    if (has_error())
+    if (body.errored)
       return nullptr;
-    if (body != nullptr)
-      return body;
+    return std::move(body.value);
   }
 
   return nullptr;
@@ -1473,11 +1468,11 @@
     }
 
     auto var = std::make_unique<ast::Variable>(
-        decl.source, decl.name, ast::StorageClass::kNone, decl.type);
+        decl->source, decl->name, ast::StorageClass::kNone, decl->type);
     var->set_is_const(true);
     var->set_constructor(std::move(constructor));
 
-    return std::make_unique<ast::VariableDeclStatement>(decl.source,
+    return std::make_unique<ast::VariableDeclStatement>(decl->source,
                                                         std::move(var));
   }
 
@@ -1510,11 +1505,11 @@
     return nullptr;
 
   auto condition = expect_paren_rhs_stmt();
-  if (has_error())
+  if (condition.errored)
     return nullptr;
 
   auto body = expect_body_stmt();
-  if (has_error())
+  if (body.errored)
     return nullptr;
 
   auto elseif = elseif_stmt();
@@ -1525,8 +1520,8 @@
   if (has_error())
     return nullptr;
 
-  auto stmt = std::make_unique<ast::IfStatement>(source, std::move(condition),
-                                                 std::move(body));
+  auto stmt = std::make_unique<ast::IfStatement>(
+      source, std::move(condition.value), std::move(body.value));
   if (el != nullptr) {
     elseif.push_back(std::move(el));
   }
@@ -1548,15 +1543,15 @@
     next();  // Consume the peek
 
     auto condition = expect_paren_rhs_stmt();
-    if (has_error())
+    if (condition.errored)
       return {};
 
     auto body = expect_body_stmt();
-    if (has_error())
+    if (body.errored)
       return {};
 
     ret.push_back(std::make_unique<ast::ElseStatement>(
-        source, std::move(condition), std::move(body)));
+        source, std::move(condition.value), std::move(body.value)));
 
     t = peek();
     if (!t.IsElseIf())
@@ -1577,10 +1572,10 @@
   next();  // Consume the peek
 
   auto body = expect_body_stmt();
-  if (has_error())
+  if (body.errored)
     return nullptr;
 
-  return std::make_unique<ast::ElseStatement>(source, std::move(body));
+  return std::make_unique<ast::ElseStatement>(source, std::move(body.value));
 }
 
 // switch_stmt
@@ -1591,11 +1586,11 @@
     return nullptr;
 
   auto condition = expect_paren_rhs_stmt();
-  if (has_error())
+  if (condition.errored)
     return nullptr;
 
   ast::CaseStatementList body;
-  bool ok = expect_brace_block("switch statement", [&] {
+  bool ok = expect_brace_block_old("switch statement", [&] {
     for (;;) {
       auto stmt = switch_body();
       if (has_error())
@@ -1611,8 +1606,8 @@
   if (!ok)
     return nullptr;
 
-  return std::make_unique<ast::SwitchStatement>(source, std::move(condition),
-                                                std::move(body));
+  return std::make_unique<ast::SwitchStatement>(
+      source, std::move(condition.value), std::move(body));
 }
 
 // switch_body
@@ -1629,14 +1624,14 @@
   auto stmt = std::make_unique<ast::CaseStatement>();
   stmt->set_source(source);
   if (t.IsCase()) {
-    auto selectors = case_selectors();
+    auto selectors = expect_case_selectors();
     if (has_error())
       return nullptr;
-    if (selectors.empty()) {
+    if (selectors.value.empty()) {
       add_error(peek(), "unable to parse case selectors");
       return nullptr;
     }
-    stmt->set_selectors(std::move(selectors));
+    stmt->set_selectors(std::move(selectors.value));
   }
 
   const char* use = "case statement";
@@ -1644,7 +1639,7 @@
   if (!expect(use, Token::Type::kColon))
     return nullptr;
 
-  auto body = expect_brace_block(use, [&] { return case_body(); });
+  auto body = expect_brace_block_old(use, [&] { return case_body(); });
 
   if (body == nullptr)
     return nullptr;
@@ -1656,25 +1651,26 @@
 
 // case_selectors
 //   : const_literal (COMMA const_literal)*
-ast::CaseSelectorList ParserImpl::case_selectors() {
+Expect<ast::CaseSelectorList> ParserImpl::expect_case_selectors() {
   ast::CaseSelectorList selectors;
 
   for (;;) {
     auto t = peek();
     auto cond = const_literal();
     if (has_error())
-      return {};
+      return Failure::kErrored;
     if (cond == nullptr)
       break;
-    if (!cond->IsInt()) {
-      add_error(t, "invalid case selector must be an integer value");
-      return {};
-    }
+    if (!cond->IsInt())
+      return add_error(t, "invalid case selector must be an integer value");
 
     std::unique_ptr<ast::IntLiteral> selector(cond.release()->AsInt());
     selectors.push_back(std::move(selector));
   }
 
+  if (selectors.empty())
+    return add_error(peek(), "unable to parse case selectors");
+
   return selectors;
 }
 
@@ -1716,18 +1712,18 @@
   if (!match(Token::Type::kLoop, &source))
     return nullptr;
 
-  return expect_brace_block(
+  return expect_brace_block_old(
       "loop", [&]() -> std::unique_ptr<ast::LoopStatement> {
-        auto body = statements();
-        if (has_error())
+        auto body = expect_statements();
+        if (body.errored)
           return nullptr;
 
         auto continuing = continuing_stmt();
         if (has_error())
           return nullptr;
 
-        return std::make_unique<ast::LoopStatement>(source, std::move(body),
-                                                    std::move(continuing));
+        return std::make_unique<ast::LoopStatement>(
+            source, std::move(body.value), std::move(continuing));
       });
 }
 
@@ -1745,49 +1741,49 @@
 //   SEMICOLON
 //      logical_or_expression? SEMICOLON
 //      (assignment_stmt | func_call_stmt)?
-std::unique_ptr<ForHeader> ParserImpl::expect_for_header() {
+Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
   std::unique_ptr<ast::Statement> initializer = nullptr;
   if (initializer == nullptr) {
     initializer = func_call_stmt();
     if (has_error()) {
-      return nullptr;
+      return Failure::kErrored;
     }
   }
   if (initializer == nullptr) {
     initializer = variable_stmt();
     if (has_error()) {
-      return nullptr;
+      return Failure::kErrored;
     }
   }
   if (initializer == nullptr) {
     initializer = assignment_stmt();
     if (has_error()) {
-      return nullptr;
+      return Failure::kErrored;
     }
   }
 
   if (!expect("initializer in for loop", Token::Type::kSemicolon))
-    return nullptr;
+    return Failure::kErrored;
 
   auto condition = logical_or_expression();
   if (has_error()) {
-    return nullptr;
+    return Failure::kErrored;
   }
 
   if (!expect("condition in for loop", Token::Type::kSemicolon))
-    return nullptr;
+    return Failure::kErrored;
 
   std::unique_ptr<ast::Statement> continuing = nullptr;
   if (continuing == nullptr) {
     continuing = func_call_stmt();
     if (has_error()) {
-      return nullptr;
+      return Failure::kErrored;
     }
   }
   if (continuing == nullptr) {
     continuing = assignment_stmt();
     if (has_error()) {
-      return nullptr;
+      return Failure::kErrored;
     }
   }
 
@@ -1804,23 +1800,23 @@
 
   auto header =
       expect_paren_block("for loop", [&] { return expect_for_header(); });
-  if (header == nullptr)
+  if (header.errored)
     return nullptr;
 
-  auto body = expect_brace_block("for loop", [&] { return statements(); });
+  auto body =
+      expect_brace_block("for loop", [&] { return expect_statements(); });
 
-  if (body == nullptr)
+  if (body.errored)
     return nullptr;
 
   // The for statement is a syntactic sugar on top of the loop statement.
   // We create corresponding nodes in ast with the exact same behaviour
   // as we would expect from the loop statement.
-
-  if (header->condition != nullptr) {
+  if (header.value->condition != nullptr) {
     // !condition
     auto not_condition = std::make_unique<ast::UnaryOpExpression>(
-        header->condition->source(), ast::UnaryOp::kNot,
-        std::move(header->condition));
+        header.value->condition->source(), ast::UnaryOp::kNot,
+        std::move(header.value->condition));
     // { break; }
     auto break_stmt =
         std::make_unique<ast::BreakStatement>(not_condition->source());
@@ -1831,22 +1827,22 @@
     auto break_if_not_condition = std::make_unique<ast::IfStatement>(
         not_condition->source(), std::move(not_condition),
         std::move(break_body));
-    body->insert(0, std::move(break_if_not_condition));
+    body.value->insert(0, std::move(break_if_not_condition));
   }
 
   std::unique_ptr<ast::BlockStatement> continuing_body = nullptr;
-  if (header->continuing != nullptr) {
-    continuing_body =
-        std::make_unique<ast::BlockStatement>(header->continuing->source());
-    continuing_body->append(std::move(header->continuing));
+  if (header.value->continuing != nullptr) {
+    continuing_body = std::make_unique<ast::BlockStatement>(
+        header.value->continuing->source());
+    continuing_body->append(std::move(header.value->continuing));
   }
 
-  auto loop = std::make_unique<ast::LoopStatement>(source, std::move(body),
-                                                   std::move(continuing_body));
+  auto loop = std::make_unique<ast::LoopStatement>(
+      source, std::move(body.value), std::move(continuing_body));
 
-  if (header->initializer != nullptr) {
+  if (header.value->initializer != nullptr) {
     auto result = std::make_unique<ast::BlockStatement>(source);
-    result->append(std::move(header->initializer));
+    result->append(std::move(header.value->initializer));
     result->append(std::move(loop));
     return result;
   }
@@ -1869,12 +1865,14 @@
 
   auto name = t.to_str();
 
-  t = peek();
   ast::ExpressionList params;
+
+  t = peek();
   if (!t.IsParenRight() && !t.IsEof()) {
-    params = expect_argument_expression_list();
-    if (has_error())
+    auto list = expect_argument_expression_list();
+    if (list.errored)
       return nullptr;
+    params = std::move(list.value);
   }
 
   if (!expect("call statement", Token::Type::kParenRight))
@@ -1912,7 +1910,7 @@
   if (!match(Token::Type::kContinuing))
     return std::make_unique<ast::BlockStatement>();
 
-  return expect_body_stmt();
+  return expect_body_stmt().value;
 }
 
 // primary_expression
@@ -1936,10 +1934,10 @@
   t = peek();
   if (t.IsParenLeft()) {
     auto paren = expect_paren_rhs_stmt();
-    if (has_error())
+    if (paren.errored)
       return nullptr;
 
-    return paren;
+    return std::move(paren.value);
   }
 
   if (t.IsBitcast()) {
@@ -1968,11 +1966,11 @@
     }
 
     auto params = expect_paren_rhs_stmt();
-    if (has_error())
+    if (params.errored)
       return nullptr;
 
     return std::make_unique<ast::BitcastExpression>(source, type,
-                                                    std::move(params));
+                                                    std::move(params.value));
   } else if (t.IsIdentifier()) {
     next();  // Consume the peek
 
@@ -1983,24 +1981,26 @@
   if (has_error())
     return nullptr;
   if (type != nullptr) {
-    ast::ExpressionList params;
+    auto expr = expect_paren_block(
+        "type constructor",
+        [&]() -> Expect<std::unique_ptr<ast::TypeConstructorExpression>> {
+          t = peek();
+          if (t.IsParenRight() || t.IsEof())
+            return std::make_unique<ast::TypeConstructorExpression>(
+                source, type, ast::ExpressionList{});
 
-    auto ok = expect_paren_block("type constructor", [&] {
-      t = peek();
-      if (!t.IsParenRight() && !t.IsEof()) {
-        params = expect_argument_expression_list();
-        if (has_error())
-          return false;
-      }
-      return true;
-    });
+          auto params = expect_argument_expression_list();
+          if (params.errored)
+            return Failure::kErrored;
 
-    if (!ok) {
+          return std::make_unique<ast::TypeConstructorExpression>(
+              source, type, std::move(params.value));
+        });
+
+    if (expr.errored)
       return nullptr;
-    }
 
-    return std::make_unique<ast::TypeConstructorExpression>(source, type,
-                                                            std::move(params));
+    return std::move(expr.value);
   }
   return nullptr;
 }
@@ -2038,12 +2038,14 @@
   } else if (t.IsParenLeft()) {
     next();  // Consume the peek
 
-    t = peek();
     ast::ExpressionList params;
+
+    t = peek();
     if (!t.IsParenRight() && !t.IsEof()) {
-      params = expect_argument_expression_list();
-      if (has_error())
+      auto list = expect_argument_expression_list();
+      if (list.errored)
         return nullptr;
+      params = std::move(list.value);
     }
 
     if (!expect("call expression", Token::Type::kParenRight))
@@ -2054,13 +2056,13 @@
   } else if (t.IsPeriod()) {
     next();  // Consume the peek
 
-    std::string ident;
-    if (!expect_ident("member accessor", &ident, &source))
+    auto ident = expect_ident("member accessor");
+    if (ident.errored)
       return nullptr;
 
     expr = std::make_unique<ast::MemberAccessorExpression>(
-        source, std::move(prefix),
-        std::make_unique<ast::IdentifierExpression>(source, ident));
+        ident.source, std::move(prefix),
+        std::make_unique<ast::IdentifierExpression>(ident.source, ident.value));
   } else {
     return prefix;
   }
@@ -2081,14 +2083,12 @@
 
 // argument_expression_list
 //   : (logical_or_expression COMMA)* logical_or_expression
-ast::ExpressionList ParserImpl::expect_argument_expression_list() {
+Expect<ast::ExpressionList> ParserImpl::expect_argument_expression_list() {
   auto arg = logical_or_expression();
   if (has_error())
-    return {};
-  if (arg == nullptr) {
-    add_error(peek(), "unable to parse argument expression");
-    return {};
-  }
+    return Failure::kErrored;
+  if (arg == nullptr)
+    return add_error(peek(), "unable to parse argument expression");
 
   ast::ExpressionList ret;
   ret.push_back(std::move(arg));
@@ -2096,10 +2096,10 @@
   while (match(Token::Type::kComma)) {
     arg = logical_or_expression();
     if (has_error())
-      return {};
+      return Failure::kErrored;
     if (arg == nullptr) {
-      add_error(peek(), "unable to parse argument expression after comma");
-      return {};
+      return add_error(peek(),
+                       "unable to parse argument expression after comma");
     }
     ret.push_back(std::move(arg));
   }
@@ -2141,7 +2141,7 @@
 //   | STAR unary_expression multiplicative_expr
 //   | FORWARD_SLASH unary_expression multiplicative_expr
 //   | MODULO unary_expression multiplicative_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_multiplicative_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_multiplicative_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
 
@@ -2161,10 +2161,10 @@
 
   auto rhs = unary_expression();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of " + name + " expression");
-    return nullptr;
+    return add_error(peek(),
+                     "unable to parse right side of " + name + " expression");
   }
   return expect_multiplicative_expr(std::make_unique<ast::BinaryExpression>(
       source, op, std::move(lhs), std::move(rhs)));
@@ -2179,14 +2179,14 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_multiplicative_expr(std::move(lhs));
+  return expect_multiplicative_expr(std::move(lhs)).value;
 }
 
 // additive_expr
 //   :
 //   | PLUS multiplicative_expression additive_expr
 //   | MINUS multiplicative_expression additive_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_additive_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_additive_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
 
@@ -2203,11 +2203,9 @@
 
   auto rhs = multiplicative_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of + expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of + expression");
   return expect_additive_expr(std::make_unique<ast::BinaryExpression>(
       source, op, std::move(lhs), std::move(rhs)));
 }
@@ -2221,14 +2219,14 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_additive_expr(std::move(lhs));
+  return expect_additive_expr(std::move(lhs)).value;
 }
 
 // shift_expr
 //   :
 //   | LESS_THAN LESS_THAN additive_expression shift_expr
 //   | GREATER_THAN GREATER_THAN additive_expression shift_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_shift_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_shift_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   auto source = t.source();
@@ -2252,11 +2250,10 @@
 
   auto rhs = additive_expression();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (rhs == nullptr) {
-    add_error(peek(), std::string("unable to parse right side of ") + name +
-                          " expression");
-    return nullptr;
+    return add_error(peek(), std::string("unable to parse right side of ") +
+                                 name + " expression");
   }
   return expect_shift_expr(std::make_unique<ast::BinaryExpression>(
       source, op, std::move(lhs), std::move(rhs)));
@@ -2271,7 +2268,7 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_shift_expr(std::move(lhs));
+  return expect_shift_expr(std::move(lhs)).value;
 }
 
 // relational_expr
@@ -2280,7 +2277,7 @@
 //   | GREATER_THAN shift_expression relational_expr
 //   | LESS_THAN_EQUAL shift_expression relational_expr
 //   | GREATER_THAN_EQUAL shift_expression relational_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_relational_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_relational_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   ast::BinaryOp op = ast::BinaryOp::kNone;
@@ -2301,11 +2298,12 @@
 
   auto rhs = shift_expression();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of " + name + " expression");
-    return nullptr;
+    return add_error(peek(),
+                     "unable to parse right side of " + name + " expression");
   }
+
   return expect_relational_expr(std::make_unique<ast::BinaryExpression>(
       source, op, std::move(lhs), std::move(rhs)));
 }
@@ -2319,14 +2317,14 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_relational_expr(std::move(lhs));
+  return expect_relational_expr(std::move(lhs)).value;
 }
 
 // equality_expr
 //   :
 //   | EQUAL_EQUAL relational_expression equality_expr
 //   | NOT_EQUAL relational_expression equality_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_equality_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_equality_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   ast::BinaryOp op = ast::BinaryOp::kNone;
@@ -2343,11 +2341,12 @@
 
   auto rhs = relational_expression();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of " + name + " expression");
-    return nullptr;
+    return add_error(peek(),
+                     "unable to parse right side of " + name + " expression");
   }
+
   return expect_equality_expr(std::make_unique<ast::BinaryExpression>(
       source, op, std::move(lhs), std::move(rhs)));
 }
@@ -2361,13 +2360,13 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_equality_expr(std::move(lhs));
+  return expect_equality_expr(std::move(lhs)).value;
 }
 
 // and_expr
 //   :
 //   | AND equality_expression and_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_and_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_and_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   if (!t.IsAnd())
@@ -2378,11 +2377,10 @@
 
   auto rhs = equality_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of & expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of & expression");
+
   return expect_and_expr(std::make_unique<ast::BinaryExpression>(
       source, ast::BinaryOp::kAnd, std::move(lhs), std::move(rhs)));
 }
@@ -2396,13 +2394,13 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_and_expr(std::move(lhs));
+  return expect_and_expr(std::move(lhs)).value;
 }
 
 // exclusive_or_expr
 //   :
 //   | XOR and_expression exclusive_or_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_exclusive_or_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_exclusive_or_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   if (!t.IsXor())
@@ -2413,11 +2411,10 @@
 
   auto rhs = and_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of ^ expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of ^ expression");
+
   return expect_exclusive_or_expr(std::make_unique<ast::BinaryExpression>(
       source, ast::BinaryOp::kXor, std::move(lhs), std::move(rhs)));
 }
@@ -2431,13 +2428,13 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_exclusive_or_expr(std::move(lhs));
+  return expect_exclusive_or_expr(std::move(lhs)).value;
 }
 
 // inclusive_or_expr
 //   :
 //   | OR exclusive_or_expression inclusive_or_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_inclusive_or_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_inclusive_or_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   if (!t.IsOr())
@@ -2448,11 +2445,10 @@
 
   auto rhs = exclusive_or_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of | expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of | expression");
+
   return expect_inclusive_or_expr(std::make_unique<ast::BinaryExpression>(
       source, ast::BinaryOp::kOr, std::move(lhs), std::move(rhs)));
 }
@@ -2466,13 +2462,13 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_inclusive_or_expr(std::move(lhs));
+  return expect_inclusive_or_expr(std::move(lhs)).value;
 }
 
 // logical_and_expr
 //   :
 //   | AND_AND inclusive_or_expression logical_and_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_logical_and_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_logical_and_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   if (!t.IsAndAnd())
@@ -2483,11 +2479,10 @@
 
   auto rhs = inclusive_or_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of && expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of && expression");
+
   return expect_logical_and_expr(std::make_unique<ast::BinaryExpression>(
       source, ast::BinaryOp::kLogicalAnd, std::move(lhs), std::move(rhs)));
 }
@@ -2501,13 +2496,13 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_logical_and_expr(std::move(lhs));
+  return expect_logical_and_expr(std::move(lhs)).value;
 }
 
 // logical_or_expr
 //   :
 //   | OR_OR logical_and_expression logical_or_expr
-std::unique_ptr<ast::Expression> ParserImpl::expect_logical_or_expr(
+Expect<std::unique_ptr<ast::Expression>> ParserImpl::expect_logical_or_expr(
     std::unique_ptr<ast::Expression> lhs) {
   auto t = peek();
   if (!t.IsOrOr())
@@ -2518,11 +2513,10 @@
 
   auto rhs = logical_and_expression();
   if (has_error())
-    return nullptr;
-  if (rhs == nullptr) {
-    add_error(peek(), "unable to parse right side of || expression");
-    return nullptr;
-  }
+    return Failure::kErrored;
+  if (rhs == nullptr)
+    return add_error(peek(), "unable to parse right side of || expression");
+
   return expect_logical_or_expr(std::make_unique<ast::BinaryExpression>(
       source, ast::BinaryOp::kLogicalOr, std::move(lhs), std::move(rhs)));
 }
@@ -2536,7 +2530,7 @@
   if (lhs == nullptr)
     return nullptr;
 
-  return expect_logical_or_expr(std::move(lhs));
+  return expect_logical_or_expr(std::move(lhs)).value;
 }
 
 // assignment_stmt
@@ -2603,51 +2597,52 @@
 // const_expr
 //   : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
 //   | const_literal
-std::unique_ptr<ast::ConstructorExpression> ParserImpl::expect_const_expr() {
+Expect<std::unique_ptr<ast::ConstructorExpression>>
+ParserImpl::expect_const_expr() {
   return expect_const_expr_internal(0);
 }
 
-std::unique_ptr<ast::ConstructorExpression>
+Expect<std::unique_ptr<ast::ConstructorExpression>>
 ParserImpl::expect_const_expr_internal(uint32_t depth) {
   auto t = peek();
 
   if (depth > kMaxConstExprDepth) {
-    add_error(t, "max const_expr depth reached");
-    return nullptr;
+    return add_error(t, "max const_expr depth reached");
   }
 
   auto source = t.source();
 
   auto* type = type_decl();
   if (type != nullptr) {
-    ast::ExpressionList params;
-    bool ok = expect_paren_block("type constructor", [&] {
-      auto param = expect_const_expr_internal(depth + 1);
-      if (has_error())
-        return false;
-      params.push_back(std::move(param));
-      while (match(Token::Type::kComma)) {
-        param = expect_const_expr_internal(depth + 1);
-        if (has_error())
-          return false;
-        params.push_back(std::move(param));
-      }
-      return true;
-    });
+    auto params = expect_paren_block(
+        "type constructor", [&]() -> Expect<ast::ExpressionList> {
+          ast::ExpressionList list;
+          auto param = expect_const_expr_internal(depth + 1);
+          if (param.errored)
+            return Failure::kErrored;
+          list.emplace_back(std::move(param.value));
+          while (match(Token::Type::kComma)) {
+            param = expect_const_expr_internal(depth + 1);
+            if (param.errored)
+              return Failure::kErrored;
+            list.emplace_back(std::move(param.value));
+          }
+          return list;
+        });
 
-    if (!ok)
-      return nullptr;
+    if (params.errored)
+      return Failure::kErrored;
 
-    return std::make_unique<ast::TypeConstructorExpression>(source, type,
-                                                            std::move(params));
+    return std::make_unique<ast::TypeConstructorExpression>(
+        source, type, std::move(params.value));
   }
 
   auto lit = const_literal();
   if (has_error())
-    return nullptr;
+    return Failure::kErrored;
   if (lit == nullptr) {
     add_error(peek(), "unable to parse const literal");
-    return nullptr;
+    return Failure::kErrored;
   }
   return std::make_unique<ast::ScalarConstructorExpression>(source,
                                                             std::move(lit));
@@ -2672,11 +2667,11 @@
   }
 
   while (true) {
-    if (auto deco = expect_decoration()) {
-      decos.emplace_back(std::move(deco));
-    } else {
+    auto deco = expect_decoration();
+    if (deco.errored)
       return false;
-    }
+
+    decos.emplace_back(std::move(deco.value));
 
     if (match(Token::Type::kComma)) {
       continue;
@@ -2694,85 +2689,94 @@
   }
 }
 
-std::unique_ptr<ast::Decoration> ParserImpl::expect_decoration() {
+Expect<std::unique_ptr<ast::Decoration>> ParserImpl::expect_decoration() {
   auto t = peek();
-  if (auto deco = decoration()) {
-    return deco;
-  }
-  if (!has_error()) {
-    add_error(t, "expected decoration");
-  }
-  return nullptr;
+  auto deco = decoration();
+  if (has_error())
+    return Failure::kErrored;
+  if (deco == nullptr)
+    return add_error(t, "expected decoration");
+  return std::move(deco);
 }
 
 std::unique_ptr<ast::Decoration> ParserImpl::decoration() {
+  using Result = std::unique_ptr<ast::Decoration>;
   auto t = next();
   if (t.IsLocation()) {
     const char* use = "location decoration";
-    return expect_paren_block(use, [&]() {
-      uint32_t val;
-      bool ok = expect_positive_sint(use, &val);
-      return ok ? std::make_unique<ast::LocationDecoration>(val, t.source())
-                : nullptr;
+    return expect_paren_block_old(use, [&]() -> Result {
+      auto val = expect_positive_sint(use);
+      if (val.errored)
+        return nullptr;
+
+      return std::make_unique<ast::LocationDecoration>(val.value, val.source);
     });
   }
   if (t.IsBinding()) {
     const char* use = "binding decoration";
-    return expect_paren_block(use, [&]() {
-      uint32_t val;
-      bool ok = expect_positive_sint(use, &val);
-      return ok ? std::make_unique<ast::BindingDecoration>(val, t.source())
-                : nullptr;
+    return expect_paren_block_old(use, [&]() -> Result {
+      auto val = expect_positive_sint(use);
+      if (val.errored)
+        return nullptr;
+
+      return std::make_unique<ast::BindingDecoration>(val.value, val.source);
     });
   }
   if (t.IsSet()) {
     const char* use = "set decoration";
-    return expect_paren_block(use, [&]() {
-      uint32_t val;
-      bool ok = expect_positive_sint(use, &val);
-      return ok ? std::make_unique<ast::SetDecoration>(val, t.source())
-                : nullptr;
+    return expect_paren_block_old(use, [&]() -> Result {
+      auto val = expect_positive_sint(use);
+      if (val.errored)
+        return nullptr;
+
+      return std::make_unique<ast::SetDecoration>(val.value, val.source);
     });
   }
   if (t.IsBuiltin()) {
-    return expect_paren_block("builtin decoration", [&]() {
-      ast::Builtin builtin;
-      Source source;
-      std::tie(builtin, source) = expect_builtin();
-      return (builtin != ast::Builtin::kNone)
-                 ? std::make_unique<ast::BuiltinDecoration>(builtin, source)
-                 : nullptr;
+    return expect_paren_block_old("builtin decoration", [&]() -> Result {
+      auto builtin = expect_builtin();
+      if (builtin.errored)
+        return nullptr;
+
+      return std::make_unique<ast::BuiltinDecoration>(builtin.value,
+                                                      builtin.source);
     });
   }
   if (t.IsWorkgroupSize()) {
-    return expect_paren_block("workgroup_size decoration", [&]() {
+    return expect_paren_block_old("workgroup_size decoration", [&]() -> Result {
       uint32_t x;
-      if (!expect_nonzero_positive_sint("workgroup_size x parameter", &x)) {
-        return std::unique_ptr<ast::WorkgroupDecoration>(nullptr);
-      }
       uint32_t y = 1;
       uint32_t z = 1;
+
+      auto val = expect_nonzero_positive_sint("workgroup_size x parameter");
+      if (val.errored)
+        return nullptr;
+      x = val.value;
+
       if (match(Token::Type::kComma)) {
-        if (!expect_nonzero_positive_sint("workgroup_size y parameter", &y)) {
-          return std::unique_ptr<ast::WorkgroupDecoration>(nullptr);
-        }
+        val = expect_nonzero_positive_sint("workgroup_size y parameter");
+        if (val.errored)
+          return nullptr;
+        y = val.value;
+
         if (match(Token::Type::kComma)) {
-          if (!expect_nonzero_positive_sint("workgroup_size z parameter", &z)) {
-            return std::unique_ptr<ast::WorkgroupDecoration>(nullptr);
-          }
+          val = expect_nonzero_positive_sint("workgroup_size z parameter");
+          if (val.errored)
+            return nullptr;
+          z = val.value;
         }
       }
+
       return std::make_unique<ast::WorkgroupDecoration>(x, y, z, t.source());
     });
   }
   if (t.IsStage()) {
-    return expect_paren_block("stage decoration", [&]() {
-      ast::PipelineStage stage;
-      Source source;
-      std::tie(stage, source) = expect_pipeline_stage();
-      return (stage != ast::PipelineStage::kNone)
-                 ? std::make_unique<ast::StageDecoration>(stage, source)
-                 : nullptr;
+    return expect_paren_block_old("stage decoration", [&]() -> Result {
+      auto stage = expect_pipeline_stage();
+      if (stage.errored)
+        return nullptr;
+
+      return std::make_unique<ast::StageDecoration>(stage.value, stage.source);
     });
   }
   if (t.IsBlock()) {
@@ -2780,29 +2784,32 @@
   }
   if (t.IsStride()) {
     const char* use = "stride decoration";
-    return expect_paren_block(use, [&]() {
-      uint32_t val;
-      bool ok = expect_nonzero_positive_sint(use, &val);
-      return ok ? std::make_unique<ast::StrideDecoration>(val, t.source())
-                : nullptr;
+    return expect_paren_block_old(use, [&]() -> Result {
+      auto val = expect_nonzero_positive_sint(use);
+      if (val.errored)
+        return nullptr;
+
+      return std::make_unique<ast::StrideDecoration>(val.value, t.source());
     });
   }
   if (t.IsOffset()) {
     const char* use = "offset decoration";
-    return expect_paren_block(use, [&]() {
-      uint32_t val;
-      bool ok = expect_positive_sint(use, &val);
-      return ok ? std::make_unique<ast::StructMemberOffsetDecoration>(
-                      val, t.source())
-                : nullptr;
+    return expect_paren_block_old(use, [&]() -> Result {
+      auto val = expect_positive_sint(use);
+      if (val.errored)
+        return nullptr;
+
+      return std::make_unique<ast::StructMemberOffsetDecoration>(val.value,
+                                                                 t.source());
     });
   }
   return nullptr;
 }
 
 template <typename T>
-std::vector<std::unique_ptr<T>> ParserImpl::cast_decorations(
+Expect<std::vector<std::unique_ptr<T>>> ParserImpl::cast_decorations(
     ast::DecorationList& in) {
+  bool ok = true;
   std::vector<std::unique_ptr<T>> out;
   out.reserve(in.size());
   for (auto& deco : in) {
@@ -2811,6 +2818,7 @@
       msg << deco->GetKind() << " decoration type cannot be used for "
           << T::Kind;
       add_error(deco->GetSource(), msg.str());
+      ok = false;
       continue;
     }
     out.emplace_back(ast::As<T>(std::move(deco)));
@@ -2818,6 +2826,10 @@
   // clear in so that we can verify decorations were consumed with
   // expect_decorations_consumed()
   in.clear();
+
+  if (!ok)
+    return Failure::kErrored;
+
   return out;
 }
 
@@ -2856,67 +2868,51 @@
   return true;
 }
 
-bool ParserImpl::expect_sint(const std::string& use, int32_t* out) {
-  auto t = next();
-  if (!t.IsSintLiteral()) {
-    add_error(t.source(), "expected signed integer literal", use);
-    return false;
-  }
-  *out = t.to_i32();
-  return true;
-}
-
-bool ParserImpl::expect_positive_sint(const std::string& use, uint32_t* out) {
-  auto t = peek();
-  int32_t val;
-  if (!expect_sint(use, &val))
-    return false;
-
-  if (val < 0) {
-    add_error(t, use + " must be positive");
-    return false;
-  }
-  *out = static_cast<uint32_t>(val);
-  return true;
-}
-
-bool ParserImpl::expect_nonzero_positive_sint(const std::string& use,
-                                              uint32_t* out) {
-  auto t = peek();
-  int32_t val;
-  if (!expect_sint(use, &val))
-    return false;
-
-  if (val <= 0) {
-    add_error(t, use + " must be greater than 0");
-    return false;
-  }
-  *out = static_cast<uint32_t>(val);
-  return true;
-}
-
-bool ParserImpl::expect_ident(const std::string& use,
-                              std::string* out,
-                              Source* source /* = nullptr */) {
+Expect<int32_t> ParserImpl::expect_sint(const std::string& use) {
   auto t = next();
 
-  if (source != nullptr)
-    *source = t.source();
+  if (!t.IsSintLiteral())
+    return add_error(t.source(), "expected signed integer literal", use);
 
-  if (!t.IsIdentifier()) {
-    add_error(t.source(), "expected identifier", use);
-    return false;
-  }
+  return {t.to_i32(), t.source()};
+}
 
-  *out = t.to_str();
-  return true;
+Expect<uint32_t> ParserImpl::expect_positive_sint(const std::string& use) {
+  auto sint = expect_sint(use);
+  if (sint.errored)
+    return Failure::kErrored;
+
+  if (sint.value < 0)
+    return add_error(sint.source, use + " must be positive");
+
+  return {static_cast<uint32_t>(sint.value), sint.source};
+}
+
+Expect<uint32_t> ParserImpl::expect_nonzero_positive_sint(
+    const std::string& use) {
+  auto sint = expect_sint(use);
+  if (sint.errored)
+    return Failure::kErrored;
+
+  if (sint.value <= 0)
+    return add_error(sint.source, use + " must be greater than 0");
+
+  return {static_cast<uint32_t>(sint.value), sint.source};
+}
+
+Expect<std::string> ParserImpl::expect_ident(const std::string& use) {
+  auto t = next();
+  if (!t.IsIdentifier())
+    return add_error(t.source(), "expected identifier", use);
+
+  return {t.to_str(), t.source()};
 }
 
 template <typename F, typename T>
-T ParserImpl::expect_block(Token::Type start,
-                           Token::Type end,
-                           const std::string& use,
-                           F&& body) {
+T ParserImpl::expect_block_old(Token::Type start,
+                               Token::Type end,
+                               const std::string& use,
+                               F&& body) {
   if (!expect(use, start)) {
     return {};
   }
@@ -2931,6 +2927,36 @@
 }
 
 template <typename F, typename T>
+T ParserImpl::expect_block(Token::Type start,
+                           Token::Type end,
+                           const std::string& use,
+                           F&& body) {
+  if (!expect(use, start)) {
+    return Failure::kErrored;
+  }
+  auto res = body();
+  if (res.errored) {
+    return Failure::kErrored;
+  }
+  if (!expect(use, end)) {
+    return Failure::kErrored;
+  }
+  return res;
+}
+
+template <typename F, typename T>
+T ParserImpl::expect_paren_block_old(const std::string& use, F&& body) {
+  return expect_block_old(Token::Type::kParenLeft, Token::Type::kParenRight,
+                          use, std::forward<F>(body));
+}
+
+template <typename F, typename T>
+T ParserImpl::expect_brace_block_old(const std::string& use, F&& body) {
+  return expect_block_old(Token::Type::kBraceLeft, Token::Type::kBraceRight,
+                          use, std::forward<F>(body));
+}
+
+template <typename F, typename T>
 T ParserImpl::expect_paren_block(const std::string& use, F&& body) {
   return expect_block(Token::Type::kParenLeft, Token::Type::kParenRight, use,
                       std::forward<F>(body));
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 5e1b941..5bddd68 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -15,6 +15,7 @@
 #ifndef SRC_READER_WGSL_PARSER_IMPL_H_
 #define SRC_READER_WGSL_PARSER_IMPL_H_
 
+#include <cassert>
 #include <deque>
 #include <memory>
 #include <string>
@@ -79,7 +80,61 @@
 
 /// ParserImpl for WGSL source data
 class ParserImpl {
+  /// Failure holds enumerator values used for the constructing an Expect in the
+  /// errored state.
+  struct Failure {
+    enum Errored { kErrored };
+  };
+
  public:
+  /// Expect is the return type of the parser methods that are expected to
+  /// return a parsed value of type T, unless there was an parse error.
+  /// In the case of a parse error the called method will have called
+  /// |add_error()| and the Expect will have |errored| set to true.
+  template <typename T>
+  struct Expect {
+    /// An alias to the templated type T.
+    using type = T;
+
+    /// Don't allow an Expect to take a nullptr.
+    inline Expect(std::nullptr_t) = delete;  // NOLINT
+
+    /// Constructor for a successful parse.
+    /// @param val the result value of the parse
+    /// @param s the optional source of the value
+    template <typename U>
+    inline Expect(U&& val, const Source& s = {})  // NOLINT
+        : value(std::forward<U>(val)), source(s) {}
+
+    /// Constructor for parse error.
+    inline Expect(Failure::Errored) : errored(true) {}  // NOLINT
+
+    /// Copy constructor
+    inline Expect(const Expect&) = default;
+    /// Move constructor
+    inline Expect(Expect&&) = default;
+    /// Assignment operator
+    /// @return this Expect
+    inline Expect& operator=(const Expect&) = default;
+    /// Assignment move operator
+    /// @return this Expect
+    inline Expect& operator=(Expect&&) = default;
+
+    /// @return a pointer to |value|. |errored| must be false to call.
+    inline T* operator->() {
+      assert(!errored);
+      return &value;
+    }
+
+    /// The expected value of a successful parse.
+    /// Zero-initialized when there was a parse error.
+    T value{};
+    /// Optional source of the value.
+    Source source;
+    /// True if there was a error parsing.
+    bool errored = false;
+  };
+
   /// TypedIdentifier holds a parsed identifier and type. Returned by
   /// variable_ident_decl().
   struct TypedIdentifier {
@@ -130,19 +185,25 @@
   /// Appends an error at |t| with the message |msg|
   /// @param t the token to associate the error with
   /// @param msg the error message
-  void add_error(const Token& t, const std::string& msg);
+  /// @return |errored| so that you can combine an add_error call and return on
+  /// the same line.
+  Failure::Errored add_error(const Token& t, const std::string& msg);
   /// Appends an error raised when parsing |use| at |t| with the message |msg|
   /// @param source the source to associate the error with
   /// @param msg the error message
   /// @param use a description of what was being parsed when the error was
   /// raised.
-  void add_error(const Source& source,
-                 const std::string& msg,
-                 const std::string& use);
+  /// @return |errored| so that you can combine an add_error call and return on
+  /// the same line.
+  Failure::Errored add_error(const Source& source,
+                             const std::string& msg,
+                             const std::string& use);
   /// Appends an error at |source| with the message |msg|
   /// @param source the source to associate the error with
   /// @param msg the error message
-  void add_error(const Source& source, const std::string& msg);
+  /// @return |errored| so that you can combine an add_error call and return on
+  /// the same line.
+  Failure::Errored add_error(const Source& source, const std::string& msg);
 
   /// Registers a constructed type into the parser
   /// @param name the constructed name
@@ -156,7 +217,8 @@
   /// Parses the `translation_unit` grammar element
   void translation_unit();
   /// Parses the `global_decl` grammar element, erroring on parse failure.
-  void expect_global_decl();
+  /// @return true on parse success, otherwise an error.
+  Expect<bool> expect_global_decl();
   /// Parses a `global_variable_decl` grammar element with the initial
   /// `variable_decoration_list*` provided as |decos|.
   /// @returns the variable parsed or nullptr
@@ -173,7 +235,7 @@
   /// failure.
   /// @param use a description of what was being parsed if an error was raised.
   /// @returns the identifier and type parsed or empty otherwise
-  TypedIdentifier expect_variable_ident_decl(const std::string& use);
+  Expect<TypedIdentifier> expect_variable_ident_decl(const std::string& use);
   /// Parses a `variable_storage_decoration` grammar element
   /// @returns the storage class or StorageClass::kNone if none matched
   ast::StorageClass variable_storage_decoration();
@@ -183,10 +245,10 @@
   /// Parses a `type_decl` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   ast::type::Type* type_decl();
-  /// Parses a `storage_class` grammar element
-  /// @param use a description of what was being parsed if an error was raised
+  /// Parses a `storage_class` grammar element, erroring on parse failure.
+  /// @param use a description of what was being parsed if an error was raised.
   /// @returns the storage class or StorageClass::kNone if none matched
-  ast::StorageClass expect_storage_class(const std::string& use);
+  Expect<ast::StorageClass> expect_storage_class(const std::string& use);
   /// Parses a `struct_decl` grammar element with the initial
   /// `struct_decoration_decl*` provided as |decos|.
   /// @returns the struct type or nullptr on error
@@ -195,13 +257,13 @@
       ast::DecorationList& decos);
   /// Parses a `struct_body_decl` grammar element, erroring on parse failure.
   /// @returns the struct members
-  ast::StructMemberList expect_struct_body_decl();
+  Expect<ast::StructMemberList> expect_struct_body_decl();
   /// Parses a `struct_member` grammar element with the initial
   /// `struct_member_decoration_decl+` provided as |decos|, erroring on parse
   /// failure.
   /// @param decos the list of decorations for the struct member.
   /// @returns the struct member or nullptr
-  std::unique_ptr<ast::StructMember> expect_struct_member(
+  Expect<std::unique_ptr<ast::StructMember>> expect_struct_member(
       ast::DecorationList& decos);
   /// Parses a `function_decl` grammar element with the initial
   /// `function_decoration_decl*` provided as |decos|.
@@ -230,8 +292,10 @@
   /// @returns the parsed Type or nullptr if none matched.
   ast::type::Type* depth_texture_type();
   /// Parses a `image_storage_type` grammar element
+  /// @param use a description of what was being parsed if an error was raised.
   /// @returns returns the image format or kNone if none matched.
-  ast::type::ImageFormat image_storage_type();
+  Expect<ast::type::ImageFormat> expect_image_storage_type(
+      const std::string& use);
   /// Parses a `function_type_decl` grammar element
   /// @returns the parsed type or nullptr otherwise
   ast::type::Type* function_type_decl();
@@ -240,26 +304,24 @@
   std::unique_ptr<ast::Function> function_header();
   /// Parses a `param_list` grammar element, erroring on parse failure.
   /// @returns the parsed variables
-  ast::VariableList expect_param_list();
+  Expect<ast::VariableList> expect_param_list();
   /// Parses a `pipeline_stage` grammar element, erroring if the next token does
   /// not match a stage name.
-  /// @returns the pipeline stage or PipelineStage::kNone if none matched, along
-  /// with the source location for the stage.
-  std::pair<ast::PipelineStage, Source> expect_pipeline_stage();
+  /// @returns the pipeline stage.
+  Expect<ast::PipelineStage> expect_pipeline_stage();
   /// Parses a builtin identifier, erroring if the next token does not match a
   /// valid builtin name.
-  /// @returns the builtin or Builtin::kNone if none matched, along with the
-  /// source location for the stage.
-  std::pair<ast::Builtin, Source> expect_builtin();
+  /// @returns the parsed builtin.
+  Expect<ast::Builtin> expect_builtin();
   /// Parses a `body_stmt` grammar element, erroring on parse failure.
   /// @returns the parsed statements
-  std::unique_ptr<ast::BlockStatement> expect_body_stmt();
+  Expect<std::unique_ptr<ast::BlockStatement>> expect_body_stmt();
   /// Parses a `paren_rhs_stmt` grammar element, erroring on parse failure.
   /// @returns the parsed element or nullptr
-  std::unique_ptr<ast::Expression> expect_paren_rhs_stmt();
+  Expect<std::unique_ptr<ast::Expression>> expect_paren_rhs_stmt();
   /// Parses a `statements` grammar element
   /// @returns the statements parsed
-  std::unique_ptr<ast::BlockStatement> statements();
+  Expect<std::unique_ptr<ast::BlockStatement>> expect_statements();
   /// Parses a `statement` grammar element
   /// @returns the parsed statement or nullptr
   std::unique_ptr<ast::Statement> statement();
@@ -292,7 +354,7 @@
   std::unique_ptr<ast::CaseStatement> switch_body();
   /// Parses a `case_selectors` grammar element
   /// @returns the list of literals
-  ast::CaseSelectorList case_selectors();
+  Expect<ast::CaseSelectorList> expect_case_selectors();
   /// Parses a `case_body` grammar element
   /// @returns the parsed statements
   std::unique_ptr<ast::BlockStatement> case_body();
@@ -304,7 +366,7 @@
   std::unique_ptr<ast::LoopStatement> loop_stmt();
   /// Parses a `for_header` grammar element, erroring on parse failure.
   /// @returns the parsed for header or nullptr
-  std::unique_ptr<ForHeader> expect_for_header();
+  Expect<std::unique_ptr<ForHeader>> expect_for_header();
   /// Parses a `for_stmt` grammar element
   /// @returns the parsed for loop or nullptr
   std::unique_ptr<ast::Statement> for_stmt();
@@ -316,14 +378,14 @@
   std::unique_ptr<ast::Literal> const_literal();
   /// Parses a `const_expr` grammar element, erroring on parse failure.
   /// @returns the parsed constructor expression or nullptr on error
-  std::unique_ptr<ast::ConstructorExpression> expect_const_expr();
+  Expect<std::unique_ptr<ast::ConstructorExpression>> expect_const_expr();
   /// Parses a `primary_expression` grammar element
   /// @returns the parsed expression or nullptr
   std::unique_ptr<ast::Expression> primary_expression();
   /// Parses a `argument_expression_list` grammar element, erroring on parse
   /// failure.
   /// @returns the list of arguments
-  ast::ExpressionList expect_argument_expression_list();
+  Expect<ast::ExpressionList> expect_argument_expression_list();
   /// Parses the recursive portion of the postfix_expression
   /// @param prefix the left side of the expression
   /// @returns the parsed expression or nullptr
@@ -339,7 +401,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_multiplicative_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_multiplicative_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `multiplicative_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -348,7 +410,7 @@
   /// failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_additive_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_additive_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `additive_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -357,7 +419,7 @@
   /// failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_shift_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_shift_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `shift_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -366,7 +428,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_relational_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_relational_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `relational_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -375,7 +437,7 @@
   /// failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_equality_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_equality_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `equality_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -384,7 +446,7 @@
   /// failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_and_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_and_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `and_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -393,7 +455,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_exclusive_or_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_exclusive_or_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `exclusive_or_expression` grammar elememnt
   /// @returns the parsed expression or nullptr
@@ -402,7 +464,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_inclusive_or_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_inclusive_or_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses the `inclusive_or_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -411,7 +473,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_logical_and_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_logical_and_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses a `logical_and_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -420,7 +482,7 @@
   /// parse failure.
   /// @param lhs the left side of the expression
   /// @returns the parsed expression or nullptr
-  std::unique_ptr<ast::Expression> expect_logical_or_expr(
+  Expect<std::unique_ptr<ast::Expression>> expect_logical_or_expr(
       std::unique_ptr<ast::Expression> lhs);
   /// Parses a `logical_or_expression` grammar element
   /// @returns the parsed expression or nullptr
@@ -449,12 +511,16 @@
   /// represent a decoration.
   /// @see #decoration for the full list of decorations this method parses.
   /// @return the parsed decoration, or nullptr on error.
-  std::unique_ptr<ast::Decoration> expect_decoration();
+  Expect<std::unique_ptr<ast::Decoration>> expect_decoration();
 
  private:
-  /// ResultType resolves to the return type for the function or lambda F.
+  /// ReturnType resolves to the return type for the function or lambda F.
   template <typename F>
-  using ResultType = typename std::result_of<F()>::type;
+  using ReturnType = typename std::result_of<F()>::type;
+
+  /// ResultType resolves to |T| for a |RESULT| of type Expect<T>.
+  template <typename RESULT>
+  using ResultType = typename RESULT::type;
 
   /// @returns true and consumes the next token if it equals |tok|.
   /// @param source if not nullptr, the next token's source is written to this
@@ -470,33 +536,25 @@
   /// next token is not a signed integer.
   /// Always consumes the next token.
   /// @param use a description of what was being parsed if an error was raised
-  /// @param out the pointer to write the parsed integer to
-  /// @returns true if the signed integer was parsed without error
-  bool expect_sint(const std::string& use, int32_t* out);
+  /// @returns the parsed integer.
+  Expect<int32_t> expect_sint(const std::string& use);
   /// Parses a signed integer from the next token in the stream, erroring if
   /// the next token is not a signed integer or is negative.
   /// Always consumes the next token.
   /// @param use a description of what was being parsed if an error was raised
-  /// @param out the pointer to write the parsed integer to
-  /// @returns true if the signed integer was parsed without error
-  bool expect_positive_sint(const std::string& use, uint32_t* out);
+  /// @returns the parsed integer.
+  Expect<uint32_t> expect_positive_sint(const std::string& use);
   /// Parses a non-zero signed integer from the next token in the stream,
   /// erroring if the next token is not a signed integer or is less than 1.
   /// Always consumes the next token.
   /// @param use a description of what was being parsed if an error was raised
-  /// @param out the pointer to write the parsed integer to
-  /// @returns true if the signed integer was parsed without error
-  bool expect_nonzero_positive_sint(const std::string& use, uint32_t* out);
+  /// @returns the parsed integer.
+  Expect<uint32_t> expect_nonzero_positive_sint(const std::string& use);
   /// Errors if the next token is not an identifier.
   /// Always consumes the next token.
   /// @param use a description of what was being parsed if an error was raised
-  /// @param out the pointer to write the parsed identifier to
-  /// @param source if not nullptr, the next token's source is written to this
-  /// pointer, regardless of success or error
-  /// @returns true if the identifier was parsed without error
-  bool expect_ident(const std::string& use,
-                    std::string* out,
-                    Source* source = nullptr);
+  /// @returns the parsed identifier.
+  Expect<std::string> expect_ident(const std::string& use);
   /// Parses a lexical block starting with the token |start| and ending with
   /// the token |end|. |body| is called to parse the lexical block body between
   /// the |start| and |end| tokens.
@@ -508,10 +566,10 @@
   /// @param end the token that ends the lexical block
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature T().
+  /// body, with the signature: `Expect<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// a zero-initialized |T|.
-  template <typename F, typename T = ResultType<F>>
+  template <typename F, typename T = ReturnType<F>>
   T expect_block(Token::Type start,
                  Token::Type end,
                  const std::string& use,
@@ -521,36 +579,53 @@
   /// and |end| arguments, respectively.
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature T().
+  /// body, with the signature: `Expect<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// a zero-initialized |T|.
-  template <typename F, typename T = ResultType<F>>
+  template <typename F, typename T = ReturnType<F>>
   T expect_paren_block(const std::string& use, F&& body);
   /// A convenience function that calls |expect_block| passing
   /// |Token::Type::kBraceLeft| and |Token::Type::kBraceRight| for the |start|
   /// and |end| arguments, respectively.
   /// @param use a description of what was being parsed if an error was raised
   /// @param body a function or lambda that is called to parse the lexical block
-  /// body, with the signature T().
+  /// body, with the signature: `Expect<Result>()`.
   /// @return the value returned by |body| if no errors are raised, otherwise
   /// a zero-initialized |T|.
-  template <typename F, typename T = ResultType<F>>
+  template <typename F, typename T = ReturnType<F>>
   T expect_brace_block(const std::string& use, F&& body);
+
+  // Versions of expect_block(), expect_paren_block_old(),
+  // expect_brace_block_old() that do not take an Expect for T.
+  // These will be removed in the near future.
+  // TODO(ben-clayton) - migrate remaining uses of these.
+  template <typename F, typename T = ReturnType<F>>
+  T expect_block_old(Token::Type start,
+                     Token::Type end,
+                     const std::string& use,
+                     F&& body);
+  template <typename F, typename T = ReturnType<F>>
+  T expect_paren_block_old(const std::string& use, F&& body);
+  template <typename F, typename T = ReturnType<F>>
+  T expect_brace_block_old(const std::string& use, F&& body);
+
   /// Downcasts all the decorations in |list| to the type |T|, raising a parser
   /// error if any of the decorations aren't of the type |T|.
   template <typename T>
-  std::vector<std::unique_ptr<T>> cast_decorations(ast::DecorationList& in);
+  Expect<std::vector<std::unique_ptr<T>>> cast_decorations(
+      ast::DecorationList& in);
   /// Reports an error if the decoration list |list| is not empty.
   /// Used to ensure that all decorations are consumed.
   bool expect_decorations_consumed(const ast::DecorationList& list);
 
-  ast::type::Type* expect_type_decl_pointer();
-  ast::type::Type* expect_type_decl_vector(Token t);
-  ast::type::Type* expect_type_decl_array(ast::ArrayDecorationList decos);
-  ast::type::Type* expect_type_decl_matrix(Token t);
+  Expect<ast::type::Type*> expect_type_decl_pointer();
+  Expect<ast::type::Type*> expect_type_decl_vector(Token t);
+  Expect<ast::type::Type*> expect_type_decl_array(
+      ast::ArrayDecorationList decos);
+  Expect<ast::type::Type*> expect_type_decl_matrix(Token t);
 
-  std::unique_ptr<ast::ConstructorExpression> expect_const_expr_internal(
-      uint32_t depth);
+  Expect<std::unique_ptr<ast::ConstructorExpression>>
+  expect_const_expr_internal(uint32_t depth);
 
   Context& ctx_;
   diag::List diags_;
diff --git a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
index 78007b1..6302c8e 100644
--- a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
+++ b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
@@ -30,26 +30,29 @@
   auto* p = parser("a");
   auto e = p->expect_argument_expression_list();
   ASSERT_FALSE(p->has_error()) << p->error();
+  ASSERT_FALSE(e.errored);
 
-  ASSERT_EQ(e.size(), 1u);
-  ASSERT_TRUE(e[0]->IsIdentifier());
+  ASSERT_EQ(e.value.size(), 1u);
+  ASSERT_TRUE(e.value[0]->IsIdentifier());
 }
 
 TEST_F(ParserImplTest, ArgumentExpressionList_ParsesMultiple) {
   auto* p = parser("a, -33, 1+2");
   auto e = p->expect_argument_expression_list();
   ASSERT_FALSE(p->has_error()) << p->error();
+  ASSERT_FALSE(e.errored);
 
-  ASSERT_EQ(e.size(), 3u);
-  ASSERT_TRUE(e[0]->IsIdentifier());
-  ASSERT_TRUE(e[1]->IsConstructor());
-  ASSERT_TRUE(e[2]->IsBinary());
+  ASSERT_EQ(e.value.size(), 3u);
+  ASSERT_TRUE(e.value[0]->IsIdentifier());
+  ASSERT_TRUE(e.value[1]->IsConstructor());
+  ASSERT_TRUE(e.value[2]->IsBinary());
 }
 
 TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingExpression) {
   auto* p = parser("a, ");
   auto e = p->expect_argument_expression_list();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(e.errored);
   EXPECT_EQ(p->error(), "1:4: unable to parse argument expression after comma");
 }
 
@@ -57,6 +60,7 @@
   auto* p = parser("if(a) {}");
   auto e = p->expect_argument_expression_list();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(e.errored);
   EXPECT_EQ(p->error(), "1:1: unable to parse argument expression");
 }
 
diff --git a/src/reader/wgsl/parser_impl_body_stmt_test.cc b/src/reader/wgsl/parser_impl_body_stmt_test.cc
index 6451de5..4a3b5a0 100644
--- a/src/reader/wgsl/parser_impl_body_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_body_stmt_test.cc
@@ -28,22 +28,25 @@
 })");
   auto e = p->expect_body_stmt();
   ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_EQ(e->size(), 2u);
-  EXPECT_TRUE(e->get(0)->IsDiscard());
-  EXPECT_TRUE(e->get(1)->IsReturn());
+  ASSERT_FALSE(e.errored);
+  ASSERT_EQ(e.value->size(), 2u);
+  EXPECT_TRUE(e.value->get(0)->IsDiscard());
+  EXPECT_TRUE(e.value->get(1)->IsReturn());
 }
 
 TEST_F(ParserImplTest, BodyStmt_Empty) {
   auto* p = parser("{}");
   auto e = p->expect_body_stmt();
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_EQ(e->size(), 0u);
+  ASSERT_FALSE(e.errored);
+  EXPECT_EQ(e.value->size(), 0u);
 }
 
 TEST_F(ParserImplTest, BodyStmt_InvalidStmt) {
   auto* p = parser("{fn main() -> void {}}");
   auto e = p->expect_body_stmt();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(e.errored);
   EXPECT_EQ(p->error(), "1:2: expected '}'");
 }
 
@@ -51,6 +54,7 @@
   auto* p = parser("{return;");
   auto e = p->expect_body_stmt();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(e.errored);
   EXPECT_EQ(p->error(), "1:9: expected '}'");
 }
 
diff --git a/src/reader/wgsl/parser_impl_const_expr_test.cc b/src/reader/wgsl/parser_impl_const_expr_test.cc
index 5d427dc..14b716f 100644
--- a/src/reader/wgsl/parser_impl_const_expr_test.cc
+++ b/src/reader/wgsl/parser_impl_const_expr_test.cc
@@ -30,11 +30,11 @@
   auto* p = parser("vec2<f32>(1., 2.)");
   auto e = p->expect_const_expr();
   ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsConstructor());
-  ASSERT_TRUE(e->AsConstructor()->IsTypeConstructor());
+  ASSERT_FALSE(e.errored);
+  ASSERT_TRUE(e.value->IsConstructor());
+  ASSERT_TRUE(e.value->AsConstructor()->IsTypeConstructor());
 
-  auto* t = e->AsConstructor()->AsTypeConstructor();
+  auto* t = e.value->AsConstructor()->AsTypeConstructor();
   ASSERT_TRUE(t->type()->IsVector());
   EXPECT_EQ(t->type()->AsVector()->size(), 2u);
 
@@ -58,7 +58,8 @@
   auto* p = parser("vec2<f32>(1., 2.");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:17: expected ')' for type constructor");
 }
 
@@ -66,7 +67,8 @@
   auto* p = parser("vec2<f32> 1., 2.)");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:11: expected '(' for type constructor");
 }
 
@@ -74,7 +76,8 @@
   auto* p = parser("vec2<f32>(1.,)");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:14: unable to parse const literal");
 }
 
@@ -82,7 +85,8 @@
   auto* p = parser("vec2<f32>(1. 2.");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:14: expected ')' for type constructor");
 }
 
@@ -90,7 +94,8 @@
   auto* p = parser("vec2<f32>()");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:11: unable to parse const literal");
 }
 
@@ -98,7 +103,8 @@
   auto* p = parser("vec2<f32>(1., if(a) {})");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:15: unable to parse const literal");
 }
 
@@ -106,10 +112,11 @@
   auto* p = parser("true");
   auto e = p->expect_const_expr();
   ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsConstructor());
-  ASSERT_TRUE(e->AsConstructor()->IsScalarConstructor());
-  auto* c = e->AsConstructor()->AsScalarConstructor();
+  ASSERT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e.value->IsConstructor());
+  ASSERT_TRUE(e.value->AsConstructor()->IsScalarConstructor());
+  auto* c = e.value->AsConstructor()->AsScalarConstructor();
   ASSERT_TRUE(c->literal()->IsBool());
   EXPECT_TRUE(c->literal()->AsBool()->IsTrue());
 }
@@ -118,7 +125,8 @@
   auto* p = parser("invalid");
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:1: unknown constructed type 'invalid'");
 }
 
@@ -134,7 +142,8 @@
   auto* p = parser(out.str());
   auto e = p->expect_const_expr();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:517: max const_expr depth reached");
 }
 
diff --git a/src/reader/wgsl/parser_impl_for_stmt_test.cc b/src/reader/wgsl/parser_impl_for_stmt_test.cc
index a8a8128..3520a58 100644
--- a/src/reader/wgsl/parser_impl_for_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_for_stmt_test.cc
@@ -27,16 +27,18 @@
  public:
   void TestForLoop(std::string loop_str, std::string for_str) {
     auto* p_loop = parser(loop_str);
-    auto e_loop = p_loop->statements();
-    ASSERT_FALSE(p_loop->has_error()) << p_loop->error();
-    ASSERT_NE(e_loop, nullptr);
+    auto e_loop = p_loop->expect_statements();
+    EXPECT_FALSE(e_loop.errored);
+    EXPECT_FALSE(p_loop->has_error()) << p_loop->error();
+    ASSERT_NE(e_loop.value, nullptr);
 
     auto* p_for = parser(for_str);
-    auto e_for = p_for->statements();
-    ASSERT_FALSE(p_for->has_error()) << p_for->error();
-    ASSERT_NE(e_for, nullptr);
+    auto e_for = p_for->expect_statements();
+    EXPECT_FALSE(e_for.errored);
+    EXPECT_FALSE(p_for->has_error()) << p_for->error();
+    ASSERT_NE(e_for.value, nullptr);
 
-    EXPECT_EQ(e_loop->str(), e_for->str());
+    EXPECT_EQ(e_loop.value->str(), e_for.value->str());
   }
 };
 
diff --git a/src/reader/wgsl/parser_impl_image_storage_type_test.cc b/src/reader/wgsl/parser_impl_image_storage_type_test.cc
index 0caed67..f454a3b 100644
--- a/src/reader/wgsl/parser_impl_image_storage_type_test.cc
+++ b/src/reader/wgsl/parser_impl_image_storage_type_test.cc
@@ -24,253 +24,289 @@
 
 TEST_F(ParserImplTest, ImageStorageType_Invalid) {
   auto* p = parser("1234");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kNone);
-  EXPECT_FALSE(p->has_error());
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_TRUE(t.errored);
+  EXPECT_TRUE(p->has_error());
+  EXPECT_EQ(p->error(), "1:1: invalid format for test");
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R8Unorm) {
   auto* p = parser("r8unorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR8Unorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR8Unorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R8Snorm) {
   auto* p = parser("r8snorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR8Snorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR8Snorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R8Uint) {
   auto* p = parser("r8uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR8Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR8Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R8Sint) {
   auto* p = parser("r8sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR8Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR8Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R16Uint) {
   auto* p = parser("r16uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR16Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR16Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R16Sint) {
   auto* p = parser("r16sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR16Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR16Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R16Float) {
   auto* p = parser("r16float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR16Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR16Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg8Unorm) {
   auto* p = parser("rg8unorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg8Unorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg8Unorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg8Snorm) {
   auto* p = parser("rg8snorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg8Snorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg8Snorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg8Uint) {
   auto* p = parser("rg8uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg8Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg8Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg8Sint) {
   auto* p = parser("rg8sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg8Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg8Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R32Uint) {
   auto* p = parser("r32uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR32Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR32Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R32Sint) {
   auto* p = parser("r32sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR32Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR32Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_R32Float) {
   auto* p = parser("r32float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kR32Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kR32Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg16Uint) {
   auto* p = parser("rg16uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg16Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg16Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg16Sint) {
   auto* p = parser("rg16sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg16Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg16Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg16Float) {
   auto* p = parser("rg16float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg16Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg16Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba8Unorm) {
   auto* p = parser("rgba8unorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba8Unorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba8Unorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba8UnormSrgb) {
   auto* p = parser("rgba8unorm_srgb");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba8UnormSrgb);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba8UnormSrgb);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba8Snorm) {
   auto* p = parser("rgba8snorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba8Snorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba8Snorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba8Uint) {
   auto* p = parser("rgba8uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba8Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba8Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba8Sint) {
   auto* p = parser("rgba8sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba8Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba8Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Bgra8Unorm) {
   auto* p = parser("bgra8unorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kBgra8Unorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kBgra8Unorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Bgra8UnormSrgb) {
   auto* p = parser("bgra8unorm_srgb");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kBgra8UnormSrgb);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kBgra8UnormSrgb);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgb10A2Unorm) {
   auto* p = parser("rgb10a2unorm");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgb10A2Unorm);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgb10A2Unorm);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg11B10Float) {
   auto* p = parser("rg11b10float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg11B10Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg11B10Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg32Uint) {
   auto* p = parser("rg32uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg32Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg32Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg32Sint) {
   auto* p = parser("rg32sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg32Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg32Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rg32Float) {
   auto* p = parser("rg32float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRg32Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRg32Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba16Uint) {
   auto* p = parser("rgba16uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba16Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba16Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba16Sint) {
   auto* p = parser("rgba16sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba16Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba16Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba16Float) {
   auto* p = parser("rgba16float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba16Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba16Float);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba32Uint) {
   auto* p = parser("rgba32uint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba32Uint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba32Uint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba32Sint) {
   auto* p = parser("rgba32sint");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba32Sint);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba32Sint);
   EXPECT_FALSE(p->has_error());
 }
 
 TEST_F(ParserImplTest, ImageStorageType_Rgba32Float) {
   auto* p = parser("rgba32float");
-  auto t = p->image_storage_type();
-  EXPECT_EQ(t, ast::type::ImageFormat::kRgba32Float);
+  auto t = p->expect_image_storage_type("test");
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::ImageFormat::kRgba32Float);
   EXPECT_FALSE(p->has_error());
 }
 
diff --git a/src/reader/wgsl/parser_impl_param_list_test.cc b/src/reader/wgsl/parser_impl_param_list_test.cc
index 1711106..03eea45 100644
--- a/src/reader/wgsl/parser_impl_param_list_test.cc
+++ b/src/reader/wgsl/parser_impl_param_list_test.cc
@@ -34,16 +34,17 @@
   auto* p = parser("a : i32");
   auto e = p->expect_param_list();
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_EQ(e.size(), 1u);
+  ASSERT_FALSE(e.errored);
+  EXPECT_EQ(e.value.size(), 1u);
 
-  EXPECT_EQ(e[0]->name(), "a");
-  EXPECT_EQ(e[0]->type(), i32);
-  EXPECT_TRUE(e[0]->is_const());
+  EXPECT_EQ(e.value[0]->name(), "a");
+  EXPECT_EQ(e.value[0]->type(), i32);
+  EXPECT_TRUE(e.value[0]->is_const());
 
-  ASSERT_EQ(e[0]->source().range.begin.line, 1u);
-  ASSERT_EQ(e[0]->source().range.begin.column, 1u);
-  ASSERT_EQ(e[0]->source().range.end.line, 1u);
-  ASSERT_EQ(e[0]->source().range.end.column, 2u);
+  ASSERT_EQ(e.value[0]->source().range.begin.line, 1u);
+  ASSERT_EQ(e.value[0]->source().range.begin.column, 1u);
+  ASSERT_EQ(e.value[0]->source().range.end.line, 1u);
+  ASSERT_EQ(e.value[0]->source().range.end.column, 2u);
 }
 
 TEST_F(ParserImplTest, ParamList_Multiple) {
@@ -54,47 +55,50 @@
   auto* p = parser("a : i32, b: f32, c: vec2<f32>");
   auto e = p->expect_param_list();
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_EQ(e.size(), 3u);
+  ASSERT_FALSE(e.errored);
+  EXPECT_EQ(e.value.size(), 3u);
 
-  EXPECT_EQ(e[0]->name(), "a");
-  EXPECT_EQ(e[0]->type(), i32);
-  EXPECT_TRUE(e[0]->is_const());
+  EXPECT_EQ(e.value[0]->name(), "a");
+  EXPECT_EQ(e.value[0]->type(), i32);
+  EXPECT_TRUE(e.value[0]->is_const());
 
-  ASSERT_EQ(e[0]->source().range.begin.line, 1u);
-  ASSERT_EQ(e[0]->source().range.begin.column, 1u);
-  ASSERT_EQ(e[0]->source().range.end.line, 1u);
-  ASSERT_EQ(e[0]->source().range.end.column, 2u);
+  ASSERT_EQ(e.value[0]->source().range.begin.line, 1u);
+  ASSERT_EQ(e.value[0]->source().range.begin.column, 1u);
+  ASSERT_EQ(e.value[0]->source().range.end.line, 1u);
+  ASSERT_EQ(e.value[0]->source().range.end.column, 2u);
 
-  EXPECT_EQ(e[1]->name(), "b");
-  EXPECT_EQ(e[1]->type(), f32);
-  EXPECT_TRUE(e[1]->is_const());
+  EXPECT_EQ(e.value[1]->name(), "b");
+  EXPECT_EQ(e.value[1]->type(), f32);
+  EXPECT_TRUE(e.value[1]->is_const());
 
-  ASSERT_EQ(e[1]->source().range.begin.line, 1u);
-  ASSERT_EQ(e[1]->source().range.begin.column, 10u);
-  ASSERT_EQ(e[1]->source().range.end.line, 1u);
-  ASSERT_EQ(e[1]->source().range.end.column, 11u);
+  ASSERT_EQ(e.value[1]->source().range.begin.line, 1u);
+  ASSERT_EQ(e.value[1]->source().range.begin.column, 10u);
+  ASSERT_EQ(e.value[1]->source().range.end.line, 1u);
+  ASSERT_EQ(e.value[1]->source().range.end.column, 11u);
 
-  EXPECT_EQ(e[2]->name(), "c");
-  EXPECT_EQ(e[2]->type(), vec2);
-  EXPECT_TRUE(e[2]->is_const());
+  EXPECT_EQ(e.value[2]->name(), "c");
+  EXPECT_EQ(e.value[2]->type(), vec2);
+  EXPECT_TRUE(e.value[2]->is_const());
 
-  ASSERT_EQ(e[2]->source().range.begin.line, 1u);
-  ASSERT_EQ(e[2]->source().range.begin.column, 18u);
-  ASSERT_EQ(e[2]->source().range.end.line, 1u);
-  ASSERT_EQ(e[2]->source().range.end.column, 19u);
+  ASSERT_EQ(e.value[2]->source().range.begin.line, 1u);
+  ASSERT_EQ(e.value[2]->source().range.begin.column, 18u);
+  ASSERT_EQ(e.value[2]->source().range.end.line, 1u);
+  ASSERT_EQ(e.value[2]->source().range.end.column, 19u);
 }
 
 TEST_F(ParserImplTest, ParamList_Empty) {
   auto* p = parser("");
   auto e = p->expect_param_list();
   ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(e.size(), 0u);
+  ASSERT_FALSE(e.errored);
+  EXPECT_EQ(e.value.size(), 0u);
 }
 
 TEST_F(ParserImplTest, ParamList_HangingComma) {
   auto* p = parser("a : i32,");
   auto e = p->expect_param_list();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(e.errored);
   EXPECT_EQ(p->error(), "1:9: expected identifier for parameter");
 }
 
diff --git a/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc b/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
index 9360f7e..e55bc89 100644
--- a/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+++ b/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
@@ -25,15 +25,17 @@
   auto* p = parser("(a + b)");
   auto e = p->expect_paren_rhs_stmt();
   ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsBinary());
+  ASSERT_FALSE(e.errored);
+  ASSERT_NE(e.value, nullptr);
+  ASSERT_TRUE(e.value->IsBinary());
 }
 
 TEST_F(ParserImplTest, ParenRhsStmt_MissingLeftParen) {
   auto* p = parser("true)");
   auto e = p->expect_paren_rhs_stmt();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:1: expected '('");
 }
 
@@ -41,7 +43,8 @@
   auto* p = parser("(true");
   auto e = p->expect_paren_rhs_stmt();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:6: expected ')'");
 }
 
@@ -49,7 +52,8 @@
   auto* p = parser("(if (a() {})");
   auto e = p->expect_paren_rhs_stmt();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:2: unable to parse expression");
 }
 
@@ -57,7 +61,8 @@
   auto* p = parser("()");
   auto e = p->expect_paren_rhs_stmt();
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
+  ASSERT_TRUE(e.errored);
+  ASSERT_EQ(e.value, nullptr);
   EXPECT_EQ(p->error(), "1:2: unable to parse expression");
 }
 
diff --git a/src/reader/wgsl/parser_impl_pipeline_stage_test.cc b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
index 0fcffc4..4cae54e 100644
--- a/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
+++ b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
@@ -36,15 +36,14 @@
   auto params = GetParam();
   auto* p = parser(params.input);
 
-  ast::PipelineStage stage;
-  Source source;
-  std::tie(stage, source) = p->expect_pipeline_stage();
+  auto stage = p->expect_pipeline_stage();
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_EQ(stage, params.result);
-  EXPECT_EQ(source.range.begin.line, 1u);
-  EXPECT_EQ(source.range.begin.column, 1u);
-  EXPECT_EQ(source.range.end.line, 1u);
-  EXPECT_EQ(source.range.end.column, 1u + params.input.size());
+  ASSERT_FALSE(stage.errored);
+  EXPECT_EQ(stage.value, params.result);
+  EXPECT_EQ(stage.source.range.begin.line, 1u);
+  EXPECT_EQ(stage.source.range.begin.column, 1u);
+  EXPECT_EQ(stage.source.range.end.line, 1u);
+  EXPECT_EQ(stage.source.range.end.column, 1u + params.input.size());
 
   auto t = p->next();
   EXPECT_TRUE(t.IsEof());
@@ -59,16 +58,10 @@
 
 TEST_F(ParserImplTest, PipelineStage_NoMatch) {
   auto* p = parser("not-a-stage");
-  ast::PipelineStage stage;
-  Source source;
-  std::tie(stage, source) = p->expect_pipeline_stage();
+  auto stage = p->expect_pipeline_stage();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(stage.errored);
   ASSERT_EQ(p->error(), "1:1: invalid value for stage decoration");
-  ASSERT_EQ(stage, ast::PipelineStage::kNone);
-  EXPECT_EQ(source.range.begin.line, 1u);
-  EXPECT_EQ(source.range.begin.column, 1u);
-  EXPECT_EQ(source.range.end.line, 1u);
-  EXPECT_EQ(source.range.end.column, 4u);
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_statements_test.cc b/src/reader/wgsl/parser_impl_statements_test.cc
index a22e656..c7417e9 100644
--- a/src/reader/wgsl/parser_impl_statements_test.cc
+++ b/src/reader/wgsl/parser_impl_statements_test.cc
@@ -24,18 +24,20 @@
 
 TEST_F(ParserImplTest, Statements) {
   auto* p = parser("discard; return;");
-  auto e = p->statements();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_EQ(e->size(), 2u);
-  EXPECT_TRUE(e->get(0)->IsDiscard());
-  EXPECT_TRUE(e->get(1)->IsReturn());
+  auto e = p->expect_statements();
+  EXPECT_FALSE(e.errored);
+  EXPECT_FALSE(p->has_error()) << p->error();
+  ASSERT_EQ(e.value->size(), 2u);
+  EXPECT_TRUE(e.value->get(0)->IsDiscard());
+  EXPECT_TRUE(e.value->get(1)->IsReturn());
 }
 
 TEST_F(ParserImplTest, Statements_Empty) {
   auto* p = parser("");
-  auto e = p->statements();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_EQ(e->size(), 0u);
+  auto e = p->expect_statements();
+  EXPECT_FALSE(e.errored);
+  EXPECT_FALSE(p->has_error()) << p->error();
+  ASSERT_EQ(e.value->size(), 0u);
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_storage_class_test.cc b/src/reader/wgsl/parser_impl_storage_class_test.cc
index bca0a66..2945512 100644
--- a/src/reader/wgsl/parser_impl_storage_class_test.cc
+++ b/src/reader/wgsl/parser_impl_storage_class_test.cc
@@ -38,8 +38,9 @@
   auto* p = parser(params.input);
 
   auto sc = p->expect_storage_class("test");
-  ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(sc, params.result);
+  EXPECT_FALSE(sc.errored);
+  EXPECT_FALSE(p->has_error());
+  EXPECT_EQ(sc.value, params.result);
 
   auto t = p->next();
   EXPECT_TRUE(t.IsEof());
@@ -62,8 +63,8 @@
 TEST_F(ParserImplTest, StorageClass_NoMatch) {
   auto* p = parser("not-a-storage-class");
   auto sc = p->expect_storage_class("test");
-  ASSERT_EQ(sc, ast::StorageClass::kNone);
-  ASSERT_TRUE(p->has_error());
+  EXPECT_EQ(sc.errored, true);
+  EXPECT_TRUE(p->has_error());
   EXPECT_EQ(p->error(), "1:1: invalid storage class for test");
 
   auto t = p->next();
diff --git a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc
index 7e8d7d2..f46cc30 100644
--- a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc
@@ -29,9 +29,10 @@
   auto* p = parser("{a : i32;}");
   auto m = p->expect_struct_body_decl();
   ASSERT_FALSE(p->has_error());
-  ASSERT_EQ(m.size(), 1u);
+  ASSERT_FALSE(m.errored);
+  ASSERT_EQ(m.value.size(), 1u);
 
-  const auto& mem = m[0];
+  const auto& mem = m.value[0];
   EXPECT_EQ(mem->name(), "a");
   EXPECT_EQ(mem->type(), i32);
   EXPECT_EQ(mem->decorations().size(), 0u);
@@ -41,7 +42,8 @@
   auto* p = parser("{}");
   auto m = p->expect_struct_body_decl();
   ASSERT_FALSE(p->has_error());
-  ASSERT_EQ(m.size(), 0u);
+  ASSERT_FALSE(m.errored);
+  ASSERT_EQ(m.value.size(), 0u);
 }
 
 TEST_F(ParserImplTest, StructBodyDecl_InvalidMember) {
@@ -51,6 +53,7 @@
 })");
   auto m = p->expect_struct_body_decl();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(m.errored);
   EXPECT_EQ(p->error(),
             "3:12: expected signed integer literal for offset decoration");
 }
@@ -59,6 +62,7 @@
   auto* p = parser("{a : i32;");
   auto m = p->expect_struct_body_decl();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(m.errored);
   EXPECT_EQ(p->error(), "1:10: expected '}' for struct declaration");
 }
 
@@ -70,6 +74,7 @@
 } )");
   auto m = p->expect_struct_body_decl();
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(m.errored);
   EXPECT_EQ(p->error(), "4:3: expected identifier for struct member");
 }
 
diff --git a/src/reader/wgsl/parser_impl_struct_member_test.cc b/src/reader/wgsl/parser_impl_struct_member_test.cc
index 186e0f0..563ece6 100644
--- a/src/reader/wgsl/parser_impl_struct_member_test.cc
+++ b/src/reader/wgsl/parser_impl_struct_member_test.cc
@@ -32,16 +32,17 @@
   EXPECT_EQ(decos.size(), 0u);
   auto m = p->expect_struct_member(decos);
   ASSERT_FALSE(p->has_error());
-  ASSERT_NE(m, nullptr);
+  ASSERT_FALSE(m.errored);
+  ASSERT_NE(m.value, nullptr);
 
-  EXPECT_EQ(m->name(), "a");
-  EXPECT_EQ(m->type(), i32);
-  EXPECT_EQ(m->decorations().size(), 0u);
+  EXPECT_EQ(m.value->name(), "a");
+  EXPECT_EQ(m.value->type(), i32);
+  EXPECT_EQ(m.value->decorations().size(), 0u);
 
-  ASSERT_EQ(m->source().range.begin.line, 1u);
-  ASSERT_EQ(m->source().range.begin.column, 1u);
-  ASSERT_EQ(m->source().range.end.line, 1u);
-  ASSERT_EQ(m->source().range.end.column, 2u);
+  ASSERT_EQ(m.value->source().range.begin.line, 1u);
+  ASSERT_EQ(m.value->source().range.begin.column, 1u);
+  ASSERT_EQ(m.value->source().range.end.line, 1u);
+  ASSERT_EQ(m.value->source().range.end.column, 2u);
 }
 
 TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
@@ -52,18 +53,19 @@
   EXPECT_EQ(decos.size(), 1u);
   auto m = p->expect_struct_member(decos);
   ASSERT_FALSE(p->has_error());
-  ASSERT_NE(m, nullptr);
+  ASSERT_FALSE(m.errored);
+  ASSERT_NE(m.value, nullptr);
 
-  EXPECT_EQ(m->name(), "a");
-  EXPECT_EQ(m->type(), i32);
-  EXPECT_EQ(m->decorations().size(), 1u);
-  EXPECT_TRUE(m->decorations()[0]->IsOffset());
-  EXPECT_EQ(m->decorations()[0]->AsOffset()->offset(), 2u);
+  EXPECT_EQ(m.value->name(), "a");
+  EXPECT_EQ(m.value->type(), i32);
+  EXPECT_EQ(m.value->decorations().size(), 1u);
+  EXPECT_TRUE(m.value->decorations()[0]->IsOffset());
+  EXPECT_EQ(m.value->decorations()[0]->AsOffset()->offset(), 2u);
 
-  ASSERT_EQ(m->source().range.begin.line, 1u);
-  ASSERT_EQ(m->source().range.begin.column, 15u);
-  ASSERT_EQ(m->source().range.end.line, 1u);
-  ASSERT_EQ(m->source().range.end.column, 16u);
+  ASSERT_EQ(m.value->source().range.begin.line, 1u);
+  ASSERT_EQ(m.value->source().range.begin.column, 15u);
+  ASSERT_EQ(m.value->source().range.end.line, 1u);
+  ASSERT_EQ(m.value->source().range.end.column, 16u);
 }
 
 TEST_F(ParserImplTest, StructMember_ParsesWithMultipleDecorations) {
@@ -75,20 +77,21 @@
   EXPECT_EQ(decos.size(), 2u);
   auto m = p->expect_struct_member(decos);
   ASSERT_FALSE(p->has_error());
-  ASSERT_NE(m, nullptr);
+  ASSERT_FALSE(m.errored);
+  ASSERT_NE(m.value, nullptr);
 
-  EXPECT_EQ(m->name(), "a");
-  EXPECT_EQ(m->type(), i32);
-  EXPECT_EQ(m->decorations().size(), 2u);
-  EXPECT_TRUE(m->decorations()[0]->IsOffset());
-  EXPECT_EQ(m->decorations()[0]->AsOffset()->offset(), 2u);
-  EXPECT_TRUE(m->decorations()[1]->IsOffset());
-  EXPECT_EQ(m->decorations()[1]->AsOffset()->offset(), 4u);
+  EXPECT_EQ(m.value->name(), "a");
+  EXPECT_EQ(m.value->type(), i32);
+  EXPECT_EQ(m.value->decorations().size(), 2u);
+  EXPECT_TRUE(m.value->decorations()[0]->IsOffset());
+  EXPECT_EQ(m.value->decorations()[0]->AsOffset()->offset(), 2u);
+  EXPECT_TRUE(m.value->decorations()[1]->IsOffset());
+  EXPECT_EQ(m.value->decorations()[1]->AsOffset()->offset(), 4u);
 
-  ASSERT_EQ(m->source().range.begin.line, 2u);
-  ASSERT_EQ(m->source().range.begin.column, 15u);
-  ASSERT_EQ(m->source().range.end.line, 2u);
-  ASSERT_EQ(m->source().range.end.column, 16u);
+  ASSERT_EQ(m.value->source().range.begin.line, 2u);
+  ASSERT_EQ(m.value->source().range.begin.column, 15u);
+  ASSERT_EQ(m.value->source().range.end.line, 2u);
+  ASSERT_EQ(m.value->source().range.end.column, 16u);
 }
 
 TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
@@ -96,7 +99,8 @@
   auto decos = p->decoration_list();
   auto m = p->expect_struct_member(decos);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(m, nullptr);
+  ASSERT_TRUE(m.errored);
+  ASSERT_EQ(m.value, nullptr);
   EXPECT_EQ(p->error(),
             "1:10: expected signed integer literal for offset decoration");
 }
@@ -106,7 +110,8 @@
   auto decos = p->decoration_list();
   auto m = p->expect_struct_member(decos);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(m, nullptr);
+  ASSERT_TRUE(m.errored);
+  ASSERT_EQ(m.value, nullptr);
   EXPECT_EQ(p->error(), "1:19: unknown constructed type 'B'");
 }
 
@@ -115,7 +120,8 @@
   auto decos = p->decoration_list();
   auto m = p->expect_struct_member(decos);
   ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(m, nullptr);
+  ASSERT_TRUE(m.errored);
+  ASSERT_EQ(m.value, nullptr);
   EXPECT_EQ(p->error(), "1:8: expected ';' for struct member");
 }
 
diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index f9eb7a7..08d0a39 100644
--- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -25,34 +25,38 @@
   auto* p = parser("my_var : f32");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_FALSE(p->has_error());
-  ASSERT_EQ(decl.name, "my_var");
-  ASSERT_NE(decl.type, nullptr);
-  ASSERT_TRUE(decl.type->IsF32());
+  ASSERT_FALSE(decl.errored);
+  ASSERT_EQ(decl->name, "my_var");
+  ASSERT_NE(decl->type, nullptr);
+  ASSERT_TRUE(decl->type->IsF32());
 
-  ASSERT_EQ(decl.source.range.begin.line, 1u);
-  ASSERT_EQ(decl.source.range.begin.column, 1u);
-  ASSERT_EQ(decl.source.range.end.line, 1u);
-  ASSERT_EQ(decl.source.range.end.column, 7u);
+  ASSERT_EQ(decl->source.range.begin.line, 1u);
+  ASSERT_EQ(decl->source.range.begin.column, 1u);
+  ASSERT_EQ(decl->source.range.end.line, 1u);
+  ASSERT_EQ(decl->source.range.end.column, 7u);
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
   auto* p = parser(": f32");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(decl.errored);
   ASSERT_EQ(p->error(), "1:1: expected identifier for test");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingColon) {
   auto* p = parser("my_var f32");
-  auto r = p->expect_variable_ident_decl("test");
+  auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(decl.errored);
   ASSERT_EQ(p->error(), "1:8: expected ':' for test");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingType) {
   auto* p = parser("my_var :");
-  auto r = p->expect_variable_ident_decl("test");
+  auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(decl.errored);
   ASSERT_EQ(p->error(), "1:9: invalid type for test");
 }
 
@@ -60,13 +64,15 @@
   auto* p = parser("123 : f32");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(decl.errored);
   ASSERT_EQ(p->error(), "1:1: expected identifier for test");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_InvalidType) {
   auto* p = parser("my_var : invalid");
-  auto r = p->expect_variable_ident_decl("test");
+  auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
+  ASSERT_TRUE(decl.errored);
   ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
 }