]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Use error wrapper when required instead of nullptr
authorPierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
Fri, 26 Dec 2025 03:38:52 +0000 (04:38 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Fri, 16 Jan 2026 16:32:05 +0000 (17:32 +0100)
gcc/rust/ChangeLog:

* ast/rust-ast-collector.cc (TokenCollector::visit): Update function
name.
* ast/rust-ast-pointer-visitor.cc (PointerVisitor::visit): Likewise.
* ast/rust-ast-visitor.cc (DefaultASTVisitor::visit): Likewise.
* ast/rust-ast.cc (BreakExpr::as_string): Use getter function.
(AttributeParser::parse_path_meta_item): Convert to expected type.
(ReturnExpr::as_string): Change access to returned expr.
* ast/rust-desugar-for-loops.cc (DesugarForLoops::DesugarCtx::make_break_arm):
Likewise.
* ast/rust-expr.h (class BreakExpr): Make expr in break optional.
(class ReturnExpr): Make returned expr explicitely optional. Change the
getters and introduce a  const getter.
* expand/rust-cfg-strip.cc (CfgStrip::visit): Convert to expected type.
* expand/rust-macro-builtins-asm.cc (parse_reg_operand_in): Update
constructor call to new expected types.
(parse_reg_operand_out): Likewise.
(parse_reg_operand_inout): Likewise.
(parse_llvm_operands): Likewise.
* expand/rust-macro-builtins-format-args.cc (format_args_parse_expr):
Likewise.
(format_args_parse_arguments): Likewise.
* expand/rust-macro-builtins-helpers.cc (try_expand_many_expr): Update
value for tl::expected.
* expand/rust-macro-builtins-include.cc (MacroBuiltin::include_handler):
Likewise.
* expand/rust-macro-expand.cc (transcribe_expression): Update
constructor call with expected value.
* hir/rust-ast-lower-expr.cc (ASTLoweringExpr::visit): Likewise.
* parse/rust-parse-error.h (enum class): Add new error types for Expr
and StructExprField.
* parse/rust-parse-impl-expr.hxx: Explicitely handle return expr
parsing failure. Update to fit new expected types.
* parse/rust-parse-impl.hxx: Likewise.
* resolve/rust-late-name-resolver-2.0.cc (Late::visit): Adapt to
tl::expected.
* ast/rust-desugar-while-let.cc (DesugarWhileLet::DesugarCtx::make_break_arm):
Use optional for break expressions when missing instead of nullptr.
* parse/rust-parse.h: Change function return type with expected. Remove
error state from ExprOrStmt.

Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
19 files changed:
gcc/rust/ast/rust-ast-collector.cc
gcc/rust/ast/rust-ast-pointer-visitor.cc
gcc/rust/ast/rust-ast-visitor.cc
gcc/rust/ast/rust-ast.cc
gcc/rust/ast/rust-desugar-for-loops.cc
gcc/rust/ast/rust-desugar-while-let.cc
gcc/rust/ast/rust-expr.h
gcc/rust/expand/rust-cfg-strip.cc
gcc/rust/expand/rust-macro-builtins-asm.cc
gcc/rust/expand/rust-macro-builtins-format-args.cc
gcc/rust/expand/rust-macro-builtins-helpers.cc
gcc/rust/expand/rust-macro-builtins-include.cc
gcc/rust/expand/rust-macro-expand.cc
gcc/rust/hir/rust-ast-lower-expr.cc
gcc/rust/parse/rust-parse-error.h
gcc/rust/parse/rust-parse-impl-expr.hxx
gcc/rust/parse/rust-parse-impl.hxx
gcc/rust/parse/rust-parse.h
gcc/rust/resolve/rust-late-name-resolver-2.0.cc

index 3da35557578f8e4e247d25d4ea1d4caa2f7e0376..0ef5f06e5290a36d40623b8bb3ea6196c1be3c82 100644 (file)
@@ -1477,7 +1477,7 @@ TokenCollector::visit (BreakExpr &expr)
     if (expr.has_label ())
       visit (expr.get_label_unchecked ());
     if (expr.has_break_expr ())
-      visit (expr.get_break_expr ());
+      visit (expr.get_break_expr_unchecked ());
   });
 }
 
index 6b5b5f2c66bf0eb9d743b5bee140e6a822a4c936..d403ef6157831f0e484651da4ecdc28598e0a8f4 100644 (file)
@@ -490,7 +490,7 @@ PointerVisitor::visit (AST::BreakExpr &expr)
     visit (expr.get_label_unchecked ());
 
   if (expr.has_break_expr ())
-    reseat (expr.get_break_expr_ptr ());
+    reseat (expr.get_break_expr_ptr_unchecked ());
 }
 
 void
index 4157b75ab8adffe1e0a5be80bff3be93b2e1c54b..6973d65bcffffa3bf6b80c1c103e45d685aa4528 100644 (file)
@@ -510,7 +510,7 @@ DefaultASTVisitor::visit (AST::BreakExpr &expr)
     visit (expr.get_label_unchecked ());
 
   if (expr.has_break_expr ())
-    visit (expr.get_break_expr ());
+    visit (expr.get_break_expr_unchecked ());
 }
 
 void
index b9f7d280d8090e827a0503638412232fa5f7f5f3..f550edb088d8c975e1f3afb553f45f18c13e55e8 100644 (file)
@@ -1649,7 +1649,7 @@ ReturnExpr::as_string () const
   std::string str ("return ");
 
   if (has_returned_expr ())
-    str += return_expr->as_string ();
+    str += get_returned_expr ().as_string ();
 
   return str;
 }
@@ -2233,7 +2233,7 @@ BreakExpr::as_string () const
     str += get_label_unchecked ().as_string () + " ";
 
   if (has_break_expr ())
-    str += break_expr->as_string ();
+    str += get_break_expr_unchecked ().as_string ();
 
   return str;
 }
@@ -3726,7 +3726,7 @@ AttributeParser::parse_path_meta_item ()
       {
        lexer->skip_token ();
 
-       std::unique_ptr<Expr> expr = parser->parse_expr ();
+       auto expr = parser->parse_expr ();
 
        // handle error
        // parse_expr should already emit an error and return nullptr
@@ -3734,7 +3734,8 @@ AttributeParser::parse_path_meta_item ()
          return nullptr;
 
        return std::unique_ptr<MetaItemPathExpr> (
-         new MetaItemPathExpr (std::move (path.value ()), std::move (expr)));
+         new MetaItemPathExpr (std::move (path.value ()),
+                               std::move (expr.value ())));
       }
     case COMMA:
       // just simple path
index eda844790f11e004bb8e85fd047fb6a63ae2b9e9..20aca533d918d281c9890fc9fd11abcfbc5bbee7 100644 (file)
@@ -37,7 +37,7 @@ DesugarForLoops::DesugarCtx::make_break_arm ()
     builder.path_in_expression (LangItem::Kind::OPTION_NONE))));
 
   auto break_expr
-    = std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, nullptr, {}, loc));
+    = std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, tl::nullopt, {}, loc));
 
   return MatchCase (std::move (arm), std::move (break_expr));
 }
index f75e39193d09ba83e35f6f4f331f5f43be9b9dd1..f2aec109f509ba1728eb5df564377ad602dd2d1f 100644 (file)
@@ -36,7 +36,7 @@ DesugarWhileLet::DesugarCtx::make_break_arm ()
   auto arm = builder.match_arm (builder.wildcard ());
 
   auto break_expr
-    = std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, nullptr, {}, loc));
+    = std::unique_ptr<Expr> (new BreakExpr (tl::nullopt, tl::nullopt, {}, loc));
 
   return MatchCase (std::move (arm), std::move (break_expr));
 }
index ce3f4141f4dce6c1e57b460a895268620da6a2a3..2e49c50f46f2f0d8e57e1879ddd100b52e5c9601 100644 (file)
@@ -3192,7 +3192,7 @@ class BreakExpr : public ExprWithoutBlock
 {
   std::vector<Attribute> outer_attrs;
   tl::optional<LoopLabel> label;
-  std::unique_ptr<Expr> break_expr;
+  tl::optional<std::unique_ptr<Expr>> break_expr;
   location_t locus;
 
   // TODO: find another way to store this to save memory?
@@ -3206,15 +3206,18 @@ public:
 
   /* Returns whether the break expression has an expression used in the break or
    * not. */
-  bool has_break_expr () const { return break_expr != nullptr; }
+  bool has_break_expr () const { return break_expr.has_value (); }
 
   // Constructor for a break expression
   BreakExpr (tl::optional<LoopLabel> break_label,
-            std::unique_ptr<Expr> expr_in_break,
+            tl::optional<std::unique_ptr<Expr>> expr_in_break,
             std::vector<Attribute> outer_attribs, location_t locus)
     : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
       break_expr (std::move (expr_in_break)), locus (locus)
-  {}
+  {
+    if (this->has_break_expr ())
+      rust_assert (this->break_expr != nullptr);
+  }
 
   // Copy constructor defined to use clone for unique pointer
   BreakExpr (BreakExpr const &other)
@@ -3222,9 +3225,8 @@ public:
       label (other.label), locus (other.locus),
       marked_for_strip (other.marked_for_strip)
   {
-    // guard to protect from null pointer dereference
-    if (other.break_expr != nullptr)
-      break_expr = other.break_expr->clone_expr ();
+    if (other.has_break_expr ())
+      break_expr = other.get_break_expr_unchecked ().clone_expr ();
   }
 
   // Overload assignment operator to clone unique pointer
@@ -3237,10 +3239,10 @@ public:
     outer_attrs = other.outer_attrs;
 
     // guard to protect from null pointer dereference
-    if (other.break_expr != nullptr)
-      break_expr = other.break_expr->clone_expr ();
+    if (other.has_break_expr ())
+      break_expr = other.get_break_expr_unchecked ().clone_expr ();
     else
-      break_expr = nullptr;
+      break_expr = tl::nullopt;
 
     return *this;
   }
@@ -3258,16 +3260,22 @@ public:
   bool is_marked_for_strip () const override { return marked_for_strip; }
 
   // TODO: is this better? Or is a "vis_block" better?
-  Expr &get_break_expr ()
+  Expr &get_break_expr_unchecked ()
   {
     rust_assert (has_break_expr ());
-    return *break_expr;
+    return *break_expr.value ();
   }
 
-  std::unique_ptr<Expr> &get_break_expr_ptr ()
+  const Expr &get_break_expr_unchecked () const
   {
     rust_assert (has_break_expr ());
-    return break_expr;
+    return *break_expr.value ();
+  }
+
+  std::unique_ptr<Expr> &get_break_expr_ptr_unchecked ()
+  {
+    rust_assert (has_break_expr ());
+    return break_expr.value ();
   }
 
   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
@@ -3831,7 +3839,7 @@ protected:
 class ReturnExpr : public ExprWithoutBlock
 {
   std::vector<Attribute> outer_attrs;
-  std::unique_ptr<Expr> return_expr;
+  tl::optional<std::unique_ptr<Expr>> return_expr;
   location_t locus;
 
   // TODO: find another way to store this to save memory?
@@ -3842,10 +3850,10 @@ public:
 
   /* Returns whether the object has an expression returned (i.e. not void return
    * type). */
-  bool has_returned_expr () const { return return_expr != nullptr; }
+  bool has_returned_expr () const { return return_expr.has_value (); }
 
   // Constructor for ReturnExpr.
-  ReturnExpr (std::unique_ptr<Expr> returned_expr,
+  ReturnExpr (tl::optional<std::unique_ptr<Expr>> returned_expr,
              std::vector<Attribute> outer_attribs, location_t locus)
     : outer_attrs (std::move (outer_attribs)),
       return_expr (std::move (returned_expr)), locus (locus)
@@ -3857,8 +3865,8 @@ public:
       locus (other.locus), marked_for_strip (other.marked_for_strip)
   {
     // guard to protect from null pointer dereference
-    if (other.return_expr != nullptr)
-      return_expr = other.return_expr->clone_expr ();
+    if (other.return_expr)
+      return_expr = other.return_expr.value ()->clone_expr ();
   }
 
   // Overloaded assignment operator to clone return_expr pointer
@@ -3870,10 +3878,10 @@ public:
     outer_attrs = other.outer_attrs;
 
     // guard to protect from null pointer dereference
-    if (other.return_expr != nullptr)
-      return_expr = other.return_expr->clone_expr ();
+    if (other.return_expr)
+      return_expr = other.return_expr.value ()->clone_expr ();
     else
-      return_expr = nullptr;
+      return_expr = tl::nullopt;
 
     return *this;
   }
@@ -3893,14 +3901,20 @@ public:
   // TODO: is this better? Or is a "vis_block" better?
   Expr &get_returned_expr ()
   {
-    rust_assert (return_expr != nullptr);
-    return *return_expr;
+    rust_assert (return_expr);
+    return *return_expr.value ();
+  }
+
+  const Expr &get_returned_expr () const
+  {
+    rust_assert (return_expr);
+    return *return_expr.value ();
   }
 
   std::unique_ptr<Expr> &get_returned_expr_ptr ()
   {
-    rust_assert (return_expr != nullptr);
-    return return_expr;
+    rust_assert (return_expr);
+    return return_expr.value ();
   }
 
   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
index ef43ae785b0c11cbc10fd77ce9076cc2cb86a31e..071104036f790dac64a7aeb8ef7f58a24a25c4b2 100644 (file)
@@ -1236,7 +1236,7 @@ CfgStrip::visit (AST::BreakExpr &expr)
    * is the only thing that can be done */
   if (expr.has_break_expr ())
     {
-      auto &break_expr = expr.get_break_expr ();
+      auto &break_expr = expr.get_break_expr_unchecked ();
 
       if (break_expr.is_marked_for_strip ())
        rust_error_at (break_expr.get_locus (),
index 136e6197c697b0486feb00f1a29df2dc0e6edac1..77c747bf5cdc8c1de0bbeeea4faa2f6796ae7fbd 100644 (file)
@@ -326,10 +326,11 @@ parse_reg_operand_in (InlineAsmContext inline_asm_ctx)
        }
 
       auto expr = parser.parse_expr ();
+      rust_assert (expr);
 
       // TODO: When we've succesfully parse an expr, remember to clone_expr()
       // instead of nullptr
-      AST::InlineAsmOperand::In in (reg, std::move (expr));
+      AST::InlineAsmOperand::In in (reg, std::move (expr.value ()));
       inline_asm_ctx.inline_asm.operands.emplace_back (in, locus);
       return inline_asm_ctx;
     }
@@ -344,16 +345,15 @@ parse_reg_operand_out (InlineAsmContext inline_asm_ctx)
   if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "out"))
     {
       auto reg = parse_reg (inline_asm_ctx);
-      std::unique_ptr<AST::Expr> expr = parser.parse_expr ();
-
-      rust_assert (expr != nullptr);
+      auto expr = parser.parse_expr ();
+      rust_assert (expr);
 
       /*auto expr_ptr =
         std::make_unique<AST::Expr>(AST::LiteralExpr(Literal))*/
 
       // TODO: When we've succesfully parse an expr, remember to clone_expr()
       // instead of nullptr
-      AST::InlineAsmOperand::Out out (reg, false, std::move (expr));
+      AST::InlineAsmOperand::Out out (reg, false, std::move (expr.value ()));
 
       inline_asm_ctx.inline_asm.operands.emplace_back (out, locus);
 
@@ -402,10 +402,8 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
 
       // TODO: Is error propogation our top priority, the ? in rust's asm.rs is
       // doing a lot of work.
-      std::unique_ptr<AST::Expr> in_expr = parser.parse_expr ();
-      rust_assert (in_expr != nullptr);
-
-      std::unique_ptr<AST::Expr> out_expr;
+      auto in_expr = parser.parse_expr ();
+      rust_assert (in_expr);
 
       if (parser.skip_token (MATCH_ARROW))
        {
@@ -413,10 +411,12 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
            {
              // auto result = parse_format_string (inline_asm_ctx);
 
-             out_expr = parser.parse_expr ();
+             auto out_expr = parser.parse_expr ();
+             rust_assert (out_expr);
 
              AST::InlineAsmOperand::SplitInOut splitinout (
-               reg, false, std::move (in_expr), std::move (out_expr));
+               reg, false, std::move (in_expr.value ()),
+               std::move (out_expr.value ()));
 
              inline_asm_ctx.inline_asm.operands.emplace_back (splitinout,
                                                               locus);
@@ -439,7 +439,8 @@ parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
        }
       else
        {
-         AST::InlineAsmOperand::InOut inout (reg, false, std::move (in_expr));
+         AST::InlineAsmOperand::InOut inout (reg, false,
+                                             std::move (in_expr.value ()));
          inline_asm_ctx.inline_asm.operands.emplace_back (inout, locus);
          // https://github.com/rust-lang/rust/blob/a3167859f2fd8ff2241295469876a2b687280bdc/compiler/rustc_builtin_macros/src/asm.rs#L137
          // RUST VERSION: ast::InlineAsmOperand::InOut { reg, expr, late: false
@@ -1090,10 +1091,11 @@ parse_llvm_operands (LlvmAsmContext &ctx, std::vector<AST::LlvmOperand> &result)
       ParseRestrictions restrictions;
       restrictions.expr_can_be_null = true;
       auto expr = parser.parse_expr ();
+      rust_assert (expr);
 
       parser.skip_token (RIGHT_PAREN);
 
-      result.emplace_back (constraint, std::move (expr));
+      result.emplace_back (constraint, std::move (expr.value ()));
 
       if (parser.peek_current_token ()->get_id () == COMMA)
        parser.skip_token (COMMA);
index b4cbe0f8db72e851e51fbb63514c31b07127bc25..d7d2aa3d44738dd29e5c83940ecdae4f2a0e1850 100644 (file)
@@ -42,8 +42,9 @@ format_args_parse_expr (location_t invoc_locus, AST::MacroInvocData &invoc,
                        Parser<MacroInvocLexer> &parser,
                        BuiltinMacro macro_kind)
 {
-  std::unique_ptr<AST::Expr> format_expr = parser.parse_expr ();
-  rust_assert (format_expr);
+  auto format_expr_res = parser.parse_expr ();
+  rust_assert (format_expr_res);
+  auto format_expr = std::move (format_expr_res.value ());
 
   if (format_expr->get_expr_kind () == AST::Expr::Kind::MacroInvocation)
     {
@@ -114,7 +115,8 @@ format_args_parse_arguments (AST::MacroInvocData &invoc,
          if (!expr)
            rust_unreachable ();
 
-         args.push (AST::FormatArgument::named (ident, std::move (expr)));
+         args.push (
+           AST::FormatArgument::named (ident, std::move (expr.value ())));
        }
       else
        {
@@ -124,7 +126,7 @@ format_args_parse_arguments (AST::MacroInvocData &invoc,
          if (!expr)
            rust_unreachable ();
 
-         args.push (AST::FormatArgument::normal (std::move (expr)));
+         args.push (AST::FormatArgument::normal (std::move (expr.value ())));
        }
       // we need to skip commas, don't we?
     }
index 2e5710c3bfa11b1e4891affe106a5e22ed736aa6..e1b983c524f53942fb6d6dc8db5d6bf9591436f7 100644 (file)
@@ -151,7 +151,7 @@ try_expand_many_expr (Parser<MacroInvocLexer> &parser,
       auto expr = parser.parse_expr (AST::AttrVec (), restrictions);
       // something must be so wrong that the expression could not be parsed
       rust_assert (expr);
-      result.push_back (std::move (expr));
+      result.push_back (std::move (expr.value ()));
 
       auto next_token = parser.peek_current_token ();
       if (!parser.skip_token (COMMA) && next_token->get_id () != last_token_id)
index 2f6e36d4febdbac9b59ea1743d340a28ff7b74ed..1461e523eaa966e8ff4c55f98938ecd8a929f98f 100644 (file)
@@ -251,7 +251,7 @@ MacroBuiltin::include_handler (location_t invoc_locus,
     parsed_items = parser.parse_items ().value_or (
       std::vector<std::unique_ptr<AST::Item>>{});
   else
-    parsed_expr = parser.parse_expr ();
+    parsed_expr = parser.parse_expr ().value ();
 
   bool has_error = !parser.get_errors ().empty ();
 
index a37252644dea3af58816cc6ee821996dddcb1073..3848328ce72532c63806c4904d49da359a74ecae 100644 (file)
@@ -987,7 +987,8 @@ transcribe_expression (Parser<MacroInvocLexer> &parser)
 
   auto end = lexer.get_offs ();
 
-  return AST::Fragment ({std::move (expr)}, lexer.get_token_slice (start, end));
+  return AST::Fragment ({std::move (expr.value ())},
+                       lexer.get_token_slice (start, end));
 }
 
 /**
index e3596bd77bb335e9cbf14713870bebe4d99c3a50..d76811855f6c2dc5ba520156409ac2ae850d8bb5 100644 (file)
@@ -639,7 +639,7 @@ ASTLoweringExpr::visit (AST::BreakExpr &expr)
 
   HIR::Expr *break_expr
     = expr.has_break_expr ()
-       ? ASTLoweringExpr::translate (expr.get_break_expr ())
+       ? ASTLoweringExpr::translate (expr.get_break_expr_unchecked ())
        : nullptr;
 
   auto crate_num = mappings.get_current_crate ();
index 4d2d0e1c1a72df5be675112fb42be63f02391d28..73d42f0c0fa3ae5c2c47388e0d1cc6c441cc65c0 100644 (file)
@@ -303,6 +303,23 @@ private:
   Self (Kind kind) : kind (kind) {}
 };
 
+enum class Expr
+{
+  MALFORMED,
+  CHILD_ERROR,
+  NULL_EXPR,
+  NULL_DENOTATION,
+  LEFT_DENOTATION,
+};
+
+enum class StructExprField
+{
+  MALFORMED,
+  CHILD_ERROR,
+  // Not a hard error
+  STRUCT_BASE,
+};
+
 // Generic intermediate AST node error used when the errors need no special
 // handling
 enum class Node
index 4eae47cfd2f1cc71b7bb31c336434bde39ff77d6..80a47803669f01fa3bf1a093d6622a3e37673536 100644 (file)
@@ -51,8 +51,8 @@ Parser<ManagedTokenSource>::parse_block_expr (
   const_TokenPtr t = lexer.peek_token ();
   while (t->get_id () != RIGHT_CURLY)
     {
-      ExprOrStmt expr_or_stmt = parse_stmt_or_expr ();
-      if (expr_or_stmt.is_error ())
+      auto expr_or_stmt = parse_stmt_or_expr ();
+      if (!expr_or_stmt)
        {
          skip_after_end_block ();
          return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
@@ -60,15 +60,14 @@ Parser<ManagedTokenSource>::parse_block_expr (
 
       t = lexer.peek_token ();
 
-      if (expr_or_stmt.stmt != nullptr)
+      if (expr_or_stmt->stmt != nullptr)
        {
-         stmts.push_back (std::move (expr_or_stmt.stmt));
+         stmts.push_back (std::move (expr_or_stmt->stmt));
        }
       else
        {
          // assign to expression and end parsing inside
-         expr = std::move (expr_or_stmt.expr);
-         break;
+         expr = std::move (expr_or_stmt->expr);
        }
     }
 
@@ -113,7 +112,7 @@ Parser<ManagedTokenSource>::parse_anon_const ()
   if (!expr)
     return tl::make_unexpected (Parse::Error::Node{});
 
-  return AST::AnonConst (std::move (expr), locus);
+  return AST::AnonConst (std::move (expr.value ()), locus);
 }
 
 /* Parse a "const block", a block preceded by the `const` keyword whose
@@ -153,7 +152,7 @@ Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs)
   AST::AttrVec inner_attrs = parse_inner_attributes ();
 
   // parse required expr inside parentheses
-  std::unique_ptr<AST::Expr> expr_in_parens = parse_expr ();
+  auto expr_in_parens = parse_expr ();
   if (!expr_in_parens)
     {
       // skip after somewhere?
@@ -168,8 +167,9 @@ Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs)
     }
 
   return std::unique_ptr<AST::GroupedExpr> (
-    new AST::GroupedExpr (std::move (expr_in_parens), std::move (inner_attrs),
-                         std::move (outer_attrs), locus));
+    new AST::GroupedExpr (std::move (expr_in_parens.value ()),
+                         std::move (inner_attrs), std::move (outer_attrs),
+                         locus));
 }
 
 // Parses a closure expression (closure definition).
@@ -282,8 +282,8 @@ Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs)
       // must be expr-only closure
 
       // parse expr, which is required
-      std::unique_ptr<AST::Expr> expr = parse_expr ();
-      if (expr == nullptr)
+      auto expr = parse_expr ();
+      if (!expr)
        {
          Error error (t->get_locus (),
                       "failed to parse expression in closure");
@@ -294,8 +294,9 @@ Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs)
        }
 
       return std::unique_ptr<AST::ClosureExprInner> (
-       new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
-                                  has_move, std::move (outer_attrs)));
+       new AST::ClosureExprInner (std::move (expr.value ()),
+                                  std::move (params), locus, has_move,
+                                  std::move (outer_attrs)));
     }
 }
 
@@ -378,7 +379,7 @@ Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs)
 }
 
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::BoxExpr>
+tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_box_expr (AST::AttrVec outer_attrs,
                                            location_t pratt_parsed_loc)
 {
@@ -392,15 +393,18 @@ Parser<ManagedTokenSource>::parse_box_expr (AST::AttrVec outer_attrs,
   ParseRestrictions restrictions;
   restrictions.expr_can_be_null = false;
 
-  std::unique_ptr<AST::Expr> expr = parse_expr (AST::AttrVec (), restrictions);
+  auto expr = parse_expr (AST::AttrVec (), restrictions);
+  if (!expr)
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 
   return std::unique_ptr<AST::BoxExpr> (
-    new AST::BoxExpr (std::move (expr), std::move (outer_attrs), locus));
+    new AST::BoxExpr (std::move (expr.value ()), std::move (outer_attrs),
+                     locus));
 }
 
 // Parses a return expression (including any expression to return).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ReturnExpr>
+tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs,
                                               location_t pratt_parsed_loc)
 {
@@ -414,17 +418,18 @@ Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs,
   // parse expression to return, if it exists
   ParseRestrictions restrictions;
   restrictions.expr_can_be_null = true;
-  std::unique_ptr<AST::Expr> returned_expr
-    = parse_expr (AST::AttrVec (), restrictions);
+  auto returned_expr = parse_expr (AST::AttrVec (), restrictions);
+  tl::optional<std::unique_ptr<AST::Expr>> expr = tl::nullopt;
+  if (returned_expr)
+    expr = std::move (returned_expr.value ());
 
-  return std::unique_ptr<AST::ReturnExpr> (
-    new AST::ReturnExpr (std::move (returned_expr), std::move (outer_attrs),
-                        locus));
+  return std::make_unique<AST::ReturnExpr> (std::move (expr),
+                                           std::move (outer_attrs), locus);
 }
 
 // Parses a try expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::TryExpr>
+tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs,
                                            location_t pratt_parsed_loc)
 {
@@ -438,13 +443,7 @@ Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs,
   auto block_expr = parse_block_expr ();
 
   if (!block_expr)
-    {
-      Error error (lexer.peek_token ()->get_locus (),
-                  "failed to parse try block expression");
-      add_error (std::move (error));
-
-      return nullptr;
-    }
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 
   return std::unique_ptr<AST::TryExpr> (
     new AST::TryExpr (std::move (block_expr.value ()), std::move (outer_attrs),
@@ -454,7 +453,7 @@ Parser<ManagedTokenSource>::parse_try_expr (AST::AttrVec outer_attrs,
 /* Parses a break expression (including any label to break to AND any return
  * expression). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::BreakExpr>
+tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs,
                                              location_t pratt_parsed_loc)
 {
@@ -473,12 +472,18 @@ Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs,
   // parse break return expression if it exists
   ParseRestrictions restrictions;
   restrictions.expr_can_be_null = true;
-  std::unique_ptr<AST::Expr> return_expr
-    = parse_expr (AST::AttrVec (), restrictions);
+  auto return_expr = parse_expr (AST::AttrVec (), restrictions);
 
-  return std::unique_ptr<AST::BreakExpr> (
-    new AST::BreakExpr (std::move (label), std::move (return_expr),
-                       std::move (outer_attrs), locus));
+  if (return_expr)
+    return std::unique_ptr<AST::BreakExpr> (
+      new AST::BreakExpr (std::move (label), std::move (return_expr.value ()),
+                         std::move (outer_attrs), locus));
+  else if (return_expr.error () == Parse::Error::Expr::NULL_EXPR)
+    return std::unique_ptr<AST::BreakExpr> (
+      new AST::BreakExpr (std::move (label), tl::nullopt,
+                         std::move (outer_attrs), locus));
+  else
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 }
 
 // Parses a continue expression (including any label to continue from).
@@ -499,15 +504,15 @@ Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs,
                 ? tl::optional<AST::Lifetime> (parsed_label.value ())
                 : tl::nullopt;
 
-  return std::unique_ptr<AST::ContinueExpr> (
-    new AST::ContinueExpr (std::move (label), std::move (outer_attrs), locus));
+  return std::make_unique<AST::ContinueExpr> (std::move (label),
+                                             std::move (outer_attrs), locus);
 }
 
 /* Parses an if expression of any kind, including with else, else if, else if
  * let, and neither. Note that any outer attributes will be ignored because if
  * expressions don't support them. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::IfExpr>
+tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
                                           location_t pratt_parsed_loc)
 {
@@ -519,7 +524,7 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
       if (!skip_token (IF))
        {
          skip_after_end_block ();
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
 
@@ -532,36 +537,37 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   /* parse required condition expr - HACK to prevent struct expr from being
    * parsed */
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> condition = parse_expr ({}, no_struct_expr);
-  if (condition == nullptr)
+  auto condition = parse_expr ({}, no_struct_expr);
+  if (!condition)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse condition expression in if expression");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   // parse required block expr
   auto if_body = parse_block_expr ();
   if (!if_body)
-    return nullptr;
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 
   // branch to parse end or else (and then else, else if, or else if let)
   if (lexer.peek_token ()->get_id () != ELSE)
     {
       // single selection - end of if expression
       return std::unique_ptr<AST::IfExpr> (
-       new AST::IfExpr (std::move (condition), std::move (if_body.value ()),
-                        std::move (outer_attrs), locus));
+       new AST::IfExpr (std::move (condition.value ()),
+                        std::move (if_body.value ()), std::move (outer_attrs),
+                        locus));
     }
   else
     {
@@ -587,11 +593,11 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
                add_error (std::move (error));
 
                // skip somewhere?
-               return nullptr;
+               return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
              }
 
            return std::unique_ptr<AST::IfExprConseqElse> (
-             new AST::IfExprConseqElse (std::move (condition),
+             new AST::IfExprConseqElse (std::move (condition.value ()),
                                         std::move (if_body.value ()),
                                         std::move (else_body.value ()),
                                         std::move (outer_attrs), locus));
@@ -603,9 +609,8 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
            if (lexer.peek_token (1)->get_id () == LET)
              {
                // parse if let expr (required)
-               std::unique_ptr<AST::IfLetExpr> if_let_expr
-                 = parse_if_let_expr ();
-               if (if_let_expr == nullptr)
+               auto if_let_expr = parse_if_let_expr ();
+               if (!if_let_expr)
                  {
                    Error error (lexer.peek_token ()->get_locus (),
                                 "failed to parse (else) if let expression "
@@ -613,20 +618,20 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
                    add_error (std::move (error));
 
                    // skip somewhere?
-                   return nullptr;
+                   return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
                  }
 
                return std::unique_ptr<AST::IfExprConseqElse> (
-                 new AST::IfExprConseqElse (std::move (condition),
+                 new AST::IfExprConseqElse (std::move (condition.value ()),
                                             std::move (if_body.value ()),
-                                            std::move (if_let_expr),
+                                            std::move (if_let_expr.value ()),
                                             std::move (outer_attrs), locus));
              }
            else
              {
                // parse if expr (required)
-               std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
-               if (if_expr == nullptr)
+               auto if_expr = parse_if_expr ();
+               if (!if_expr)
                  {
                    Error error (lexer.peek_token ()->get_locus (),
                                 "failed to parse (else) if expression after "
@@ -634,13 +639,13 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
                    add_error (std::move (error));
 
                    // skip somewhere?
-                   return nullptr;
+                   return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
                  }
 
                return std::unique_ptr<AST::IfExprConseqElse> (
-                 new AST::IfExprConseqElse (std::move (condition),
+                 new AST::IfExprConseqElse (std::move (condition.value ()),
                                             std::move (if_body.value ()),
-                                            std::move (if_expr),
+                                            std::move (if_expr.value ()),
                                             std::move (outer_attrs), locus));
              }
          }
@@ -651,7 +656,7 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
                            t->get_token_description ()));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
 }
@@ -660,7 +665,7 @@ Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
  * if let, and none. Note that any outer attributes will be ignored as if let
  * expressions don't support them. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::IfLetExpr>
+tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
                                               location_t pratt_parsed_loc)
 {
@@ -672,7 +677,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
       if (!skip_token (IF))
        {
          skip_after_end_block ();
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
 
@@ -685,7 +690,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
   lexer.skip_token ();
 
@@ -700,27 +705,27 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   if (!skip_token (EQUAL))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   // parse expression (required) - HACK to prevent struct expr being parsed
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> scrutinee_expr = parse_expr ({}, no_struct_expr);
-  if (scrutinee_expr == nullptr)
+  auto scrutinee_expr = parse_expr ({}, no_struct_expr);
+  if (!scrutinee_expr)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse scrutinee expression in if let expression");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   /* TODO: check for expression not being a struct expression or lazy boolean
    * expression here? or actually probably in semantic analysis. */
@@ -735,7 +740,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   // branch to parse end or else (and then else, else if, or else if let)
@@ -743,7 +748,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
     {
       // single selection - end of if let expression
       return std::unique_ptr<AST::IfLetExpr> (new AST::IfLetExpr (
-       std::move (match_arm_pattern), std::move (scrutinee_expr),
+       std::move (match_arm_pattern), std::move (scrutinee_expr.value ()),
        std::move (if_let_body.value ()), std::move (outer_attrs), locus));
     }
   else
@@ -770,12 +775,12 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
                add_error (std::move (error));
 
                // skip somewhere?
-               return nullptr;
+               return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
              }
 
            return std::unique_ptr<AST::IfLetExprConseqElse> (
              new AST::IfLetExprConseqElse (std::move (match_arm_pattern),
-                                           std::move (scrutinee_expr),
+                                           std::move (scrutinee_expr.value ()),
                                            std::move (if_let_body.value ()),
                                            std::move (else_body.value ()),
                                            std::move (outer_attrs), locus));
@@ -787,9 +792,8 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
            if (lexer.peek_token (1)->get_id () == LET)
              {
                // parse if let expr (required)
-               std::unique_ptr<AST::IfLetExpr> if_let_expr
-                 = parse_if_let_expr ();
-               if (if_let_expr == nullptr)
+               auto if_let_expr = parse_if_let_expr ();
+               if (!if_let_expr)
                  {
                    Error error (lexer.peek_token ()->get_locus (),
                                 "failed to parse (else) if let expression "
@@ -797,20 +801,22 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
                    add_error (std::move (error));
 
                    // skip somewhere?
-                   return nullptr;
+                   return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
                  }
 
                return std::unique_ptr<AST::IfLetExprConseqElse> (
                  new AST::IfLetExprConseqElse (
-                   std::move (match_arm_pattern), std::move (scrutinee_expr),
-                   std::move (if_let_body.value ()), std::move (if_let_expr),
-                   std::move (outer_attrs), locus));
+                   std::move (match_arm_pattern),
+                   std::move (scrutinee_expr.value ()),
+                   std::move (if_let_body.value ()),
+                   std::move (if_let_expr.value ()), std::move (outer_attrs),
+                   locus));
              }
            else
              {
                // parse if expr (required)
-               std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
-               if (if_expr == nullptr)
+               auto if_expr = parse_if_expr ();
+               if (!if_expr)
                  {
                    Error error (lexer.peek_token ()->get_locus (),
                                 "failed to parse (else) if expression after "
@@ -818,14 +824,16 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
                    add_error (std::move (error));
 
                    // skip somewhere?
-                   return nullptr;
+                   return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
                  }
 
                return std::unique_ptr<AST::IfLetExprConseqElse> (
                  new AST::IfLetExprConseqElse (
-                   std::move (match_arm_pattern), std::move (scrutinee_expr),
-                   std::move (if_let_body.value ()), std::move (if_expr),
-                   std::move (outer_attrs), locus));
+                   std::move (match_arm_pattern),
+                   std::move (scrutinee_expr.value ()),
+                   std::move (if_let_body.value ()),
+                   std::move (if_expr.value ()), std::move (outer_attrs),
+                   locus));
              }
          }
        default:
@@ -836,7 +844,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
                   t->get_token_description ()));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
 }
@@ -847,7 +855,7 @@ Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
 /* Parses a "loop" infinite loop expression. Label is not parsed and should be
  * parsed via parse_labelled_loop_expr, which would call this. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::LoopExpr>
+tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
                                             tl::optional<AST::LoopLabel> label,
                                             location_t pratt_parsed_loc)
@@ -863,7 +871,7 @@ Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
       if (!skip_token (LOOP))
        {
          skip_after_end_block ();
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
   else
@@ -875,7 +883,7 @@ Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
   // parse loop body, which is required
   auto loop_body = parse_block_expr ();
   if (!loop_body)
-    return nullptr;
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 
   return std::unique_ptr<AST::LoopExpr> (
     new AST::LoopExpr (std::move (loop_body.value ()), locus, std::move (label),
@@ -885,7 +893,7 @@ Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
 /* Parses a "while" loop expression. Label is not parsed and should be parsed
  * via parse_labelled_loop_expr, which would call this. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::WhileLoopExpr>
+tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_while_loop_expr (
   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label,
   location_t pratt_parsed_loc)
@@ -901,7 +909,7 @@ Parser<ManagedTokenSource>::parse_while_loop_expr (
       if (!skip_token (WHILE))
        {
          skip_after_end_block ();
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
   else
@@ -919,21 +927,21 @@ Parser<ManagedTokenSource>::parse_while_loop_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   // parse loop predicate (required) with HACK to prevent struct expr parsing
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> predicate = parse_expr ({}, no_struct_expr);
-  if (predicate == nullptr)
+  auto predicate = parse_expr ({}, no_struct_expr);
+  if (!predicate)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse predicate expression in while loop");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   /* TODO: check that it isn't struct expression here? actually, probably in
    * semantic analysis */
@@ -947,18 +955,19 @@ Parser<ManagedTokenSource>::parse_while_loop_expr (
       add_error (std::move (error));
 
       // skip somewhere
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   return std::unique_ptr<AST::WhileLoopExpr> (
-    new AST::WhileLoopExpr (std::move (predicate), std::move (body.value ()),
-                           locus, std::move (label), std::move (outer_attrs)));
+    new AST::WhileLoopExpr (std::move (predicate.value ()),
+                           std::move (body.value ()), locus, std::move (label),
+                           std::move (outer_attrs)));
 }
 
 /* Parses a "while let" loop expression. Label is not parsed and should be
  * parsed via parse_labelled_loop_expr, which would call this. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::WhileLetLoopExpr>
+tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_while_let_loop_expr (
   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
 {
@@ -979,7 +988,7 @@ Parser<ManagedTokenSource>::parse_while_let_loop_expr (
       add_error (std::move (error));
 
       // skip somewhere
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
   // as this token is definitely let now, save the computation of comparison
   lexer.skip_token ();
@@ -993,28 +1002,28 @@ Parser<ManagedTokenSource>::parse_while_let_loop_expr (
       Error error (lexer.peek_token ()->get_locus (),
                   "should be at least 1 pattern");
       add_error (std::move (error));
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   if (!skip_token (EQUAL))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   /* parse predicate expression, which is required (and HACK to prevent struct
    * expr) */
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> predicate_expr = parse_expr ({}, no_struct_expr);
-  if (predicate_expr == nullptr)
+  auto predicate_expr = parse_expr ({}, no_struct_expr);
+  if (!predicate_expr)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse predicate expression in while let loop");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   /* TODO: ensure that struct expression is not parsed? Actually, probably in
    * semantic analysis. */
@@ -1028,12 +1037,12 @@ Parser<ManagedTokenSource>::parse_while_let_loop_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   return std::unique_ptr<AST::WhileLetLoopExpr> (
     new AST::WhileLetLoopExpr (std::move (predicate_pattern),
-                              std::move (predicate_expr),
+                              std::move (predicate_expr.value ()),
                               std::move (body.value ()), locus,
                               std::move (label), std::move (outer_attrs)));
 }
@@ -1041,7 +1050,7 @@ Parser<ManagedTokenSource>::parse_while_let_loop_expr (
 /* Parses a "for" iterative loop. Label is not parsed and should be parsed via
  * parse_labelled_loop_expr, which would call this. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ForLoopExpr>
+tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_for_loop_expr (
   AST::AttrVec outer_attrs, tl::optional<AST::LoopLabel> label)
 {
@@ -1054,35 +1063,35 @@ Parser<ManagedTokenSource>::parse_for_loop_expr (
 
   // parse pattern, which is required
   std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
-  if (pattern == nullptr)
+  if (!pattern)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse iterator pattern in for loop");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   if (!skip_token (IN))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   /* parse iterator expression, which is required - also HACK to prevent
    * struct expr */
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> expr = parse_expr ({}, no_struct_expr);
-  if (expr == nullptr)
+  auto expr = parse_expr ({}, no_struct_expr);
+  if (!expr)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse iterator expression in for loop");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
 
@@ -1095,17 +1104,17 @@ Parser<ManagedTokenSource>::parse_for_loop_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   return std::unique_ptr<AST::ForLoopExpr> (
-    new AST::ForLoopExpr (std::move (pattern), std::move (expr),
+    new AST::ForLoopExpr (std::move (pattern), std::move (expr.value ()),
                          std::move (body.value ()), locus, std::move (label),
                          std::move (outer_attrs)));
 }
 
 // Parses a loop expression with label (any kind of loop - disambiguates).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
                                                      AST::AttrVec outer_attrs)
 {
@@ -1125,7 +1134,7 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
                       "label) - found %qs",
                       tok->get_token_description ());
          add_error (std::move (error));
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
 
       else
@@ -1135,7 +1144,7 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
          add_error (std::move (error));
 
          // skip?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
     }
 
@@ -1154,24 +1163,13 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
     case WHILE:
       // further disambiguate into while vs while let
       if (lexer.peek_token (1)->get_id () == LET)
-       {
-         return parse_while_let_loop_expr (std::move (outer_attrs),
-                                           std::move (label));
-       }
+       return parse_while_let_loop_expr (std::move (outer_attrs),
+                                         std::move (label));
       else
-       {
-         return parse_while_loop_expr (std::move (outer_attrs),
-                                       std::move (label));
-       }
+       return parse_while_loop_expr (std::move (outer_attrs),
+                                     std::move (label));
     case LEFT_CURLY:
-      {
-       auto block
-         = parse_block_expr (std::move (outer_attrs), std::move (label));
-       if (block)
-         return std::move (block.value ());
-       else
-         return nullptr;
-      }
+      return parse_block_expr (std::move (outer_attrs), std::move (label));
     default:
       // error
       add_error (Error (t->get_locus (),
@@ -1179,13 +1177,13 @@ Parser<ManagedTokenSource>::parse_labelled_loop_expr (const_TokenPtr tok,
                        t->get_token_description ()));
 
       // skip?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 }
 
 // Parses a match expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::MatchExpr>
+tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
                                              location_t pratt_parsed_loc)
 {
@@ -1200,15 +1198,15 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
    * expr) */
   ParseRestrictions no_struct_expr;
   no_struct_expr.can_be_struct_expr = false;
-  std::unique_ptr<AST::Expr> scrutinee = parse_expr ({}, no_struct_expr);
-  if (scrutinee == nullptr)
+  auto scrutinee = parse_expr ({}, no_struct_expr);
+  if (!scrutinee)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse scrutinee expression in match expression");
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   /* TODO: check for scrutinee expr not being struct expr? or do so in
    * semantic analysis */
@@ -1216,7 +1214,7 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
   if (!skip_token (LEFT_CURLY))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   // parse inner attributes (if they exist)
@@ -1238,34 +1236,35 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
                       "failed to parse match arm in match arms");
          add_error (std::move (error));
 
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
 
       if (!skip_token (MATCH_ARROW))
        {
          // skip after somewhere?
          // TODO is returning here a good idea? or is break better?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
 
       ParseRestrictions restrictions;
       restrictions.expr_can_be_stmt = true;
 
-      std::unique_ptr<AST::Expr> expr = parse_expr ({}, restrictions);
+      auto expr = parse_expr ({}, restrictions);
 
-      if (expr == nullptr)
+      if (!expr)
        {
          Error error (lexer.peek_token ()->get_locus (),
                       "failed to parse expr in match arm in match expr");
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
 
-      bool is_expr_without_block = expr->is_expr_without_block ();
+      bool is_expr_without_block = expr.value ()->is_expr_without_block ();
 
-      match_arms.push_back (AST::MatchCase (std::move (arm), std::move (expr)));
+      match_arms.push_back (
+       AST::MatchCase (std::move (arm), std::move (expr.value ())));
 
       // handle comma presence
       if (lexer.peek_token ()->get_id () != COMMA)
@@ -1284,7 +1283,7 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
                           "expression in match arm (if not final case)");
              add_error (std::move (error));
 
-             return nullptr;
+             return tl::unexpected (Parse::Error::Node::MALFORMED);
            }
          else
            {
@@ -1298,20 +1297,20 @@ Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
   if (!skip_token (RIGHT_CURLY))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 
   match_arms.shrink_to_fit ();
 
   return std::unique_ptr<AST::MatchExpr> (
-    new AST::MatchExpr (std::move (scrutinee), std::move (match_arms),
+    new AST::MatchExpr (std::move (scrutinee.value ()), std::move (match_arms),
                        std::move (inner_attrs), std::move (outer_attrs),
                        locus));
 }
 
 // Parses an async block expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::AsyncBlockExpr>
+tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
 {
   location_t locus = lexer.peek_token ()->get_locus ();
@@ -1335,7 +1334,7 @@ Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   return std::unique_ptr<AST::AsyncBlockExpr> (
@@ -1345,7 +1344,7 @@ Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
 
 // Parses an unsafe block expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::UnsafeBlockExpr>
+tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_unsafe_block_expr (
   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
 {
@@ -1366,7 +1365,7 @@ Parser<ManagedTokenSource>::parse_unsafe_block_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
   return std::unique_ptr<AST::UnsafeBlockExpr> (
     new AST::UnsafeBlockExpr (std::move (block_expr.value ()),
@@ -1375,7 +1374,7 @@ Parser<ManagedTokenSource>::parse_unsafe_block_expr (
 
 // Parses an array definition expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArrayExpr>
+tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
                                              location_t pratt_parsed_loc)
 {
@@ -1406,8 +1405,8 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
     {
       // should have array elements
       // parse initial expression, which is required for either
-      std::unique_ptr<AST::Expr> initial_expr = parse_expr ();
-      if (initial_expr == nullptr)
+      auto initial_expr = parse_expr ();
+      if (!initial_expr)
        {
          Error error (lexer.peek_token ()->get_locus (),
                       "could not parse expression in array expression "
@@ -1415,7 +1414,7 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
 
       if (lexer.peek_token ()->get_id () == SEMICOLON)
@@ -1424,8 +1423,8 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
          lexer.skip_token ();
 
          // parse copy amount expression (required)
-         std::unique_ptr<AST::Expr> copy_amount = parse_expr ();
-         if (copy_amount == nullptr)
+         auto copy_amount = parse_expr ();
+         if (!copy_amount)
            {
              Error error (lexer.peek_token ()->get_locus (),
                           "could not parse copy amount expression in array "
@@ -1433,14 +1432,15 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
              add_error (std::move (error));
 
              // skip somewhere?
-             return nullptr;
+             return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
            }
 
          skip_token (RIGHT_SQUARE);
 
          std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
-           new AST::ArrayElemsCopied (std::move (initial_expr),
-                                      std::move (copy_amount), locus));
+           new AST::ArrayElemsCopied (std::move (initial_expr.value ()),
+                                      std::move (copy_amount.value ()),
+                                      locus));
          return std::unique_ptr<AST::ArrayExpr> (
            new AST::ArrayExpr (std::move (copied_array_elems),
                                std::move (inner_attrs),
@@ -1451,7 +1451,7 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
          // single-element array expression
          std::vector<std::unique_ptr<AST::Expr>> exprs;
          exprs.reserve (1);
-         exprs.push_back (std::move (initial_expr));
+         exprs.push_back (std::move (initial_expr.value ()));
          exprs.shrink_to_fit ();
 
          skip_token (RIGHT_SQUARE);
@@ -1467,7 +1467,7 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
        {
          // multi-element array expression (or trailing comma)
          std::vector<std::unique_ptr<AST::Expr>> exprs;
-         exprs.push_back (std::move (initial_expr));
+         exprs.push_back (std::move (initial_expr.value ()));
 
          const_TokenPtr t = lexer.peek_token ();
          while (t->get_id () == COMMA)
@@ -1479,17 +1479,17 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
                break;
 
              // parse expression (required)
-             std::unique_ptr<AST::Expr> expr = parse_expr ();
-             if (expr == nullptr)
+             auto expr = parse_expr ();
+             if (!expr)
                {
                  Error error (lexer.peek_token ()->get_locus (),
                               "failed to parse element in array expression");
                  add_error (std::move (error));
 
                  // skip somewhere?
-                 return nullptr;
+                 return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
                }
-             exprs.push_back (std::move (expr));
+             exprs.push_back (std::move (expr.value ()));
 
              t = lexer.peek_token ();
            }
@@ -1514,14 +1514,14 @@ Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Node::MALFORMED);
        }
     }
 }
 
 // Parses a grouped or tuple expression (disambiguates).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ExprWithoutBlock>
+tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
   AST::AttrVec outer_attrs, location_t pratt_parsed_loc)
 {
@@ -1549,15 +1549,15 @@ Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
     }
 
   // parse first expression (required)
-  std::unique_ptr<AST::Expr> first_expr = parse_expr ();
-  if (first_expr == nullptr)
+  auto first_expr = parse_expr ();
+  if (!first_expr)
     {
       Error error (lexer.peek_token ()->get_locus (),
                   "failed to parse expression in grouped or tuple expression");
       add_error (std::move (error));
 
       // skip after somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
     }
 
   // detect whether grouped expression with right parentheses as next token
@@ -1568,14 +1568,15 @@ Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
 
       // create grouped expr
       return std::unique_ptr<AST::GroupedExpr> (
-       new AST::GroupedExpr (std::move (first_expr), std::move (inner_attrs),
-                             std::move (outer_attrs), locus));
+       new AST::GroupedExpr (std::move (first_expr.value ()),
+                             std::move (inner_attrs), std::move (outer_attrs),
+                             locus));
     }
   else if (lexer.peek_token ()->get_id () == COMMA)
     {
       // tuple expr
       std::vector<std::unique_ptr<AST::Expr>> exprs;
-      exprs.push_back (std::move (first_expr));
+      exprs.push_back (std::move (first_expr.value ()));
 
       // parse potential other tuple exprs
       const_TokenPtr t = lexer.peek_token ();
@@ -1588,17 +1589,17 @@ Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
            break;
 
          // parse expr, which is now required
-         std::unique_ptr<AST::Expr> expr = parse_expr ();
-         if (expr == nullptr)
+         auto expr = parse_expr ();
+         if (!expr)
            {
              Error error (lexer.peek_token ()->get_locus (),
                           "failed to parse expr in tuple expr");
              add_error (std::move (error));
 
              // skip somewhere?
-             return nullptr;
+             return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
            }
-         exprs.push_back (std::move (expr));
+         exprs.push_back (std::move (expr.value ()));
 
          t = lexer.peek_token ();
        }
@@ -1622,13 +1623,14 @@ Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Node::MALFORMED);
     }
 }
 
 // Parses a struct expression field.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::StructExprField>
+tl::expected<std::unique_ptr<AST::StructExprField>,
+            Parse::Error::StructExprField>
 Parser<ManagedTokenSource>::parse_struct_expr_field ()
 {
   AST::AttrVec outer_attrs = parse_outer_attributes ();
@@ -1643,20 +1645,21 @@ Parser<ManagedTokenSource>::parse_struct_expr_field ()
          lexer.skip_token (1);
 
          // parse expression (required)
-         std::unique_ptr<AST::Expr> expr = parse_expr ();
-         if (expr == nullptr)
+         auto expr = parse_expr ();
+         if (!expr)
            {
              Error error (t->get_locus (),
                           "failed to parse struct expression field with "
                           "identifier and expression");
              add_error (std::move (error));
 
-             return nullptr;
+             return tl::unexpected (
+               Parse::Error::StructExprField::CHILD_ERROR);
            }
 
          return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
            new AST::StructExprFieldIdentifierValue (std::move (ident),
-                                                    std::move (expr),
+                                                    std::move (expr.value ()),
                                                     std::move (outer_attrs),
                                                     t->get_locus ()));
        }
@@ -1680,23 +1683,23 @@ Parser<ManagedTokenSource>::parse_struct_expr_field ()
        if (!skip_token (COLON))
          {
            // skip somewhere?
-           return nullptr;
+           return tl::unexpected (Parse::Error::StructExprField::MALFORMED);
          }
 
        // parse field expression (required)
-       std::unique_ptr<AST::Expr> expr = parse_expr ();
-       if (expr == nullptr)
+       auto expr = parse_expr ();
+       if (!expr)
          {
            Error error (t->get_locus (),
                         "failed to parse expr in struct (or enum) expr "
                         "field with tuple index");
            add_error (std::move (error));
 
-           return nullptr;
+           return tl::unexpected (Parse::Error::StructExprField::CHILD_ERROR);
          }
 
        return std::unique_ptr<AST::StructExprFieldIndexValue> (
-         new AST::StructExprFieldIndexValue (index, std::move (expr),
+         new AST::StructExprFieldIndexValue (index, std::move (expr.value ()),
                                              std::move (outer_attrs),
                                              t->get_locus ()));
       }
@@ -1704,7 +1707,7 @@ Parser<ManagedTokenSource>::parse_struct_expr_field ()
       /* this is a struct base and can't be parsed here, so just return
        * nothing without erroring */
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::StructExprField::STRUCT_BASE);
     default:
       add_error (
        Error (t->get_locus (),
@@ -1712,14 +1715,14 @@ Parser<ManagedTokenSource>::parse_struct_expr_field ()
               "expected identifier or integer literal",
               t->get_token_description ()));
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::StructExprField::MALFORMED);
     }
 }
 
 /* Pratt parser impl of parse_expr. FIXME: this is only provisional and
  * probably will be changed. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
                                        AST::AttrVec outer_attrs,
                                        ParseRestrictions restrictions)
@@ -1735,17 +1738,17 @@ Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
       TokenId id = current_token->get_id ();
       if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY
          || id == RIGHT_SQUARE || id == COMMA || id == LEFT_CURLY)
-       return nullptr;
+       return tl::unexpected (Parse::Error::Expr::NULL_EXPR);
     }
 
   ParseRestrictions null_denotation_restrictions = restrictions;
   null_denotation_restrictions.expr_can_be_stmt = false;
 
   // parse null denotation (unary part of expression)
-  std::unique_ptr<AST::Expr> expr
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
     = null_denotation ({}, null_denotation_restrictions);
-  if (expr == nullptr)
-    return nullptr;
+  if (!expr)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   return left_denotations (std::move (expr), right_binding_power,
                           std::move (outer_attrs), restrictions);
@@ -1753,7 +1756,7 @@ Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
 
 // Parse expression with lowest left binding power.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
                                        ParseRestrictions restrictions)
 {
@@ -1761,27 +1764,27 @@ Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
 }
 
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
-Parser<ManagedTokenSource>::left_denotations (std::unique_ptr<AST::Expr> expr,
-                                             int right_binding_power,
-                                             AST::AttrVec outer_attrs,
-                                             ParseRestrictions restrictions)
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
+Parser<ManagedTokenSource>::left_denotations (
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr,
+  int right_binding_power, AST::AttrVec outer_attrs,
+  ParseRestrictions restrictions)
 {
-  if (expr == nullptr)
+  if (!expr)
     {
       // DEBUG
       rust_debug ("null denotation is null; returning null for parse_expr");
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::NULL_DENOTATION);
     }
 
   const_TokenPtr current_token = lexer.peek_token ();
 
-  if (restrictions.expr_can_be_stmt && !expr->is_expr_without_block ()
+  if (restrictions.expr_can_be_stmt && !expr.value ()->is_expr_without_block ()
       && current_token->get_id () != DOT
       && current_token->get_id () != QUESTION_MARK)
     {
       rust_debug ("statement expression with block");
-      expr->set_outer_attrs (std::move (outer_attrs));
+      expr.value ()->set_outer_attrs (std::move (outer_attrs));
       return expr;
     }
 
@@ -1793,15 +1796,15 @@ Parser<ManagedTokenSource>::left_denotations (std::unique_ptr<AST::Expr> expr,
       lexer.skip_token ();
 
       // FIXME attributes should generally be applied to the null denotation.
-      expr = left_denotation (current_token, std::move (expr),
+      expr = left_denotation (current_token, std::move (expr.value ()),
                              std::move (outer_attrs), restrictions);
 
-      if (expr == nullptr)
+      if (!expr)
        {
          // DEBUG
          rust_debug ("left denotation is null; returning null for parse_expr");
 
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::LEFT_DENOTATION);
        }
 
       current_token = lexer.peek_token ();
@@ -1812,7 +1815,7 @@ Parser<ManagedTokenSource>::left_denotations (std::unique_ptr<AST::Expr> expr,
 
 /* Determines action to take when finding token at beginning of expression. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::null_denotation (AST::AttrVec outer_attrs,
                                             ParseRestrictions restrictions)
 {
@@ -1860,7 +1863,7 @@ Parser<ManagedTokenSource>::null_denotation (AST::AttrVec outer_attrs,
 
 // Handling of expresions that start with a path for `null_denotation`.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::null_denotation_path (
   AST::PathInExpression path, AST::AttrVec outer_attrs,
   ParseRestrictions restrictions)
@@ -1873,8 +1876,7 @@ Parser<ManagedTokenSource>::null_denotation_path (
     {
       // HACK: add outer attrs to path
       path.set_outer_attrs (std::move (outer_attrs));
-      return std::unique_ptr<AST::PathInExpression> (
-       new AST::PathInExpression (std::move (path)));
+      return std::make_unique<AST::PathInExpression> (std::move (path));
     }
 
   // branch on next token
@@ -1928,8 +1930,7 @@ Parser<ManagedTokenSource>::null_denotation_path (
          // assume path is returned
          // HACK: add outer attributes to path
          path.set_outer_attrs (std::move (outer_attrs));
-         return std::unique_ptr<AST::PathInExpression> (
-           new AST::PathInExpression (std::move (path)));
+         return std::make_unique<AST::PathInExpression> (std::move (path));
        }
       return parse_struct_expr_tuple_partial (std::move (path),
                                              std::move (outer_attrs));
@@ -1954,7 +1955,7 @@ Parser<ManagedTokenSource>::null_denotation_path (
 
 // Handling of expresions that do not start with a path for `null_denotation`.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::null_denotation_not_path (
   const_TokenPtr tok, AST::AttrVec outer_attrs, ParseRestrictions restrictions)
 {
@@ -1969,8 +1970,8 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
        AST::QualifiedPathInExpression path
          = parse_qualified_path_in_expression (tok->get_locus ());
        path.set_outer_attrs (std::move (outer_attrs));
-       return std::unique_ptr<AST::QualifiedPathInExpression> (
-         new AST::QualifiedPathInExpression (std::move (path)));
+       return std::make_unique<AST::QualifiedPathInExpression> (
+         std::move (path));
       }
     // FIXME: delegate to parse_literal_expr instead? would have to rejig
     // tokens and whatever.
@@ -2018,8 +2019,15 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
                              AST::Literal::BOOL, tok->get_type_hint (), {},
                              tok->get_locus ()));
     case LEFT_PAREN:
-      return parse_grouped_or_tuple_expr (std::move (outer_attrs),
-                                         tok->get_locus ());
+      {
+       auto grouped_or_tuple_expr
+         = parse_grouped_or_tuple_expr (std::move (outer_attrs),
+                                        tok->get_locus ());
+       if (grouped_or_tuple_expr)
+         return std::move (grouped_or_tuple_expr.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
 
     /*case PLUS: { // unary plus operator
        // invoke parse_expr recursively with appropriate priority, etc. for
@@ -2043,11 +2051,10 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
        entered_from_unary.entered_from_unary = true;
        if (!restrictions.can_be_struct_expr)
          entered_from_unary.can_be_struct_expr = false;
-       std::unique_ptr<AST::Expr> expr
-         = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
+       auto expr = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
 
-       if (expr == nullptr)
-         return nullptr;
+       if (!expr)
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        // can only apply to integer and float expressions
        /*if (expr.get_type() != integer_type_node || expr.get_type() !=
        float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
@@ -2061,9 +2068,10 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
 
        /* FIXME: allow outer attributes on these expressions by having an
         * outer attrs parameter in function*/
-       return std::unique_ptr<AST::NegationExpr> (
-         new AST::NegationExpr (std::move (expr), NegationOperator::NEGATE,
-                                std::move (outer_attrs), tok->get_locus ()));
+       return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
+                                                   NegationOperator::NEGATE,
+                                                   std::move (outer_attrs),
+                                                   tok->get_locus ());
       }
     case EXCLAM:
       { // logical or bitwise not
@@ -2071,11 +2079,10 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
        entered_from_unary.entered_from_unary = true;
        if (!restrictions.can_be_struct_expr)
          entered_from_unary.can_be_struct_expr = false;
-       std::unique_ptr<AST::Expr> expr
-         = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
+       auto expr = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
 
-       if (expr == nullptr)
-         return nullptr;
+       if (!expr)
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        // can only apply to boolean expressions
        /*if (expr.get_type() != boolean_type_node) {
            rust_error_at(tok->get_locus(),
@@ -2087,9 +2094,10 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
         * analysis */
 
        // FIXME: allow outer attributes on these expressions
-       return std::unique_ptr<AST::NegationExpr> (
-         new AST::NegationExpr (std::move (expr), NegationOperator::NOT,
-                                std::move (outer_attrs), tok->get_locus ()));
+       return std::make_unique<AST::NegationExpr> (std::move (expr.value ()),
+                                                   NegationOperator::NOT,
+                                                   std::move (outer_attrs),
+                                                   tok->get_locus ());
       }
     case ASTERISK:
       {
@@ -2098,17 +2106,20 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
        ParseRestrictions entered_from_unary;
        entered_from_unary.entered_from_unary = true;
        entered_from_unary.can_be_struct_expr = false;
-       std::unique_ptr<AST::Expr> expr
-         = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
+       auto expr = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
+       if (!expr)
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        // FIXME: allow outer attributes on expression
-       return std::unique_ptr<AST::DereferenceExpr> (
-         new AST::DereferenceExpr (std::move (expr), std::move (outer_attrs),
-                                   tok->get_locus ()));
+       return std::make_unique<AST::DereferenceExpr> (std::move (
+                                                        expr.value ()),
+                                                      std::move (outer_attrs),
+                                                      tok->get_locus ());
       }
     case AMP:
       {
        // (single) "borrow" expression - shared (mutable) or immutable
-       std::unique_ptr<AST::Expr> expr = nullptr;
+       tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr
+         = tl::unexpected (Parse::Error::Expr::MALFORMED);
        Mutability mutability = Mutability::Imm;
        bool raw_borrow = false;
 
@@ -2142,26 +2153,42 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
                               "raw borrow should be either const or mut");
              }
            lexer.skip_token ();
-           expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           auto expr_result
+             = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           if (expr_result)
+             expr = std::move (expr_result.value ());
+           else
+             return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
            raw_borrow = true;
          }
        else if (t->get_id () == MUT)
          {
            lexer.skip_token ();
-           expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           auto expr_result
+             = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           if (expr_result)
+             expr = std::move (expr_result.value ());
+           else
+             return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
            mutability = Mutability::Mut;
            raw_borrow = false;
          }
        else
          {
-           expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+           auto expr_result
+             = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+           if (expr_result)
+             expr = std::move (expr_result.value ());
+           else
+             return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
            raw_borrow = false;
          }
 
        // FIXME: allow outer attributes on expression
-       return std::unique_ptr<AST::BorrowExpr> (
-         new AST::BorrowExpr (std::move (expr), mutability, raw_borrow, false,
-                              std::move (outer_attrs), tok->get_locus ()));
+       return std::make_unique<AST::BorrowExpr> (std::move (expr.value ()),
+                                                 mutability, raw_borrow, false,
+                                                 std::move (outer_attrs),
+                                                 tok->get_locus ());
       }
     case LOGICAL_AND:
       {
@@ -2175,113 +2202,224 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
        if (lexer.peek_token ()->get_id () == MUT)
          {
            lexer.skip_token ();
-           expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           auto expr_res
+             = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+           if (!expr_res)
+             return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+           expr = std::move (expr_res.value ());
            mutability = Mutability::Mut;
          }
        else
          {
-           expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+           auto expr_result
+             = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+           if (expr_result)
+             expr = std::move (expr_result.value ());
+           else
+             return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
            mutability = Mutability::Imm;
          }
 
        // FIXME: allow outer attributes on expression
-       return std::unique_ptr<AST::BorrowExpr> (
-         new AST::BorrowExpr (std::move (expr), mutability, false, true,
-                              std::move (outer_attrs), tok->get_locus ()));
+       return std::make_unique<AST::BorrowExpr> (std::move (expr), mutability,
+                                                 false, true,
+                                                 std::move (outer_attrs),
+                                                 tok->get_locus ());
       }
     case OR:
     case PIPE:
     case MOVE:
       // closure expression
-      return parse_closure_expr_pratt (tok, std::move (outer_attrs));
+      {
+       auto ret = parse_closure_expr_pratt (tok, std::move (outer_attrs));
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case DOT_DOT:
       // either "range to" or "range full" expressions
-      return parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
+      {
+       auto ret
+         = parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case DOT_DOT_EQ:
       // range to inclusive expr
-      return parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
+      {
+       auto ret = parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case RETURN_KW:
       // FIXME: is this really a null denotation expression?
-      return parse_return_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret
+         = parse_return_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case TRY:
       // FIXME: is this really a null denotation expression?
-      return parse_try_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret = parse_try_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case BREAK:
       // FIXME: is this really a null denotation expression?
-      return parse_break_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret
+         = parse_break_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case CONTINUE:
       return parse_continue_expr (std::move (outer_attrs), tok->get_locus ());
     case LEFT_CURLY:
       // ok - this is an expression with block for once.
       {
-       auto block = parse_block_expr (std::move (outer_attrs), tl::nullopt,
-                                      tok->get_locus ());
-       if (block)
-         return std::move (block.value ());
+       auto ret = parse_block_expr (std::move (outer_attrs), tl::nullopt,
+                                    tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
        else
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
       }
     case IF:
       // if or if let, so more lookahead to find out
       if (lexer.peek_token ()->get_id () == LET)
        {
          // if let expr
-         return parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
+         auto ret
+           = parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
+         if (ret)
+           return std::move (ret.value ());
+         else
+           return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
       else
        {
          // if expr
-         return parse_if_expr (std::move (outer_attrs), tok->get_locus ());
+         auto ret = parse_if_expr (std::move (outer_attrs), tok->get_locus ());
+         if (ret)
+           return std::move (ret.value ());
+         else
+           return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
     case LIFETIME:
-      return parse_labelled_loop_expr (tok, std::move (outer_attrs));
+      {
+       auto ret = parse_labelled_loop_expr (tok, std::move (outer_attrs));
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case LOOP:
-      return parse_loop_expr (std::move (outer_attrs), tl::nullopt,
-                             tok->get_locus ());
+      {
+       auto ret = parse_loop_expr (std::move (outer_attrs), tl::nullopt,
+                                   tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case WHILE:
       if (lexer.peek_token ()->get_id () == LET)
        {
-         return parse_while_let_loop_expr (std::move (outer_attrs));
+         auto ret = parse_while_let_loop_expr (std::move (outer_attrs));
+         if (ret)
+           return std::move (ret.value ());
+         else
+           return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
       else
        {
-         return parse_while_loop_expr (std::move (outer_attrs), tl::nullopt,
-                                       tok->get_locus ());
+         auto ret = parse_while_loop_expr (std::move (outer_attrs),
+                                           tl::nullopt, tok->get_locus ());
+         if (ret)
+           return std::move (ret.value ());
+         else
+           return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
     case FOR:
-      return parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
+      {
+       auto ret = parse_for_loop_expr (std::move (outer_attrs), tl::nullopt);
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case MATCH_KW:
       // also an expression with block
-      return parse_match_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret
+         = parse_match_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
+
     case LEFT_SQUARE:
       // array definition expr (not indexing)
-      return parse_array_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret
+         = parse_array_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case UNSAFE:
-      return parse_unsafe_block_expr (std::move (outer_attrs),
-                                     tok->get_locus ());
+      {
+       auto ret = parse_unsafe_block_expr (std::move (outer_attrs),
+                                           tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case BOX:
-      return parse_box_expr (std::move (outer_attrs), tok->get_locus ());
+      {
+       auto ret = parse_box_expr (std::move (outer_attrs), tok->get_locus ());
+       if (ret)
+         return std::move (ret.value ());
+       else
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
+      }
     case UNDERSCORE:
       add_error (
        Error (tok->get_locus (),
               "use of %qs is not allowed on the right-side of an assignment",
               tok->get_token_description ()));
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     case CONST:
       {
-       auto const_block
+       auto ret
          = parse_const_block_expr (std::move (outer_attrs), tok->get_locus ());
-       if (const_block)
-         return std::move (const_block.value ());
+       if (ret)
+         return std::move (ret.value ());
        else
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
       }
     default:
       if (!restrictions.expr_can_be_null)
        add_error (Error (tok->get_locus (),
                          "found unexpected token %qs in null denotation",
                          tok->get_token_description ()));
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 }
 
@@ -2289,7 +2427,7 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
  * operators or other punctuation. Returns a function pointer to member
  * function that implements the left denotation for the token given. */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
                                             std::unique_ptr<AST::Expr> left,
                                             AST::AttrVec outer_attrs,
@@ -2303,9 +2441,8 @@ Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
       {
        location_t left_locus = left->get_locus ();
        // error propagation expression - unary postfix
-       return std::unique_ptr<AST::ErrorPropagationExpr> (
-         new AST::ErrorPropagationExpr (std::move (left),
-                                        std::move (outer_attrs), left_locus));
+       return std::make_unique<AST::ErrorPropagationExpr> (
+         std::move (left), std::move (outer_attrs), left_locus);
       }
     case PLUS:
       // sum expression - binary infix
@@ -2553,7 +2690,7 @@ Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
               "found scope resolution operator in left denotation "
               "function - this should probably be handled elsewhere"));
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     case DOT:
       {
        /* field expression or method call - relies on parentheses after next
@@ -2634,7 +2771,7 @@ Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
                        "found unexpected token %qs in left denotation",
                        tok->get_token_description ()));
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 }
 
@@ -2676,247 +2813,226 @@ get_lbp_for_arithmetic_or_logical_expr (
 
 // Parses an arithmetic or logical expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
   AST::ArithmeticOrLogicalExpr::ExprType expr_type,
   ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
-                 AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
+                          AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     expr_type, locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()), expr_type, locus);
 }
 
 // Parses a binary addition expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_plus_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::ADD, locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::ADD, locus);
 }
 
 // Parses a binary subtraction expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_minus_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::SUBTRACT,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::SUBTRACT, locus);
 }
 
 // Parses a binary multiplication expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_mult_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::MULTIPLY,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::MULTIPLY, locus);
 }
 
 // Parses a binary division expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_div_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::DIVIDE,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::DIVIDE, locus);
 }
 
 // Parses a binary modulo expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_mod_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::MODULUS,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::MODULUS, locus);
 }
 
 /* Parses a binary bitwise (or eager logical) and expression (with Pratt
  * parsing). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_bitwise_and_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::BITWISE_AND,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::BITWISE_AND, locus);
 }
 
 /* Parses a binary bitwise (or eager logical) or expression (with Pratt
  * parsing). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_bitwise_or_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::BITWISE_OR,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::BITWISE_OR, locus);
 }
 
 /* Parses a binary bitwise (or eager logical) xor expression (with Pratt
  * parsing). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_bitwise_xor_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::BITWISE_XOR,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::BITWISE_XOR, locus);
 }
 
 // Parses a binary left shift expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_left_shift_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::LEFT_SHIFT,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::LEFT_SHIFT, locus);
 }
 
 // Parses a binary right shift expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_right_shift_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
-    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
-                                     ArithmeticOrLogicalOperator::RIGHT_SHIFT,
-                                     locus));
+  return std::make_unique<AST::ArithmeticOrLogicalExpr> (
+    std::move (left), std::move (right.value ()),
+    ArithmeticOrLogicalOperator::RIGHT_SHIFT, locus);
 }
 
 /* Returns the left binding power for the given ComparisonExpr type.
@@ -2950,235 +3066,227 @@ get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type)
  * specify one and have the other looked up - e.g. specify ExprType and
  * binding power is looked up? */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_comparison_expr (
   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
   AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (get_lbp_for_comparison_expr (expr_type), AST::AttrVec (),
-                 restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (get_lbp_for_comparison_expr (expr_type),
+                          AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right), expr_type,
-                            locus));
+  return std::make_unique<AST::ComparisonExpr> (std::move (left),
+                                               std::move (right.value ()),
+                                               expr_type, locus);
 }
 
 // Parses a binary equal to expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_equal_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::EQUAL, locus));
+  return std::make_unique<AST::ComparisonExpr> (std::move (left),
+                                               std::move (right.value ()),
+                                               ComparisonOperator::EQUAL,
+                                               locus);
 }
 
 // Parses a binary not equal to expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_not_equal_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::NOT_EQUAL, locus));
+  return std::make_unique<AST::ComparisonExpr> (std::move (left),
+                                               std::move (right.value ()),
+                                               ComparisonOperator::NOT_EQUAL,
+                                               locus);
 }
 
 // Parses a binary greater than expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_greater_than_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::GREATER_THAN, locus));
+  return std::make_unique<AST::ComparisonExpr> (
+    std::move (left), std::move (right.value ()),
+    ComparisonOperator::GREATER_THAN, locus);
 }
 
 // Parses a binary less than expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_less_than_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::LESS_THAN, locus));
+  return std::make_unique<AST::ComparisonExpr> (std::move (left),
+                                               std::move (right.value ()),
+                                               ComparisonOperator::LESS_THAN,
+                                               locus);
 }
 
 // Parses a binary greater than or equal to expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_greater_equal_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::GREATER_OR_EQUAL, locus));
+  return std::make_unique<AST::ComparisonExpr> (
+    std::move (left), std::move (right.value ()),
+    ComparisonOperator::GREATER_OR_EQUAL, locus);
 }
 
 // Parses a binary less than or equal to expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ComparisonExpr>
+tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_binary_less_equal_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::ComparisonExpr> (
-    new AST::ComparisonExpr (std::move (left), std::move (right),
-                            ComparisonOperator::LESS_OR_EQUAL, locus));
+  return std::make_unique<AST::ComparisonExpr> (
+    std::move (left), std::move (right.value ()),
+    ComparisonOperator::LESS_OR_EQUAL, locus);
 }
 
 // Parses a binary lazy boolean or expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::LazyBooleanExpr>
+tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_lazy_or_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::LazyBooleanExpr> (
-    new AST::LazyBooleanExpr (std::move (left), std::move (right),
-                             LazyBooleanOperator::LOGICAL_OR, locus));
+  return std::make_unique<AST::LazyBooleanExpr> (
+    std::move (left), std::move (right.value ()),
+    LazyBooleanOperator::LOGICAL_OR, locus);
 }
 
 // Parses a binary lazy boolean and expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::LazyBooleanExpr>
+tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_lazy_and_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::LazyBooleanExpr> (
-    new AST::LazyBooleanExpr (std::move (left), std::move (right),
-                             LazyBooleanOperator::LOGICAL_AND, locus));
+  return std::make_unique<AST::LazyBooleanExpr> (
+    std::move (left), std::move (right.value ()),
+    LazyBooleanOperator::LOGICAL_AND, locus);
 }
 
 // Parses a pseudo-binary infix type cast expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::TypeCastExpr>
+tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_type_cast_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED,
   ParseRestrictions restrictions ATTRIBUTE_UNUSED)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
-  if (type == nullptr)
-    return nullptr;
+  auto type = parse_type_no_bounds ();
+  if (!type)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: how do I get precedence put in here?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = expr_to_cast->get_locus ();
 
-  return std::unique_ptr<AST::TypeCastExpr> (
-    new AST::TypeCastExpr (std::move (expr_to_cast), std::move (type), locus));
+  return std::make_unique<AST::TypeCastExpr> (std::move (expr_to_cast),
+                                             std::move (type), locus);
 }
 
 // Parses a binary assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::AssignmentExpr>
+tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
-  // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::AssignmentExpr> (
-    new AST::AssignmentExpr (std::move (left), std::move (right),
-                            std::move (outer_attrs), locus));
+  return std::make_unique<AST::AssignmentExpr> (std::move (left),
+                                               std::move (right.value ()),
+                                               std::move (outer_attrs), locus);
 }
 
 /* Returns the left binding power for the given CompoundAssignmentExpr type.
@@ -3219,260 +3327,241 @@ get_lbp_for_compound_assignment_expr (
 
 // Parses a compound assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_compound_assignment_expr (
   const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
   AST::CompoundAssignmentExpr::ExprType expr_type,
   ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
-                 AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
+                          AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    expr_type, locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()), expr_type, locus);
 }
 
 // Parses a binary add-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_plus_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::ADD, locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::ADD, locus);
 }
 
 // Parses a binary minus-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_minus_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::SUBTRACT,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::SUBTRACT, locus);
 }
 
 // Parses a binary multiplication-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_mult_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::MULTIPLY,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::MULTIPLY, locus);
 }
 
 // Parses a binary division-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_div_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::DIVIDE,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::DIVIDE, locus);
 }
 
 // Parses a binary modulo-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_mod_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::MODULUS,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::MODULUS, locus);
 }
 
 // Parses a binary and-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_and_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::BITWISE_AND,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::BITWISE_AND, locus);
 }
 
 // Parses a binary or-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_or_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::BITWISE_OR,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::BITWISE_OR, locus);
 }
 
 // Parses a binary xor-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_xor_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::BITWISE_XOR,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::BITWISE_XOR, locus);
 }
 
 // Parses a binary left shift-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_left_shift_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
+  auto right
     = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::LEFT_SHIFT,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::LEFT_SHIFT, locus);
 }
 
 // Parses a binary right shift-assignment expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CompoundAssignmentExpr>
+tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_right_shift_assig_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
+  auto right
     = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::CompoundAssignmentExpr> (
-    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
-                                    CompoundAssignmentOperator::RIGHT_SHIFT,
-                                    locus));
+  return std::make_unique<AST::CompoundAssignmentExpr> (
+    std::move (left), std::move (right.value ()),
+    CompoundAssignmentOperator::RIGHT_SHIFT, locus);
 }
 
 // Parses a postfix unary await expression (with Pratt parsing).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::AwaitExpr>
+tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_await_expr (
   const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await,
   AST::AttrVec outer_attrs)
@@ -3487,7 +3576,7 @@ Parser<ManagedTokenSource>::parse_await_expr (
       add_error (std::move (error));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // TODO: check inside async block in semantic analysis
@@ -3501,7 +3590,7 @@ Parser<ManagedTokenSource>::parse_await_expr (
 /* Parses an exclusive range ('..') in left denotation position (i.e.
  * RangeFromExpr or RangeFromToExpr). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeExpr>
+tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
@@ -3511,21 +3600,20 @@ Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
   // Can be nullptr, in which case it is a RangeFromExpr, otherwise a
   // RangeFromToExpr.
   restrictions.expr_can_be_null = true;
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+  auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
 
   location_t locus = left->get_locus ();
 
-  if (right == nullptr)
+  if (!right)
     {
       // range from expr
-      return std::unique_ptr<AST::RangeFromExpr> (
-       new AST::RangeFromExpr (std::move (left), locus));
+      return std::make_unique<AST::RangeFromExpr> (std::move (left), locus);
     }
   else
     {
-      return std::unique_ptr<AST::RangeFromToExpr> (
-       new AST::RangeFromToExpr (std::move (left), std::move (right), locus));
+      return std::make_unique<AST::RangeFromToExpr> (std::move (left),
+                                                    std::move (right.value ()),
+                                                    locus);
     }
   // FIXME: make non-associative
 }
@@ -3533,7 +3621,7 @@ Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
 /* Parses an exclusive range ('..') in null denotation position (i.e.
  * RangeToExpr or RangeFullExpr). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeExpr>
+tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
 {
@@ -3542,67 +3630,65 @@ Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
 
   // FIXME: this probably parses expressions accidently or whatever
   // try parsing RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+  auto right = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
 
   location_t locus = tok->get_locus ();
 
-  if (right == nullptr)
+  if (!right)
     {
       // range from expr
-      return std::unique_ptr<AST::RangeFullExpr> (
-       new AST::RangeFullExpr (locus));
+      return std::make_unique<AST::RangeFullExpr> (locus);
     }
   else
     {
-      return std::unique_ptr<AST::RangeToExpr> (
-       new AST::RangeToExpr (std::move (right), locus));
+      return std::make_unique<AST::RangeToExpr> (std::move (right.value ()),
+                                                locus);
     }
   // FIXME: make non-associative
 }
 
 // Parses a full binary range inclusive expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeFromToInclExpr>
+tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_range_inclusive_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
   AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right
-    = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: make non-associative
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = left->get_locus ();
 
-  return std::unique_ptr<AST::RangeFromToInclExpr> (
-    new AST::RangeFromToInclExpr (std::move (left), std::move (right), locus));
+  return std::make_unique<AST::RangeFromToInclExpr> (std::move (left),
+                                                    std::move (right.value ()),
+                                                    locus);
 }
 
 // Parses an inclusive range-to prefix unary expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::RangeToInclExpr>
+tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_range_to_inclusive_expr (
   const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
 {
   // parse RHS (as tok has already been consumed in parse_expression)
-  std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT_EQ);
-  if (right == nullptr)
-    return nullptr;
+  auto right = parse_expr (LBP_DOT_DOT_EQ);
+  if (!right)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
   // FIXME: make non-associative
 
   // TODO: check types. actually, do so during semantic analysis
 
-  return std::unique_ptr<AST::RangeToInclExpr> (
-    new AST::RangeToInclExpr (std::move (right), tok->get_locus ()));
+  return std::make_unique<AST::RangeToInclExpr> (std::move (right.value ()),
+                                                tok->get_locus ());
 }
 
 // Parses a pseudo-binary infix tuple index expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::TupleIndexExpr>
+tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_tuple_index_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr,
   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
@@ -3610,9 +3696,8 @@ Parser<ManagedTokenSource>::parse_tuple_index_expr (
   // parse int literal (as token already skipped)
   const_TokenPtr index_tok = expect_token (INT_LITERAL);
   if (index_tok == nullptr)
-    {
-      return nullptr;
-    }
+    return tl::unexpected (Parse::Error::Expr::MALFORMED);
+
   std::string index = index_tok->get_str ();
 
   // convert to integer
@@ -3626,14 +3711,14 @@ Parser<ManagedTokenSource>::parse_tuple_index_expr (
 
   location_t locus = tuple_expr->get_locus ();
 
-  return std::unique_ptr<AST::TupleIndexExpr> (
-    new AST::TupleIndexExpr (std::move (tuple_expr), index_int,
-                            std::move (outer_attrs), locus));
+  return std::make_unique<AST::TupleIndexExpr> (std::move (tuple_expr),
+                                               index_int,
+                                               std::move (outer_attrs), locus);
 }
 
 // Parses a pseudo-binary infix array (or slice) index expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ArrayIndexExpr>
+tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_index_expr (
   const_TokenPtr, std::unique_ptr<AST::Expr> array_expr,
   AST::AttrVec outer_attrs, ParseRestrictions)
@@ -3643,28 +3728,28 @@ Parser<ManagedTokenSource>::parse_index_expr (
     = parse_expr (LBP_ARRAY_REF, AST::AttrVec (),
     restrictions);*/
   // TODO: conceptually, should treat [] as brackets, so just parse all expr
-  std::unique_ptr<AST::Expr> index_expr = parse_expr ();
-  if (index_expr == nullptr)
-    return nullptr;
+  auto index_expr = parse_expr ();
+  if (!index_expr)
+    return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
 
   // skip ']' at end of array
   if (!skip_token (RIGHT_SQUARE))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = array_expr->get_locus ();
 
-  return std::unique_ptr<AST::ArrayIndexExpr> (
-    new AST::ArrayIndexExpr (std::move (array_expr), std::move (index_expr),
-                            std::move (outer_attrs), locus));
+  return std::make_unique<AST::ArrayIndexExpr> (std::move (array_expr),
+                                               std::move (index_expr.value ()),
+                                               std::move (outer_attrs), locus);
 }
 
 // Parses a pseudo-binary infix struct field access expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::FieldAccessExpr>
+tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_field_access_expr (
   const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr,
   AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
@@ -3673,21 +3758,22 @@ Parser<ManagedTokenSource>::parse_field_access_expr (
    * not await, for instance) */
   const_TokenPtr ident_tok = expect_token (IDENTIFIER);
   if (ident_tok == nullptr)
-    return nullptr;
+    return tl::unexpected (Parse::Error::Expr::MALFORMED);
 
   Identifier ident{ident_tok};
 
   location_t locus = struct_expr->get_locus ();
 
   // TODO: check types. actually, do so during semantic analysis
-  return std::unique_ptr<AST::FieldAccessExpr> (
-    new AST::FieldAccessExpr (std::move (struct_expr), std::move (ident),
-                             std::move (outer_attrs), locus));
+  return std::make_unique<AST::FieldAccessExpr> (std::move (struct_expr),
+                                                std::move (ident),
+                                                std::move (outer_attrs),
+                                                locus);
 }
 
 // Parses a pseudo-binary infix method call expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::MethodCallExpr>
+tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_method_call_expr (
   const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
   AST::AttrVec outer_attrs, ParseRestrictions)
@@ -3700,13 +3786,13 @@ Parser<ManagedTokenSource>::parse_method_call_expr (
                   "failed to parse path expr segment of method call expr");
       add_error (std::move (error));
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
     }
 
   // skip left parentheses
   if (!skip_token (LEFT_PAREN))
     {
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // parse method params (if they exist)
@@ -3715,16 +3801,16 @@ Parser<ManagedTokenSource>::parse_method_call_expr (
   const_TokenPtr t = lexer.peek_token ();
   while (t->get_id () != RIGHT_PAREN)
     {
-      std::unique_ptr<AST::Expr> param = parse_expr ();
-      if (param == nullptr)
+      auto param = parse_expr ();
+      if (!param)
        {
          Error error (t->get_locus (),
                       "failed to parse method param in method call");
          add_error (std::move (error));
 
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
-      params.push_back (std::move (param));
+      params.push_back (std::move (param.value ()));
 
       if (lexer.peek_token ()->get_id () != COMMA)
        break;
@@ -3736,21 +3822,21 @@ Parser<ManagedTokenSource>::parse_method_call_expr (
   // skip right paren
   if (!skip_token (RIGHT_PAREN))
     {
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // TODO: check types. actually do so in semantic analysis pass.
   location_t locus = receiver_expr->get_locus ();
 
-  return std::unique_ptr<AST::MethodCallExpr> (
-    new AST::MethodCallExpr (std::move (receiver_expr), std::move (segment),
-                            std::move (params), std::move (outer_attrs),
-                            locus));
+  return std::make_unique<AST::MethodCallExpr> (std::move (receiver_expr),
+                                               std::move (segment),
+                                               std::move (params),
+                                               std::move (outer_attrs), locus);
 }
 
 // Parses a pseudo-binary infix function call expression.
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CallExpr>
+tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_function_call_expr (
   const_TokenPtr, std::unique_ptr<AST::Expr> function_expr,
   AST::AttrVec outer_attrs, ParseRestrictions)
@@ -3761,16 +3847,16 @@ Parser<ManagedTokenSource>::parse_function_call_expr (
   const_TokenPtr t = lexer.peek_token ();
   while (t->get_id () != RIGHT_PAREN)
     {
-      std::unique_ptr<AST::Expr> param = parse_expr ();
-      if (param == nullptr)
+      auto param = parse_expr ();
+      if (!param)
        {
          Error error (t->get_locus (),
                       "failed to parse function param in function call");
          add_error (std::move (error));
 
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
-      params.push_back (std::move (param));
+      params.push_back (std::move (param.value ()));
 
       if (lexer.peek_token ()->get_id () != COMMA)
        break;
@@ -3783,22 +3869,22 @@ Parser<ManagedTokenSource>::parse_function_call_expr (
   if (!skip_token (RIGHT_PAREN))
     {
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // TODO: check types. actually, do so during semantic analysis
   location_t locus = function_expr->get_locus ();
 
-  return std::unique_ptr<AST::CallExpr> (
-    new AST::CallExpr (std::move (function_expr), std::move (params),
-                      std::move (outer_attrs), locus));
+  return std::make_unique<AST::CallExpr> (std::move (function_expr),
+                                         std::move (params),
+                                         std::move (outer_attrs), locus);
 }
 
 /* Parses a struct expr struct with a path in expression already parsed (but
  * not
  * '{' token). */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::StructExprStruct>
+tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
   AST::PathInExpression path, AST::AttrVec outer_attrs)
 {
@@ -3806,7 +3892,7 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
   // lookup) again, make statement if final ';'
   if (!skip_token (LEFT_CURLY))
     {
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // parse inner attributes
@@ -3821,9 +3907,10 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
       // struct with no body
       lexer.skip_token ();
 
-      return std::unique_ptr<AST::StructExprStruct> (
-       new AST::StructExprStruct (std::move (path), std::move (inner_attrs),
-                                  std::move (outer_attrs), path_locus));
+      return std::make_unique<AST::StructExprStruct> (std::move (path),
+                                                     std::move (inner_attrs),
+                                                     std::move (outer_attrs),
+                                                     path_locus);
     case DOT_DOT:
       /* technically this would give a struct base-only struct, but this
        * algorithm should work too. As such, AST type not happening. */
@@ -3838,21 +3925,21 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
 
        while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
          {
-           std::unique_ptr<AST::StructExprField> field
-             = parse_struct_expr_field ();
-           if (field == nullptr)
+           auto field = parse_struct_expr_field ();
+           if (!field
+               && field.error () != Parse::Error::StructExprField::STRUCT_BASE)
              {
                Error error (t->get_locus (),
                             "failed to parse struct (or enum) expr field");
                add_error (std::move (error));
 
-               return nullptr;
+               return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
              }
 
            // DEBUG:
            rust_debug ("struct/enum expr field validated to not be null");
 
-           fields.push_back (std::move (field));
+           fields.push_back (std::move (field.value ()));
 
            // DEBUG:
            rust_debug ("struct/enum expr field pushed back");
@@ -3883,22 +3970,22 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
            lexer.skip_token ();
 
            // parse required struct base expr
-           std::unique_ptr<AST::Expr> base_expr = parse_expr ();
-           if (base_expr == nullptr)
+           auto base_expr = parse_expr ();
+           if (!base_expr)
              {
                Error error (lexer.peek_token ()->get_locus (),
                             "failed to parse struct base expression in struct "
                             "expression");
                add_error (std::move (error));
 
-               return nullptr;
+               return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
              }
 
            // DEBUG:
            rust_debug ("struct/enum expr - parsed and validated base expr");
 
-           struct_base
-             = AST::StructBase (std::move (base_expr), dot_dot_location);
+           struct_base = AST::StructBase (std::move (base_expr.value ()),
+                                          dot_dot_location);
 
            // DEBUG:
            rust_debug ("assigned struct base to new struct base ");
@@ -3906,18 +3993,17 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
 
        if (!skip_token (RIGHT_CURLY))
          {
-           return nullptr;
+           return tl::unexpected (Parse::Error::Expr::MALFORMED);
          }
 
        // DEBUG:
        rust_debug (
          "struct/enum expr skipped right curly - done and ready to return");
 
-       return std::unique_ptr<AST::StructExprStructFields> (
-         new AST::StructExprStructFields (std::move (path), std::move (fields),
-                                          path_locus, std::move (struct_base),
-                                          std::move (inner_attrs),
-                                          std::move (outer_attrs)));
+       return std::make_unique<AST::StructExprStructFields> (
+         std::move (path), std::move (fields), path_locus,
+         std::move (struct_base), std::move (inner_attrs),
+         std::move (outer_attrs));
       }
     default:
       add_error (
@@ -3926,7 +4012,7 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
               "expected %<}%>, identifier, integer literal, or %<..%>",
               t->get_token_description ()));
 
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 }
 
@@ -3937,13 +4023,13 @@ Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
  * A better solution would be to just get this to call that function directly.
  * */
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::CallExpr>
+tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
   AST::PathInExpression path, AST::AttrVec outer_attrs)
 {
   if (!skip_token (LEFT_PAREN))
     {
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   AST::AttrVec inner_attrs = parse_inner_attributes ();
@@ -3954,16 +4040,16 @@ Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
   while (t->get_id () != RIGHT_PAREN)
     {
       // parse expression (required)
-      std::unique_ptr<AST::Expr> expr = parse_expr ();
-      if (expr == nullptr)
+      auto expr = parse_expr ();
+      if (!expr)
        {
          Error error (t->get_locus (), "failed to parse expression in "
                                        "struct (or enum) expression tuple");
          add_error (std::move (error));
 
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
-      exprs.push_back (std::move (expr));
+      exprs.push_back (std::move (expr.value ()));
 
       if (lexer.peek_token ()->get_id () != COMMA)
        break;
@@ -3975,22 +4061,21 @@ Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
 
   if (!skip_token (RIGHT_PAREN))
     {
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   location_t path_locus = path.get_locus ();
 
-  auto pathExpr = std::unique_ptr<AST::PathInExpression> (
-    new AST::PathInExpression (std::move (path)));
+  auto pathExpr = std::make_unique<AST::PathInExpression> (std::move (path));
 
-  return std::unique_ptr<AST::CallExpr> (
-    new AST::CallExpr (std::move (pathExpr), std::move (exprs),
-                      std::move (outer_attrs), path_locus));
+  return std::make_unique<AST::CallExpr> (std::move (pathExpr),
+                                         std::move (exprs),
+                                         std::move (outer_attrs), path_locus);
 }
 
 // Parses a closure expression with pratt parsing (from null denotation).
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::ClosureExpr>
+tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr>
 Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
                                                      AST::AttrVec outer_attrs)
 {
@@ -4028,7 +4113,7 @@ Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
                Error error (t->get_locus (), "could not parse closure param");
                add_error (std::move (error));
 
-               return nullptr;
+               return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
              }
            params.push_back (std::move (param));
 
@@ -4050,7 +4135,7 @@ Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
 
        if (!skip_token (PIPE))
          {
-           return nullptr;
+           return tl::unexpected (Parse::Error::Expr::MALFORMED);
          }
        break;
       }
@@ -4061,7 +4146,7 @@ Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
                        tok->get_token_description ()));
 
       // skip somewhere?
-      return nullptr;
+      return tl::unexpected (Parse::Error::Expr::MALFORMED);
     }
 
   // again branch based on next token
@@ -4074,15 +4159,15 @@ Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
       lexer.skip_token ();
 
       // parse actual type, which is required
-      std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
-      if (type == nullptr)
+      auto type = parse_type_no_bounds ();
+      if (!type)
        {
          // error
          Error error (tok->get_locus (), "failed to parse type for closure");
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
 
       // parse block expr, which is required
@@ -4095,34 +4180,33 @@ Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
 
-      return std::unique_ptr<AST::ClosureExprInnerTyped> (
-       new AST::ClosureExprInnerTyped (std::move (type),
-                                       std::move (block.value ()),
-                                       std::move (params), locus, has_move,
-                                       std::move (outer_attrs)));
+      return std::make_unique<AST::ClosureExprInnerTyped> (
+       std::move (type), std::move (block.value ()), std::move (params), locus,
+       has_move, std::move (outer_attrs));
     }
   else
     {
       // must be expr-only closure
 
       // parse expr, which is required
-      std::unique_ptr<AST::Expr> expr = parse_expr ();
-      if (expr == nullptr)
+      auto expr = parse_expr ();
+      if (!expr)
        {
          Error error (tok->get_locus (),
                       "failed to parse expression in closure");
          add_error (std::move (error));
 
          // skip somewhere?
-         return nullptr;
+         return tl::unexpected (Parse::Error::Expr::CHILD_ERROR);
        }
 
-      return std::unique_ptr<AST::ClosureExprInner> (
-       new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
-                                  has_move, std::move (outer_attrs)));
+      return std::make_unique<AST::ClosureExprInner> (std::move (expr.value ()),
+                                                     std::move (params), locus,
+                                                     has_move,
+                                                     std::move (outer_attrs));
     }
 }
 
index 1be2a5c2897ce862f3fed269d70f975f8535c9b3..9f4440ee32968402611d78112a61c452497333e0 100644 (file)
@@ -3497,11 +3497,13 @@ Parser<ManagedTokenSource>::parse_enum_item ()
        // discriminant enum item
        lexer.skip_token ();
 
-       std::unique_ptr<AST::Expr> discriminant_expr = parse_expr ();
+       auto discriminant_expr = parse_expr ();
+       if (!discriminant_expr)
+         return nullptr;
 
        return std::unique_ptr<AST::EnumItemDiscriminant> (
          new AST::EnumItemDiscriminant (std::move (item_name), std::move (vis),
-                                        std::move (discriminant_expr),
+                                        std::move (discriminant_expr.value ()),
                                         std::move (outer_attrs),
                                         item_name_tok->get_locus ()));
       }
@@ -3627,7 +3629,9 @@ Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
     }
 
   // parse constant expression (required)
-  std::unique_ptr<AST::Expr> expr = parse_expr ();
+  auto expr = parse_expr ();
+  if (!expr)
+    return nullptr;
 
   if (!skip_token (SEMICOLON))
     {
@@ -3637,7 +3641,8 @@ Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
 
   return std::unique_ptr<AST::ConstantItem> (
     new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type),
-                          std::move (expr), std::move (outer_attrs), locus));
+                          std::move (expr.value ()), std::move (outer_attrs),
+                          locus));
 }
 
 // Parses a "static item" (static storage item, with 'static lifetime).
@@ -3679,7 +3684,9 @@ Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
     }
 
   // parse static item expression (required)
-  std::unique_ptr<AST::Expr> expr = parse_expr ();
+  auto expr = parse_expr ();
+  if (!expr)
+    return nullptr;
 
   if (!skip_token (SEMICOLON))
     {
@@ -3689,7 +3696,7 @@ Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
 
   return std::unique_ptr<AST::StaticItem> (
     new AST::StaticItem (std::move (ident), is_mut, std::move (type),
-                        std::move (expr), std::move (vis),
+                        std::move (expr.value ()), std::move (vis),
                         std::move (outer_attrs), locus));
 }
 
@@ -3923,7 +3930,10 @@ Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs)
       lexer.skip_token ();
 
       // expression must exist, so parse it
-      const_body = parse_expr ();
+      auto expr = parse_expr ();
+      if (!expr)
+       return nullptr;
+      const_body = std::move (expr.value ());
     }
 
   if (!skip_token (SEMICOLON))
@@ -4880,12 +4890,13 @@ Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
       // must have an expression
       lexer.skip_token ();
 
-      expr = parse_expr ();
-      if (expr == nullptr)
+      auto expr_res = parse_expr ();
+      if (!expr_res)
        {
          skip_after_semicolon ();
          return nullptr;
        }
+      expr = std::move (expr_res.value ());
     }
 
   tl::optional<std::unique_ptr<AST::Expr>> else_expr = tl::nullopt;
@@ -5300,7 +5311,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
 {
   location_t locus = lexer.peek_token ()->get_locus ();
 
-  std::unique_ptr<AST::Expr> expr;
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr;
 
   switch (lexer.peek_token ()->get_id ())
     {
@@ -5313,7 +5324,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
     case SCOPE_RESOLUTION:
       {
        AST::PathInExpression path = parse_path_in_expression ();
-       std::unique_ptr<AST::Expr> null_denotation;
+       tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
+         null_denotation;
 
        if (lexer.peek_token ()->get_id () == EXCLAM)
          {
@@ -5361,7 +5373,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
       break;
     }
 
-  if (expr == nullptr)
+  if (!expr)
     {
       // expr is required, error
       Error error (lexer.peek_token ()->get_locus (),
@@ -5380,7 +5392,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
        {
          has_semi = true;
        }
-      else if (expr->is_expr_without_block ())
+      else if (expr.value ()->is_expr_without_block ())
        {
          if (restrictions.allow_close_after_expr_stmt)
            {
@@ -5399,8 +5411,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
        }
     }
 
-  return std::unique_ptr<AST::ExprStmt> (
-    new AST::ExprStmt (std::move (expr), locus, has_semi));
+  return std::make_unique<AST::ExprStmt> (std::move (expr.value ()), locus,
+                                         has_semi);
 }
 
 // Parses a loop label used in loop expressions.
@@ -5468,8 +5480,8 @@ Parser<ManagedTokenSource>::parse_match_arm ()
     {
       lexer.skip_token ();
 
-      guard_expr = parse_expr ();
-      if (guard_expr == nullptr)
+      auto guard_expr_res = parse_expr ();
+      if (!guard_expr_res)
        {
          Error error (lexer.peek_token ()->get_locus (),
                       "failed to parse guard expression in match arm");
@@ -5478,6 +5490,7 @@ Parser<ManagedTokenSource>::parse_match_arm ()
          // skip somewhere?
          return AST::MatchArm::create_error ();
        }
+      guard_expr = std::move (guard_expr_res.value ());
     }
 
   // DEBUG
@@ -6959,7 +6972,7 @@ Parser<ManagedTokenSource>::parse_tuple_struct_items ()
  * exists). Useful for block expressions where it cannot be determined through
  * lookahead whether it is a statement or expression to be parsed. */
 template <typename ManagedTokenSource>
-ExprOrStmt
+tl::expected<ExprOrStmt, Parse::Error::Node>
 Parser<ManagedTokenSource>::parse_stmt_or_expr ()
 {
   // quick exit for empty statement
@@ -6976,7 +6989,9 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr ()
   AST::AttrVec outer_attrs = parse_outer_attributes ();
   ParseRestrictions restrictions;
   restrictions.expr_can_be_stmt = true;
-  std::unique_ptr<AST::Expr> expr;
+
+  // Defered child error checking: we need to check for a semicolon
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr> expr;
 
   // parsing this will be annoying because of the many different possibilities
   /* best may be just to copy paste in parse_item switch, and failing that try
@@ -7066,7 +7081,7 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr ()
                              t->get_token_description ()));
 
            // skip somewhere?
-           return ExprOrStmt::create_error ();
+           return tl::unexpected (Parse::Error::Node::MALFORMED);
          }
        break;
       }
@@ -7101,7 +7116,8 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr ()
     case DOLLAR_SIGN:
       {
        AST::PathInExpression path = parse_path_in_expression ();
-       std::unique_ptr<AST::Expr> null_denotation;
+       tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
+         null_denotation;
 
        if (lexer.peek_token ()->get_id () == EXCLAM)
          {
@@ -7109,7 +7125,7 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr ()
              = parse_macro_invocation_partial (std::move (path),
                                                std::move (outer_attrs));
            if (invoc == nullptr)
-             return ExprOrStmt::create_error ();
+             return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 
            if (restrictions.consume_semi && maybe_skip_token (SEMICOLON))
              {
@@ -7160,27 +7176,31 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr ()
 
       if (expr)
        {
-         std::unique_ptr<AST::ExprStmt> stmt (
-           new AST::ExprStmt (std::move (expr), t->get_locus (), true));
-         return ExprOrStmt (std::move (stmt));
+         return ExprOrStmt (
+           std::make_unique<AST::ExprStmt> (std::move (expr.value ()),
+                                            t->get_locus (), true));
        }
       else
        {
-         return ExprOrStmt::create_error ();
+         return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
        }
     }
 
-  if (expr && !expr->is_expr_without_block ()
-      && after_expr->get_id () != RIGHT_CURLY)
+  if (expr)
     {
       // block expression statement.
-      std::unique_ptr<AST::ExprStmt> stmt (
-       new AST::ExprStmt (std::move (expr), t->get_locus (), false));
-      return ExprOrStmt (std::move (stmt));
+      if (!expr.value ()->is_expr_without_block ()
+         && after_expr->get_id () != RIGHT_CURLY)
+       return ExprOrStmt (
+         std::make_unique<AST::ExprStmt> (std::move (expr.value ()),
+                                          t->get_locus (), false));
     }
 
   // return expression
-  return ExprOrStmt (std::move (expr));
+  if (expr)
+    return ExprOrStmt (std::move (expr.value ()));
+  else
+    return tl::unexpected (Parse::Error::Node::CHILD_ERROR);
 }
 
 } // namespace Rust
index cf26cc2dacba987a663d40911e6a20c9acb54e80..7d632391c7a3f652807683a82fc17d920f5b371a 100644 (file)
@@ -152,16 +152,6 @@ struct ExprOrStmt
     : expr (std::move (macro))
   {}
 
-  // Returns whether this object is in an error state.
-  bool is_error () const
-  {
-    return (expr == nullptr && stmt == nullptr)
-          || (expr != nullptr && stmt != nullptr);
-  }
-
-  // Returns an error state object.
-  static ExprOrStmt create_error () { return ExprOrStmt (nullptr, nullptr); }
-
   ~ExprOrStmt () = default;
 
   /* no copy constructors/assignment as simple object like this shouldn't
@@ -237,7 +227,7 @@ public:
    */
   bool maybe_skip_token (TokenId t);
 
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
              ParseRestrictions restrictions = ParseRestrictions ());
 
@@ -478,217 +468,256 @@ private:
                                                   AST::AttrVec outer_attrs);
 
   // Expression-related (Pratt parsed)
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   parse_expr (int right_binding_power,
              AST::AttrVec outer_attrs = AST::AttrVec (),
              ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   null_denotation (AST::AttrVec outer_attrs = AST::AttrVec (),
                   ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   null_denotation_path (AST::PathInExpression path, AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   null_denotation_not_path (const_TokenPtr t, AST::AttrVec outer_attrs,
                            ParseRestrictions restrictions
                            = ParseRestrictions ());
-  std::unique_ptr<AST::Expr>
-  left_denotations (std::unique_ptr<AST::Expr> null_denotation,
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
+  left_denotations (tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
+                     null_denotation,
                    int right_binding_power, AST::AttrVec outer_attrs,
                    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::Expr>
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Expr>
   left_denotation (const_TokenPtr t, std::unique_ptr<AST::Expr> left,
                   AST::AttrVec outer_attrs = AST::AttrVec (),
                   ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_arithmetic_or_logical_expr (
     const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
     AST::AttrVec outer_attrs, AST::ArithmeticOrLogicalExpr::ExprType expr_type,
     ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_binary_plus_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_binary_minus_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                           AST::AttrVec outer_attrs,
                           ParseRestrictions restrictions
                           = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_binary_mult_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_binary_div_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_binary_mod_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_bitwise_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_bitwise_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_bitwise_xor_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_left_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  tl::expected<std::unique_ptr<AST::ArithmeticOrLogicalExpr>,
+              Parse::Error::Expr>
   parse_right_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr>
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
   parse_comparison_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         AST::ComparisonExpr::ExprType expr_type,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr>
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
   parse_binary_equal_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                           AST::AttrVec outer_attrs,
                           ParseRestrictions restrictions
                           = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr> parse_binary_not_equal_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_than_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr> parse_binary_less_than_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_equal_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ComparisonExpr> parse_binary_less_equal_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::LazyBooleanExpr>
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
+  parse_binary_not_equal_expr (const_TokenPtr tok,
+                              std::unique_ptr<AST::Expr> left,
+                              AST::AttrVec outer_attrs,
+                              ParseRestrictions restrictions
+                              = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
+  parse_binary_greater_than_expr (const_TokenPtr tok,
+                                 std::unique_ptr<AST::Expr> left,
+                                 AST::AttrVec outer_attrs,
+                                 ParseRestrictions restrictions
+                                 = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
+  parse_binary_less_than_expr (const_TokenPtr tok,
+                              std::unique_ptr<AST::Expr> left,
+                              AST::AttrVec outer_attrs,
+                              ParseRestrictions restrictions
+                              = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
+  parse_binary_greater_equal_expr (const_TokenPtr tok,
+                                  std::unique_ptr<AST::Expr> left,
+                                  AST::AttrVec outer_attrs,
+                                  ParseRestrictions restrictions
+                                  = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::ComparisonExpr>, Parse::Error::Expr>
+  parse_binary_less_equal_expr (const_TokenPtr tok,
+                               std::unique_ptr<AST::Expr> left,
+                               AST::AttrVec outer_attrs,
+                               ParseRestrictions restrictions
+                               = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
   parse_lazy_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                      AST::AttrVec outer_attrs,
                      ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::LazyBooleanExpr>
+  tl::expected<std::unique_ptr<AST::LazyBooleanExpr>, Parse::Error::Expr>
   parse_lazy_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                       AST::AttrVec outer_attrs,
                       ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::TypeCastExpr>
+  tl::expected<std::unique_ptr<AST::TypeCastExpr>, Parse::Error::Expr>
   parse_type_cast_expr (const_TokenPtr tok,
                        std::unique_ptr<AST::Expr> expr_to_cast,
                        AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::AssignmentExpr>
+  tl::expected<std::unique_ptr<AST::AssignmentExpr>, Parse::Error::Expr>
   parse_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                    AST::AttrVec outer_attrs,
                    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr> parse_compound_assignment_expr (
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
+  parse_compound_assignment_expr (
     const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
     AST::AttrVec outer_attrs, AST::CompoundAssignmentExpr::ExprType expr_type,
     ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_plus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_minus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                          AST::AttrVec outer_attrs,
                          ParseRestrictions restrictions
                          = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_mult_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                         AST::AttrVec outer_attrs,
                         ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_div_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                        AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_mod_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                        AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_and_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                        AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_or_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                       AST::AttrVec outer_attrs,
                       ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
   parse_xor_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
                        AST::AttrVec outer_attrs,
                        ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr> parse_left_shift_assig_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CompoundAssignmentExpr> parse_right_shift_assig_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::AwaitExpr>
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
+  parse_left_shift_assig_expr (const_TokenPtr tok,
+                              std::unique_ptr<AST::Expr> left,
+                              AST::AttrVec outer_attrs,
+                              ParseRestrictions restrictions
+                              = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::CompoundAssignmentExpr>, Parse::Error::Expr>
+  parse_right_shift_assig_expr (const_TokenPtr tok,
+                               std::unique_ptr<AST::Expr> left,
+                               AST::AttrVec outer_attrs,
+                               ParseRestrictions restrictions
+                               = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::AwaitExpr>, Parse::Error::Expr>
   parse_await_expr (const_TokenPtr tok,
                    std::unique_ptr<AST::Expr> expr_to_await,
                    AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::MethodCallExpr> parse_method_call_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::CallExpr> parse_function_call_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> function_expr,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::RangeExpr> parse_led_range_exclusive_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::RangeExpr>
+  tl::expected<std::unique_ptr<AST::MethodCallExpr>, Parse::Error::Expr>
+  parse_method_call_expr (const_TokenPtr tok,
+                         std::unique_ptr<AST::Expr> receiver_expr,
+                         AST::AttrVec outer_attrs,
+                         ParseRestrictions restrictions
+                         = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
+  parse_function_call_expr (const_TokenPtr tok,
+                           std::unique_ptr<AST::Expr> function_expr,
+                           AST::AttrVec outer_attrs,
+                           ParseRestrictions restrictions
+                           = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
+  parse_led_range_exclusive_expr (const_TokenPtr tok,
+                                 std::unique_ptr<AST::Expr> left,
+                                 AST::AttrVec outer_attrs,
+                                 ParseRestrictions restrictions
+                                 = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::RangeExpr>, Parse::Error::Expr>
   parse_nud_range_exclusive_expr (const_TokenPtr tok, AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::RangeFromToInclExpr> parse_range_inclusive_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::RangeToInclExpr>
+  tl::expected<std::unique_ptr<AST::RangeFromToInclExpr>, Parse::Error::Expr>
+  parse_range_inclusive_expr (const_TokenPtr tok,
+                             std::unique_ptr<AST::Expr> left,
+                             AST::AttrVec outer_attrs,
+                             ParseRestrictions restrictions
+                             = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::RangeToInclExpr>, Parse::Error::Expr>
   parse_range_to_inclusive_expr (const_TokenPtr tok, AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::FieldAccessExpr> parse_field_access_expr (
-    const_TokenPtr tok, std::unique_ptr<AST::Expr> struct_expr,
-    AST::AttrVec outer_attrs,
-    ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::ArrayIndexExpr>
+  tl::expected<std::unique_ptr<AST::TupleIndexExpr>, Parse::Error::Expr>
+  parse_tuple_index_expr (const_TokenPtr tok,
+                         std::unique_ptr<AST::Expr> tuple_expr,
+                         AST::AttrVec outer_attrs,
+                         ParseRestrictions restrictions
+                         = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::FieldAccessExpr>, Parse::Error::Expr>
+  parse_field_access_expr (const_TokenPtr tok,
+                          std::unique_ptr<AST::Expr> struct_expr,
+                          AST::AttrVec outer_attrs,
+                          ParseRestrictions restrictions
+                          = ParseRestrictions ());
+  tl::expected<std::unique_ptr<AST::ArrayIndexExpr>, Parse::Error::Expr>
   parse_index_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> array_expr,
                    AST::AttrVec outer_attrs,
                    ParseRestrictions restrictions = ParseRestrictions ());
   std::unique_ptr<AST::MacroInvocation> parse_macro_invocation_partial (
     AST::PathInExpression path, AST::AttrVec outer_attrs,
     ParseRestrictions restrictions = ParseRestrictions ());
-  std::unique_ptr<AST::StructExprStruct>
+  tl::expected<std::unique_ptr<AST::StructExprStruct>, Parse::Error::Expr>
   parse_struct_expr_struct_partial (AST::PathInExpression path,
                                    AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::CallExpr>
+  tl::expected<std::unique_ptr<AST::CallExpr>, Parse::Error::Expr>
   parse_struct_expr_tuple_partial (AST::PathInExpression path,
                                   AST::AttrVec outer_attrs);
-  std::unique_ptr<AST::ClosureExpr>
+  tl::expected<std::unique_ptr<AST::ClosureExpr>, Parse::Error::Expr>
   parse_closure_expr_pratt (const_TokenPtr tok,
                            AST::AttrVec outer_attrs = AST::AttrVec ());
   std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr_float (
@@ -699,37 +728,37 @@ private:
   // When given a pratt_parsed_loc, use it as the location of the
   // first token parsed in the expression (the parsing of that first
   // token should be skipped).
-  std::unique_ptr<AST::IfExpr>
+  tl::expected<std::unique_ptr<AST::IfExpr>, Parse::Error::Node>
   parse_if_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                 location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::IfLetExpr>
+  tl::expected<std::unique_ptr<AST::IfLetExpr>, Parse::Error::Node>
   parse_if_let_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                     location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::LoopExpr>
+  tl::expected<std::unique_ptr<AST::LoopExpr>, Parse::Error::Node>
   parse_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                   tl::optional<AST::LoopLabel> label = tl::nullopt,
                   location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::WhileLoopExpr>
+  tl::expected<std::unique_ptr<AST::WhileLoopExpr>, Parse::Error::Node>
   parse_while_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                         tl::optional<AST::LoopLabel> label = tl::nullopt,
                         location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::WhileLetLoopExpr>
+  tl::expected<std::unique_ptr<AST::WhileLetLoopExpr>, Parse::Error::Node>
   parse_while_let_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                             tl::optional<AST::LoopLabel> label = tl::nullopt);
-  std::unique_ptr<AST::ForLoopExpr>
+  tl::expected<std::unique_ptr<AST::ForLoopExpr>, Parse::Error::Node>
   parse_for_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                       tl::optional<AST::LoopLabel> label = tl::nullopt);
-  std::unique_ptr<AST::MatchExpr>
+  tl::expected<std::unique_ptr<AST::MatchExpr>, Parse::Error::Node>
   parse_match_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                    location_t pratt_parsed_loc = UNKNOWN_LOCATION);
   AST::MatchArm parse_match_arm ();
   std::unique_ptr<AST::Pattern> parse_match_arm_pattern (TokenId end_token_id);
-  std::unique_ptr<AST::Expr> parse_labelled_loop_expr (const_TokenPtr tok,
-                                                      AST::AttrVec outer_attrs
-                                                      = AST::AttrVec ());
+  tl::expected<std::unique_ptr<AST::Expr>, Parse::Error::Node>
+  parse_labelled_loop_expr (const_TokenPtr tok,
+                           AST::AttrVec outer_attrs = AST::AttrVec ());
   tl::expected<AST::LoopLabel, Parse::Error::LoopLabel>
   parse_loop_label (const_TokenPtr tok);
-  std::unique_ptr<AST::AsyncBlockExpr>
+  tl::expected<std::unique_ptr<AST::AsyncBlockExpr>, Parse::Error::Node>
   parse_async_block_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
   tl::expected<std::unique_ptr<AST::GroupedExpr>, Parse::Error::Node>
   parse_grouped_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
@@ -737,34 +766,36 @@ private:
   parse_closure_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
   AST::ClosureParam parse_closure_param ();
 
-  std::unique_ptr<AST::BoxExpr> parse_box_expr (AST::AttrVec outer_attrs,
-                                               location_t pratt_parsed_loc
-                                               = UNKNOWN_LOCATION);
+  tl::expected<std::unique_ptr<AST::BoxExpr>, Parse::Error::Node>
+  parse_box_expr (AST::AttrVec outer_attrs,
+                 location_t pratt_parsed_loc = UNKNOWN_LOCATION);
   // When given a pratt_parsed_loc, use it as the location of the
   // first token parsed in the expression (the parsing of that first
   // token should be skipped).
-  std::unique_ptr<AST::ReturnExpr>
+  tl::expected<std::unique_ptr<AST::ReturnExpr>, Parse::Error::Node>
   parse_return_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                     location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::TryExpr>
+  tl::expected<std::unique_ptr<AST::TryExpr>, Parse::Error::Node>
   parse_try_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                  location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::BreakExpr>
+  tl::expected<std::unique_ptr<AST::BreakExpr>, Parse::Error::Node>
   parse_break_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                    location_t pratt_parsed_loc = UNKNOWN_LOCATION);
   std::unique_ptr<AST::ContinueExpr>
   parse_continue_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                       location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::UnsafeBlockExpr>
+  tl::expected<std::unique_ptr<AST::UnsafeBlockExpr>, Parse::Error::Node>
   parse_unsafe_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                           location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::ArrayExpr>
+  tl::expected<std::unique_ptr<AST::ArrayExpr>, Parse::Error::Node>
   parse_array_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                    location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::ExprWithoutBlock>
+  tl::expected<std::unique_ptr<AST::ExprWithoutBlock>, Parse::Error::Node>
   parse_grouped_or_tuple_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
                               location_t pratt_parsed_loc = UNKNOWN_LOCATION);
-  std::unique_ptr<AST::StructExprField> parse_struct_expr_field ();
+  tl::expected<std::unique_ptr<AST::StructExprField>,
+              Parse::Error::StructExprField>
+  parse_struct_expr_field ();
   bool will_be_expr_with_block ();
 
   // Type-related
@@ -798,7 +829,7 @@ private:
   std::unique_ptr<AST::Stmt> parse_expr_stmt (AST::AttrVec outer_attrs,
                                              ParseRestrictions restrictions
                                              = ParseRestrictions ());
-  ExprOrStmt parse_stmt_or_expr ();
+  tl::expected<ExprOrStmt, Parse::Error::Node> parse_stmt_or_expr ();
 
   // Pattern-related
   std::unique_ptr<AST::Pattern> parse_literal_or_range_pattern ();
index 54b3be8f1ef9ff6a575f828c58d085ca98d8f22f..0da8c38b63e0809c163c973064df563bdefcb5fa 100644 (file)
@@ -263,7 +263,7 @@ Late::visit (AST::BreakExpr &expr)
 
   if (expr.has_break_expr ())
     {
-      auto &break_expr = expr.get_break_expr ();
+      auto &break_expr = expr.get_break_expr_unchecked ();
       if (break_expr.get_expr_kind () == AST::Expr::Kind::Identifier)
        {
          /* This is a break with an expression, and the expression is
@@ -273,9 +273,9 @@ Late::visit (AST::BreakExpr &expr)
             emit the error here though, because the identifier may still
             be in scope, and ICE'ing on valid programs would not be very
             funny.  */
-         std::string ident
-           = static_cast<AST::IdentifierExpr &> (expr.get_break_expr ())
-               .as_string ();
+         std::string ident = static_cast<AST::IdentifierExpr &> (
+                               expr.get_break_expr_unchecked ())
+                               .as_string ();
          if (ident == "rust" || ident == "gcc")
            funny_error = true;
        }