diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index c5960530d0a..2f4f409f54f 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -94,6 +94,7 @@ GRS_OBJS = \
     rust/rust-ast-lower-base.o \
     rust/rust-ast-lower-pattern.o \
     rust/rust-ast-lower-item.o \
+    rust/rust-ast-lower-expr.o \
     rust/rust-early-name-resolver.o \
     rust/rust-name-resolver.o \
     rust/rust-ast-resolve.o \
diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h
index c764f9c4c66..c58fae5e564 100644
--- a/gcc/rust/ast/rust-expr.h
+++ b/gcc/rust/ast/rust-expr.h
@@ -2134,8 +2134,6 @@ struct ClosureParam
 private:
   std::vector<Attribute> outer_attrs;
   std::unique_ptr<Pattern> pattern;
-
-  // bool has_type_given;
   std::unique_ptr<Type> type;
   Location locus;
 
@@ -2202,19 +2200,19 @@ public:
   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
 
-  // TODO: is this better? Or is a "vis_block" better?
   std::unique_ptr<Pattern> &get_pattern ()
   {
     rust_assert (pattern != nullptr);
     return pattern;
   }
 
-  // TODO: is this better? Or is a "vis_block" better?
   std::unique_ptr<Type> &get_type ()
   {
     rust_assert (has_type_given ());
     return type;
   }
+
+  Location get_locus () const { return locus; }
 };
 
 // Base closure definition expression AST node - abstract
@@ -2248,6 +2246,8 @@ public:
   {
     outer_attrs = std::move (new_attrs);
   }
+
+  bool get_has_move () const { return has_move; }
 };
 
 // Represents a non-type-specified closure expression AST node
@@ -2307,7 +2307,6 @@ public:
     return closure_inner == nullptr;
   }
 
-  // TODO: is this better? Or is a "vis_block" better?
   std::unique_ptr<Expr> &get_definition_expr ()
   {
     rust_assert (closure_inner != nullptr);
diff --git a/gcc/rust/backend/rust-compile-block.h b/gcc/rust/backend/rust-compile-block.h
index bcb7e77fc6d..0ccf81f376b 100644
--- a/gcc/rust/backend/rust-compile-block.h
+++ b/gcc/rust/backend/rust-compile-block.h
@@ -57,8 +57,7 @@ public:
   // Empty visit for unused Expression HIR nodes.
   void visit (HIR::PathInExpression &) override {}
   void visit (HIR::QualifiedPathInExpression &) override {}
-  void visit (HIR::ClosureExprInner &) override {}
-  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::ClosureExpr &) override {}
   void visit (HIR::StructExprFieldIdentifier &) override {}
   void visit (HIR::StructExprFieldIdentifierValue &) override {}
   void visit (HIR::StructExprFieldIndexValue &) override {}
@@ -146,8 +145,7 @@ public:
   // Empty visit for unused Expression HIR nodes.
   void visit (HIR::PathInExpression &) override {}
   void visit (HIR::QualifiedPathInExpression &) override {}
-  void visit (HIR::ClosureExprInner &) override {}
-  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::ClosureExpr &) override {}
   void visit (HIR::StructExprFieldIdentifier &) override {}
   void visit (HIR::StructExprFieldIdentifierValue &) override {}
   void visit (HIR::StructExprFieldIndexValue &) override {}
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index ddf914f6736..724a93a68bd 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -2803,5 +2803,11 @@ CompileExpr::visit (HIR::ArrayIndexExpr &expr)
 						   expr.get_locus ());
 }
 
+void
+CompileExpr::visit (HIR::ClosureExpr &expr)
+{
+  gcc_unreachable ();
+}
+
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 845511f9f43..7fc3f5e7f4d 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -67,10 +67,9 @@ public:
   void visit (HIR::RangeToExpr &expr) override;
   void visit (HIR::RangeFullExpr &expr) override;
   void visit (HIR::RangeFromToInclExpr &expr) override;
+  void visit (HIR::ClosureExpr &expr) override;
 
   // TODO
-  void visit (HIR::ClosureExprInner &) override {}
-  void visit (HIR::ClosureExprInnerTyped &) override {}
   void visit (HIR::ErrorPropagationExpr &) override {}
   void visit (HIR::RangeToInclExpr &) override {}
   void visit (HIR::ForLoopExpr &) override {}
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
index e5afe817801..a126e7b4462 100644
--- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
@@ -426,7 +426,7 @@ PrivacyReporter::visit (HIR::FieldAccessExpr &expr)
 }
 
 void
-PrivacyReporter::visit (HIR::ClosureExprInner &expr)
+PrivacyReporter::visit (HIR::ClosureExpr &expr)
 {
   // Not handled yet
 }
@@ -442,12 +442,6 @@ PrivacyReporter::visit (HIR::BlockExpr &expr)
     last_expr->accept_vis (*this);
 }
 
-void
-PrivacyReporter::visit (HIR::ClosureExprInnerTyped &expr)
-{
-  // Not handled yet
-}
-
 void
 PrivacyReporter::visit (HIR::ContinueExpr &expr)
 {}
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
index 98f7a9419b2..c9307787e9e 100644
--- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
@@ -83,8 +83,7 @@ types
 
   virtual void visit (HIR::QualifiedPathInExpression &expr);
   virtual void visit (HIR::PathInExpression &expr);
-  virtual void visit (HIR::ClosureExprInnerTyped &);
-  virtual void visit (HIR::ClosureExprInner &expr);
+  virtual void visit (HIR::ClosureExpr &expr);
   virtual void visit (HIR::StructExprStructFields &);
   virtual void visit (HIR::StructExprStruct &);
   virtual void visit (HIR::LiteralExpr &expr);
diff --git a/gcc/rust/checks/errors/rust-const-checker.cc b/gcc/rust/checks/errors/rust-const-checker.cc
index 2fa9614abf1..01dc2620767 100644
--- a/gcc/rust/checks/errors/rust-const-checker.cc
+++ b/gcc/rust/checks/errors/rust-const-checker.cc
@@ -382,7 +382,7 @@ ConstChecker::visit (FieldAccessExpr &expr)
 }
 
 void
-ConstChecker::visit (ClosureExprInner &expr)
+ConstChecker::visit (ClosureExpr &expr)
 {}
 
 void
@@ -395,10 +395,6 @@ ConstChecker::visit (BlockExpr &expr)
     expr.get_final_expr ()->accept_vis (*this);
 }
 
-void
-ConstChecker::visit (ClosureExprInnerTyped &expr)
-{}
-
 void
 ConstChecker::visit (ContinueExpr &expr)
 {}
diff --git a/gcc/rust/checks/errors/rust-const-checker.h b/gcc/rust/checks/errors/rust-const-checker.h
index 13cf44b1273..238173874aa 100644
--- a/gcc/rust/checks/errors/rust-const-checker.h
+++ b/gcc/rust/checks/errors/rust-const-checker.h
@@ -111,9 +111,8 @@ private:
   virtual void visit (CallExpr &expr) override;
   virtual void visit (MethodCallExpr &expr) override;
   virtual void visit (FieldAccessExpr &expr) override;
-  virtual void visit (ClosureExprInner &expr) override;
+  virtual void visit (ClosureExpr &expr) override;
   virtual void visit (BlockExpr &expr) override;
-  virtual void visit (ClosureExprInnerTyped &expr) override;
   virtual void visit (ContinueExpr &expr) override;
   virtual void visit (BreakExpr &expr) override;
   virtual void visit (RangeFromToExpr &expr) override;
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc
index bb85fa77dc7..02868794cbc 100644
--- a/gcc/rust/checks/errors/rust-unsafe-checker.cc
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc
@@ -453,7 +453,7 @@ UnsafeChecker::visit (FieldAccessExpr &expr)
 }
 
 void
-UnsafeChecker::visit (ClosureExprInner &expr)
+UnsafeChecker::visit (ClosureExpr &expr)
 {}
 
 void
@@ -466,10 +466,6 @@ UnsafeChecker::visit (BlockExpr &expr)
     expr.get_final_expr ()->accept_vis (*this);
 }
 
-void
-UnsafeChecker::visit (ClosureExprInnerTyped &expr)
-{}
-
 void
 UnsafeChecker::visit (ContinueExpr &expr)
 {}
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h
index 39c3b229ccc..9df44db06a9 100644
--- a/gcc/rust/checks/errors/rust-unsafe-checker.h
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.h
@@ -88,9 +88,8 @@ private:
   virtual void visit (CallExpr &expr) override;
   virtual void visit (MethodCallExpr &expr) override;
   virtual void visit (FieldAccessExpr &expr) override;
-  virtual void visit (ClosureExprInner &expr) override;
+  virtual void visit (ClosureExpr &expr) override;
   virtual void visit (BlockExpr &expr) override;
-  virtual void visit (ClosureExprInnerTyped &expr) override;
   virtual void visit (ContinueExpr &expr) override;
   virtual void visit (BreakExpr &expr) override;
   virtual void visit (RangeFromToExpr &expr) override;
diff --git a/gcc/rust/hir/rust-ast-lower-base.h b/gcc/rust/hir/rust-ast-lower-base.h
index adcbc5d2f69..1af44aa239f 100644
--- a/gcc/rust/hir/rust-ast-lower-base.h
+++ b/gcc/rust/hir/rust-ast-lower-base.h
@@ -313,6 +313,8 @@ protected:
   HIR::Literal lower_literal (const AST::Literal &literal);
 
   HIR::ExternBlock *lower_extern_block (AST::ExternBlock &extern_block);
+
+  HIR::ClosureParam lower_closure_param (AST::ClosureParam &param);
 };
 
 } // namespace HIR
diff --git a/gcc/rust/hir/rust-ast-lower-expr.cc b/gcc/rust/hir/rust-ast-lower-expr.cc
new file mode 100644
index 00000000000..df4ba9d2911
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-expr.cc
@@ -0,0 +1,810 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-ast-lower-expr.h"
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-block.h"
+#include "rust-ast-lower-struct-field-expr.h"
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-type.h"
+
+namespace Rust {
+namespace HIR {
+
+ASTLoweringExpr::ASTLoweringExpr ()
+  : ASTLoweringBase (), translated (nullptr), translated_array_elems (nullptr),
+    terminated (false)
+{}
+
+HIR::Expr *
+ASTLoweringExpr::translate (AST::Expr *expr, bool *terminated)
+{
+  ASTLoweringExpr resolver;
+  expr->accept_vis (resolver);
+  if (resolver.translated == nullptr)
+    {
+      rust_fatal_error (expr->get_locus (), "Failed to lower expr: [%s]",
+			expr->as_string ().c_str ());
+      return nullptr;
+    }
+
+  resolver.mappings->insert_hir_expr (resolver.translated);
+  resolver.mappings->insert_location (
+    resolver.translated->get_mappings ().get_hirid (), expr->get_locus ());
+
+  if (terminated != nullptr)
+    *terminated = resolver.terminated;
+
+  return resolver.translated;
+}
+
+void
+ASTLoweringExpr::visit (AST::TupleIndexExpr &expr)
+{
+  HIR::Expr *tuple_expr
+    = ASTLoweringExpr::translate (expr.get_tuple_expr ().get (), &terminated);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::TupleIndexExpr (mapping, std::unique_ptr<HIR::Expr> (tuple_expr),
+			       expr.get_tuple_index (), expr.get_outer_attrs (),
+			       expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::TupleExpr &expr)
+{
+  std::vector<std::unique_ptr<HIR::Expr> > tuple_elements;
+  for (auto &e : expr.get_tuple_elems ())
+    {
+      HIR::Expr *t = ASTLoweringExpr::translate (e.get ());
+      tuple_elements.push_back (std::unique_ptr<HIR::Expr> (t));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::TupleExpr (std::move (mapping), std::move (tuple_elements),
+			  expr.get_inner_attrs (), expr.get_outer_attrs (),
+			  expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::IfExpr &expr)
+{
+  translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::IfExprConseqElse &expr)
+{
+  translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::IfExprConseqIf &expr)
+{
+  translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::BlockExpr &expr)
+{
+  translated = ASTLoweringBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::UnsafeBlockExpr &expr)
+{
+  translated = ASTLoweringBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::PathInExpression &expr)
+{
+  translated = ASTLowerPathInExpression::translate (&expr);
+}
+
+void
+ASTLoweringExpr::visit (AST::QualifiedPathInExpression &expr)
+{
+  translated = ASTLowerQualPathInExpression::translate (&expr);
+}
+
+void
+ASTLoweringExpr::visit (AST::ReturnExpr &expr)
+{
+  terminated = true;
+  HIR::Expr *return_expr
+    = expr.has_returned_expr ()
+	? ASTLoweringExpr::translate (expr.get_returned_expr ().get ())
+	: nullptr;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::ReturnExpr (mapping, expr.get_locus (),
+				    std::unique_ptr<HIR::Expr> (return_expr));
+}
+
+void
+ASTLoweringExpr::visit (AST::CallExpr &expr)
+{
+  HIR::Expr *func
+    = ASTLoweringExpr::translate (expr.get_function_expr ().get ());
+
+  auto const &in_params = expr.get_params ();
+  std::vector<std::unique_ptr<HIR::Expr> > params;
+  for (auto &param : in_params)
+    {
+      auto trans = ASTLoweringExpr::translate (param.get ());
+      params.push_back (std::unique_ptr<HIR::Expr> (trans));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (
+    crate_num, UNKNOWN_NODEID /* this can map back to the AST*/,
+    mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::CallExpr (mapping, std::unique_ptr<HIR::Expr> (func),
+				  std::move (params), expr.get_outer_attrs (),
+				  expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::MethodCallExpr &expr)
+{
+  HIR::PathExprSegment method_path
+    = lower_path_expr_seg (expr.get_method_name ());
+
+  HIR::Expr *receiver
+    = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
+
+  auto const &in_params = expr.get_params ();
+  std::vector<std::unique_ptr<HIR::Expr> > params;
+  for (auto &param : in_params)
+    {
+      auto trans = ASTLoweringExpr::translate (param.get ());
+      params.push_back (std::unique_ptr<HIR::Expr> (trans));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::MethodCallExpr (mapping, std::unique_ptr<HIR::Expr> (receiver),
+			       method_path, std::move (params),
+			       expr.get_outer_attrs (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::AssignmentExpr &expr)
+{
+  HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+  HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::AssignmentExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+			       std::unique_ptr<HIR::Expr> (rhs),
+			       expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::IdentifierExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping1 (crate_num, expr.get_node_id (),
+				  mappings->get_next_hir_id (crate_num),
+				  UNKNOWN_LOCAL_DEFID);
+  Analysis::NodeMapping mapping2 (mapping1);
+
+  HIR::PathIdentSegment ident_seg (expr.get_ident ());
+  HIR::PathExprSegment seg (mapping1, ident_seg, expr.get_locus (),
+			    HIR::GenericArgs::create_empty ());
+  translated = new HIR::PathInExpression (mapping2, {seg}, expr.get_locus (),
+					  false, expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ArrayExpr &expr)
+{
+  expr.get_array_elems ()->accept_vis (*this);
+  rust_assert (translated_array_elems != nullptr);
+  HIR::ArrayElems *elems = translated_array_elems;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::ArrayExpr (mapping, std::unique_ptr<HIR::ArrayElems> (elems),
+			  expr.get_inner_attrs (), expr.get_outer_attrs (),
+			  expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ArrayIndexExpr &expr)
+{
+  HIR::Expr *array_expr
+    = ASTLoweringExpr::translate (expr.get_array_expr ().get ());
+  HIR::Expr *array_index_expr
+    = ASTLoweringExpr::translate (expr.get_index_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::ArrayIndexExpr (mapping, std::unique_ptr<HIR::Expr> (array_expr),
+			       std::unique_ptr<HIR::Expr> (array_index_expr),
+			       expr.get_outer_attrs (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ArrayElemsValues &elems)
+{
+  std::vector<std::unique_ptr<HIR::Expr> > elements;
+  for (auto &elem : elems.get_values ())
+    {
+      HIR::Expr *translated_elem = ASTLoweringExpr::translate (elem.get ());
+      elements.push_back (std::unique_ptr<HIR::Expr> (translated_elem));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (mappings->get_current_crate (),
+				 elems.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated_array_elems
+    = new HIR::ArrayElemsValues (mapping, std::move (elements));
+}
+
+void
+ASTLoweringExpr::visit (AST::ArrayElemsCopied &elems)
+{
+  HIR::Expr *element
+    = ASTLoweringExpr::translate (elems.get_elem_to_copy ().get ());
+  HIR::Expr *num_copies
+    = ASTLoweringExpr::translate (elems.get_num_copies ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (mappings->get_current_crate (),
+				 elems.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated_array_elems
+    = new HIR::ArrayElemsCopied (mapping, std::unique_ptr<HIR::Expr> (element),
+				 std::unique_ptr<HIR::Expr> (num_copies));
+}
+
+void
+ASTLoweringExpr::visit (AST::LiteralExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Literal l = lower_literal (expr.get_literal ());
+  translated = new HIR::LiteralExpr (mapping, std::move (l), expr.get_locus (),
+				     expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ArithmeticOrLogicalExpr &expr)
+{
+  HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+  rust_assert (lhs != nullptr);
+  HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+  rust_assert (rhs != nullptr);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::ArithmeticOrLogicalExpr (
+    mapping, std::unique_ptr<HIR::Expr> (lhs), std::unique_ptr<HIR::Expr> (rhs),
+    expr.get_expr_type (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ComparisonExpr &expr)
+{
+  HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+  rust_assert (lhs != nullptr);
+  HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+  rust_assert (rhs != nullptr);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::ComparisonExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+			       std::unique_ptr<HIR::Expr> (rhs),
+			       expr.get_expr_type (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::LazyBooleanExpr &expr)
+{
+  HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+  rust_assert (lhs != nullptr);
+  HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+  rust_assert (rhs != nullptr);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::LazyBooleanExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+				std::unique_ptr<HIR::Expr> (rhs),
+				expr.get_expr_type (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::NegationExpr &expr)
+{
+  HIR::Expr *negated_value
+    = ASTLoweringExpr::translate (expr.get_negated_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+  translated
+    = new HIR::NegationExpr (mapping,
+			     std::unique_ptr<HIR::Expr> (negated_value),
+			     expr.get_expr_type (), expr.get_outer_attrs (),
+			     expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::TypeCastExpr &expr)
+{
+  HIR::Expr *expr_to_cast_to
+    = ASTLoweringExpr::translate (expr.get_casted_expr ().get ());
+  HIR::Type *type_to_cast_to
+    = lower_type_no_bounds (expr.get_type_to_cast_to ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::TypeCastExpr (mapping,
+			     std::unique_ptr<HIR::Expr> (expr_to_cast_to),
+			     std::unique_ptr<HIR::Type> (type_to_cast_to),
+			     expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::CompoundAssignmentExpr &expr)
+{
+  ArithmeticOrLogicalOperator op;
+  switch (expr.get_expr_type ())
+    {
+    case CompoundAssignmentOperator::ADD:
+      op = ArithmeticOrLogicalOperator::ADD;
+      break;
+    case CompoundAssignmentOperator::SUBTRACT:
+      op = ArithmeticOrLogicalOperator::SUBTRACT;
+      break;
+    case CompoundAssignmentOperator::MULTIPLY:
+      op = ArithmeticOrLogicalOperator::MULTIPLY;
+      break;
+    case CompoundAssignmentOperator::DIVIDE:
+      op = ArithmeticOrLogicalOperator::DIVIDE;
+      break;
+    case CompoundAssignmentOperator::MODULUS:
+      op = ArithmeticOrLogicalOperator::MODULUS;
+      break;
+    case CompoundAssignmentOperator::BITWISE_AND:
+      op = ArithmeticOrLogicalOperator::BITWISE_AND;
+      break;
+    case CompoundAssignmentOperator::BITWISE_OR:
+      op = ArithmeticOrLogicalOperator::BITWISE_OR;
+      break;
+    case CompoundAssignmentOperator::BITWISE_XOR:
+      op = ArithmeticOrLogicalOperator::BITWISE_XOR;
+      break;
+    case CompoundAssignmentOperator::LEFT_SHIFT:
+      op = ArithmeticOrLogicalOperator::LEFT_SHIFT;
+      break;
+    case CompoundAssignmentOperator::RIGHT_SHIFT:
+      op = ArithmeticOrLogicalOperator::RIGHT_SHIFT;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  HIR::Expr *asignee_expr
+    = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+  HIR::Expr *value = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::CompoundAssignmentExpr (
+    mapping, std::unique_ptr<HIR::Expr> (asignee_expr),
+    std::unique_ptr<HIR::Expr> (value), op, expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::StructExprStruct &struct_expr)
+{
+  HIR::PathInExpression *path
+    = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
+  HIR::PathInExpression copied_path (*path);
+  delete path;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::StructExprStruct (mapping, copied_path,
+					  struct_expr.get_inner_attrs (),
+					  struct_expr.get_outer_attrs (),
+					  struct_expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::StructExprStructFields &struct_expr)
+{
+  // bit of a hack for now
+  HIR::PathInExpression *path
+    = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
+  HIR::PathInExpression copied_path (*path);
+  delete path;
+
+  HIR::StructBase *base = nullptr;
+  if (struct_expr.has_struct_base ())
+    {
+      HIR::Expr *translated_base = ASTLoweringExpr::translate (
+	struct_expr.get_struct_base ().get_base_struct ().get ());
+      base = new HIR::StructBase (std::unique_ptr<HIR::Expr> (translated_base));
+    }
+
+  auto const &in_fields = struct_expr.get_fields ();
+  std::vector<std::unique_ptr<HIR::StructExprField> > fields;
+  for (auto &field : in_fields)
+    {
+      HIR::StructExprField *translated
+	= ASTLowerStructExprField::translate (field.get ());
+      fields.push_back (std::unique_ptr<HIR::StructExprField> (translated));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::StructExprStructFields (mapping, copied_path, std::move (fields),
+				       struct_expr.get_locus (), base,
+				       struct_expr.get_inner_attrs (),
+				       struct_expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExpr::visit (AST::GroupedExpr &expr)
+{
+  HIR::Expr *paren_expr
+    = ASTLoweringExpr::translate (expr.get_expr_in_parens ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::GroupedExpr (mapping, std::unique_ptr<HIR::Expr> (paren_expr),
+			    expr.get_inner_attrs (), expr.get_outer_attrs (),
+			    expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::FieldAccessExpr &expr)
+{
+  HIR::Expr *receiver
+    = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+  translated
+    = new HIR::FieldAccessExpr (mapping, std::unique_ptr<HIR::Expr> (receiver),
+				expr.get_field_name (), expr.get_outer_attrs (),
+				expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::LoopExpr &expr)
+{
+  translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::WhileLoopExpr &expr)
+{
+  translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::ForLoopExpr &expr)
+{
+  translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::BreakExpr &expr)
+{
+  HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
+  HIR::Expr *break_expr
+    = expr.has_break_expr ()
+	? ASTLoweringExpr::translate (expr.get_break_expr ().get ())
+	: nullptr;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::BreakExpr (mapping, expr.get_locus (), std ::move (break_label),
+			  std::unique_ptr<HIR::Expr> (break_expr),
+			  expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ContinueExpr &expr)
+{
+  HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::ContinueExpr (mapping, expr.get_locus (),
+			     std ::move (break_label), expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExpr::visit (AST::BorrowExpr &expr)
+{
+  HIR::Expr *borrow_lvalue
+    = ASTLoweringExpr::translate (expr.get_borrowed_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::BorrowExpr *borrow_expr
+    = new HIR::BorrowExpr (mapping, std::unique_ptr<HIR::Expr> (borrow_lvalue),
+			   expr.get_is_mut () ? Mutability::Mut
+					      : Mutability::Imm,
+			   expr.get_outer_attrs (), expr.get_locus ());
+
+  if (expr.get_is_double_borrow ())
+    {
+      NodeId artifical_bouble_borrow_id = mappings->get_next_node_id ();
+      Analysis::NodeMapping mapping (crate_num, artifical_bouble_borrow_id,
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      borrow_expr
+	= new HIR::BorrowExpr (mapping,
+			       std::unique_ptr<HIR::Expr> (borrow_expr),
+			       expr.get_is_mut () ? Mutability::Mut
+						  : Mutability::Imm,
+			       expr.get_outer_attrs (), expr.get_locus ());
+    }
+
+  translated = borrow_expr;
+}
+
+void
+ASTLoweringExpr::visit (AST::DereferenceExpr &expr)
+{
+  HIR::Expr *dref_lvalue
+    = ASTLoweringExpr::translate (expr.get_dereferenced_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::DereferenceExpr (mapping,
+				std::unique_ptr<HIR::Expr> (dref_lvalue),
+				expr.get_outer_attrs (), expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::MatchExpr &expr)
+{
+  translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+}
+
+void
+ASTLoweringExpr::visit (AST::RangeFromToExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Expr *range_from
+    = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+  HIR::Expr *range_to = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+  translated
+    = new HIR::RangeFromToExpr (mapping,
+				std::unique_ptr<HIR::Expr> (range_from),
+				std::unique_ptr<HIR::Expr> (range_to),
+				expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::RangeFromExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Expr *range_from
+    = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+
+  translated
+    = new HIR::RangeFromExpr (mapping, std::unique_ptr<HIR::Expr> (range_from),
+			      expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::RangeToExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Expr *range_to = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+  translated
+    = new HIR::RangeToExpr (mapping, std::unique_ptr<HIR::Expr> (range_to),
+			    expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::RangeFullExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::RangeFullExpr (mapping, expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::RangeFromToInclExpr &expr)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Expr *range_from
+    = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+  HIR::Expr *range_to = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+  translated
+    = new HIR::RangeFromToInclExpr (mapping,
+				    std::unique_ptr<HIR::Expr> (range_from),
+				    std::unique_ptr<HIR::Expr> (range_to),
+				    expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ClosureExprInner &expr)
+{
+  HIR::Expr *closure_expr
+    = ASTLoweringExpr::translate (expr.get_definition_expr ().get ());
+
+  std::vector<HIR::ClosureParam> closure_params;
+  for (auto &param : expr.get_params ())
+    {
+      HIR::ClosureParam p = lower_closure_param (param);
+      closure_params.push_back (std::move (p));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated
+    = new HIR::ClosureExpr (mapping, std::move (closure_params),
+			    nullptr /* closure_return_type */,
+			    std::unique_ptr<HIR::Expr> (closure_expr),
+			    expr.get_has_move (), expr.get_outer_attrs (),
+			    expr.get_locus ());
+}
+
+void
+ASTLoweringExpr::visit (AST::ClosureExprInnerTyped &expr)
+{
+  HIR::Type *closure_return_type = nullptr;
+  HIR::Expr *closure_expr
+    = ASTLoweringExpr::translate (expr.get_definition_block ().get ());
+
+  std::vector<HIR::ClosureParam> closure_params;
+  for (auto &param : expr.get_params ())
+    {
+      HIR::ClosureParam p = lower_closure_param (param);
+      closure_params.push_back (std::move (p));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated
+    = new HIR::ClosureExpr (mapping, std::move (closure_params),
+			    std::unique_ptr<HIR::Type> (closure_return_type),
+			    std::unique_ptr<HIR::Expr> (closure_expr),
+			    expr.get_has_move (), expr.get_outer_attrs (),
+			    expr.get_locus ());
+}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower-expr.h b/gcc/rust/hir/rust-ast-lower-expr.h
index b0ab409646d..bea254de79d 100644
--- a/gcc/rust/hir/rust-ast-lower-expr.h
+++ b/gcc/rust/hir/rust-ast-lower-expr.h
@@ -19,11 +19,7 @@
 #ifndef RUST_AST_LOWER_EXPR
 #define RUST_AST_LOWER_EXPR
 
-#include "rust-diagnostics.h"
 #include "rust-ast-lower-base.h"
-#include "rust-ast-lower-block.h"
-#include "rust-ast-lower-struct-field-expr.h"
-#include "rust-ast-lower-pattern.h"
 
 namespace Rust {
 namespace HIR {
@@ -76,703 +72,55 @@ class ASTLoweringExpr : public ASTLoweringBase
   using Rust::HIR::ASTLoweringBase::visit;
 
 public:
-  static HIR::Expr *translate (AST::Expr *expr, bool *terminated = nullptr)
-  {
-    ASTLoweringExpr resolver;
-    expr->accept_vis (resolver);
-    if (resolver.translated == nullptr)
-      {
-	rust_fatal_error (expr->get_locus (), "Failed to lower expr: [%s]",
-			  expr->as_string ().c_str ());
-	return nullptr;
-      }
-
-    resolver.mappings->insert_hir_expr (resolver.translated);
-    resolver.mappings->insert_location (
-      resolver.translated->get_mappings ().get_hirid (), expr->get_locus ());
-
-    if (terminated != nullptr)
-      *terminated = resolver.terminated;
-
-    return resolver.translated;
-  }
-
-  void visit (AST::TupleIndexExpr &expr) override
-  {
-    HIR::Expr *tuple_expr
-      = ASTLoweringExpr::translate (expr.get_tuple_expr ().get (), &terminated);
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::TupleIndexExpr (mapping,
-				 std::unique_ptr<HIR::Expr> (tuple_expr),
-				 expr.get_tuple_index (),
-				 expr.get_outer_attrs (), expr.get_locus ());
-  }
-
-  void visit (AST::TupleExpr &expr) override
-  {
-    std::vector<std::unique_ptr<HIR::Expr> > tuple_elements;
-    for (auto &e : expr.get_tuple_elems ())
-      {
-	HIR::Expr *t = ASTLoweringExpr::translate (e.get ());
-	tuple_elements.push_back (std::unique_ptr<HIR::Expr> (t));
-      }
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::TupleExpr (std::move (mapping), std::move (tuple_elements),
-			    expr.get_inner_attrs (), expr.get_outer_attrs (),
-			    expr.get_locus ());
-  }
-
-  void visit (AST::IfExpr &expr) override
-  {
-    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::IfExprConseqElse &expr) override
-  {
-    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::IfExprConseqIf &expr) override
-  {
-    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::BlockExpr &expr) override
-  {
-    translated = ASTLoweringBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::UnsafeBlockExpr &expr) override
-  {
-    translated = ASTLoweringBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::PathInExpression &expr) override
-  {
-    translated = ASTLowerPathInExpression::translate (&expr);
-  }
-
-  void visit (AST::QualifiedPathInExpression &expr) override
-  {
-    translated = ASTLowerQualPathInExpression::translate (&expr);
-  }
-
-  void visit (AST::ReturnExpr &expr) override
-  {
-    terminated = true;
-    HIR::Expr *return_expr
-      = expr.has_returned_expr ()
-	  ? ASTLoweringExpr::translate (expr.get_returned_expr ().get ())
-	  : nullptr;
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::ReturnExpr (mapping, expr.get_locus (),
-				      std::unique_ptr<HIR::Expr> (return_expr));
-  }
-
-  void visit (AST::CallExpr &expr) override
-  {
-    HIR::Expr *func
-      = ASTLoweringExpr::translate (expr.get_function_expr ().get ());
-
-    auto const &in_params = expr.get_params ();
-    std::vector<std::unique_ptr<HIR::Expr> > params;
-    for (auto &param : in_params)
-      {
-	auto trans = ASTLoweringExpr::translate (param.get ());
-	params.push_back (std::unique_ptr<HIR::Expr> (trans));
-      }
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (
-      crate_num, UNKNOWN_NODEID /* this can map back to the AST*/,
-      mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::CallExpr (mapping, std::unique_ptr<HIR::Expr> (func),
-				    std::move (params), expr.get_outer_attrs (),
-				    expr.get_locus ());
-  }
-
-  void visit (AST::MethodCallExpr &expr) override
-  {
-    HIR::PathExprSegment method_path
-      = lower_path_expr_seg (expr.get_method_name ());
-
-    HIR::Expr *receiver
-      = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
-
-    auto const &in_params = expr.get_params ();
-    std::vector<std::unique_ptr<HIR::Expr> > params;
-    for (auto &param : in_params)
-      {
-	auto trans = ASTLoweringExpr::translate (param.get ());
-	params.push_back (std::unique_ptr<HIR::Expr> (trans));
-      }
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::MethodCallExpr (mapping, std::unique_ptr<HIR::Expr> (receiver),
-				 method_path, std::move (params),
-				 expr.get_outer_attrs (), expr.get_locus ());
-  }
-
-  void visit (AST::AssignmentExpr &expr) override
-  {
-    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
-    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::AssignmentExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
-				 std::unique_ptr<HIR::Expr> (rhs),
-				 expr.get_locus ());
-  }
-
-  void visit (AST::IdentifierExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping1 (crate_num, expr.get_node_id (),
-				    mappings->get_next_hir_id (crate_num),
-				    UNKNOWN_LOCAL_DEFID);
-    Analysis::NodeMapping mapping2 (mapping1);
-
-    HIR::PathIdentSegment ident_seg (expr.get_ident ());
-    HIR::PathExprSegment seg (mapping1, ident_seg, expr.get_locus (),
-			      HIR::GenericArgs::create_empty ());
-    translated = new HIR::PathInExpression (mapping2, {seg}, expr.get_locus (),
-					    false, expr.get_outer_attrs ());
-  }
-
-  void visit (AST::ArrayExpr &expr) override
-  {
-    expr.get_array_elems ()->accept_vis (*this);
-    rust_assert (translated_array_elems != nullptr);
-    HIR::ArrayElems *elems = translated_array_elems;
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::ArrayExpr (mapping, std::unique_ptr<HIR::ArrayElems> (elems),
-			    expr.get_inner_attrs (), expr.get_outer_attrs (),
-			    expr.get_locus ());
-  }
-
-  void visit (AST::ArrayIndexExpr &expr) override
-  {
-    HIR::Expr *array_expr
-      = ASTLoweringExpr::translate (expr.get_array_expr ().get ());
-    HIR::Expr *array_index_expr
-      = ASTLoweringExpr::translate (expr.get_index_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::ArrayIndexExpr (mapping,
-				 std::unique_ptr<HIR::Expr> (array_expr),
-				 std::unique_ptr<HIR::Expr> (array_index_expr),
-				 expr.get_outer_attrs (), expr.get_locus ());
-  }
-
-  void visit (AST::ArrayElemsValues &elems) override
-  {
-    std::vector<std::unique_ptr<HIR::Expr> > elements;
-    for (auto &elem : elems.get_values ())
-      {
-	HIR::Expr *translated_elem = ASTLoweringExpr::translate (elem.get ());
-	elements.push_back (std::unique_ptr<HIR::Expr> (translated_elem));
-      }
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (mappings->get_current_crate (),
-				   elems.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated_array_elems
-      = new HIR::ArrayElemsValues (mapping, std::move (elements));
-  }
-
-  void visit (AST::ArrayElemsCopied &elems) override
-  {
-    HIR::Expr *element
-      = ASTLoweringExpr::translate (elems.get_elem_to_copy ().get ());
-    HIR::Expr *num_copies
-      = ASTLoweringExpr::translate (elems.get_num_copies ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (mappings->get_current_crate (),
-				   elems.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated_array_elems
-      = new HIR::ArrayElemsCopied (mapping,
-				   std::unique_ptr<HIR::Expr> (element),
-				   std::unique_ptr<HIR::Expr> (num_copies));
-  }
-
-  void visit (AST::LiteralExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::Literal l = lower_literal (expr.get_literal ());
-    translated
-      = new HIR::LiteralExpr (mapping, std::move (l), expr.get_locus (),
-			      expr.get_outer_attrs ());
-  }
-
-  void visit (AST::ArithmeticOrLogicalExpr &expr) override
-  {
-    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
-    rust_assert (lhs != nullptr);
-    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
-    rust_assert (rhs != nullptr);
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::ArithmeticOrLogicalExpr (mapping,
-					  std::unique_ptr<HIR::Expr> (lhs),
-					  std::unique_ptr<HIR::Expr> (rhs),
-					  expr.get_expr_type (),
-					  expr.get_locus ());
-  }
-
-  void visit (AST::ComparisonExpr &expr) override
-  {
-    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
-    rust_assert (lhs != nullptr);
-    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
-    rust_assert (rhs != nullptr);
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::ComparisonExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
-				 std::unique_ptr<HIR::Expr> (rhs),
-				 expr.get_expr_type (), expr.get_locus ());
-  }
-
-  void visit (AST::LazyBooleanExpr &expr) override
-  {
-    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
-    rust_assert (lhs != nullptr);
-    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
-    rust_assert (rhs != nullptr);
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::LazyBooleanExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
-				  std::unique_ptr<HIR::Expr> (rhs),
-				  expr.get_expr_type (), expr.get_locus ());
-  }
-
-  void visit (AST::NegationExpr &expr) override
-  {
-    HIR::Expr *negated_value
-      = ASTLoweringExpr::translate (expr.get_negated_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-    translated
-      = new HIR::NegationExpr (mapping,
-			       std::unique_ptr<HIR::Expr> (negated_value),
-			       expr.get_expr_type (), expr.get_outer_attrs (),
-			       expr.get_locus ());
-  }
-
-  void visit (AST::TypeCastExpr &expr) override
-  {
-    HIR::Expr *expr_to_cast_to
-      = ASTLoweringExpr::translate (expr.get_casted_expr ().get ());
-    HIR::Type *type_to_cast_to
-      = lower_type_no_bounds (expr.get_type_to_cast_to ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::TypeCastExpr (mapping,
-			       std::unique_ptr<HIR::Expr> (expr_to_cast_to),
-			       std::unique_ptr<HIR::Type> (type_to_cast_to),
-			       expr.get_locus ());
-  }
-
-  void visit (AST::CompoundAssignmentExpr &expr) override
-  {
-    ArithmeticOrLogicalOperator op;
-    switch (expr.get_expr_type ())
-      {
-      case CompoundAssignmentOperator::ADD:
-	op = ArithmeticOrLogicalOperator::ADD;
-	break;
-      case CompoundAssignmentOperator::SUBTRACT:
-	op = ArithmeticOrLogicalOperator::SUBTRACT;
-	break;
-      case CompoundAssignmentOperator::MULTIPLY:
-	op = ArithmeticOrLogicalOperator::MULTIPLY;
-	break;
-      case CompoundAssignmentOperator::DIVIDE:
-	op = ArithmeticOrLogicalOperator::DIVIDE;
-	break;
-      case CompoundAssignmentOperator::MODULUS:
-	op = ArithmeticOrLogicalOperator::MODULUS;
-	break;
-      case CompoundAssignmentOperator::BITWISE_AND:
-	op = ArithmeticOrLogicalOperator::BITWISE_AND;
-	break;
-      case CompoundAssignmentOperator::BITWISE_OR:
-	op = ArithmeticOrLogicalOperator::BITWISE_OR;
-	break;
-      case CompoundAssignmentOperator::BITWISE_XOR:
-	op = ArithmeticOrLogicalOperator::BITWISE_XOR;
-	break;
-      case CompoundAssignmentOperator::LEFT_SHIFT:
-	op = ArithmeticOrLogicalOperator::LEFT_SHIFT;
-	break;
-      case CompoundAssignmentOperator::RIGHT_SHIFT:
-	op = ArithmeticOrLogicalOperator::RIGHT_SHIFT;
-	break;
-      default:
-	gcc_unreachable ();
-      }
-
-    HIR::Expr *asignee_expr
-      = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
-    HIR::Expr *value
-      = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::CompoundAssignmentExpr (
-      mapping, std::unique_ptr<HIR::Expr> (asignee_expr),
-      std::unique_ptr<HIR::Expr> (value), op, expr.get_locus ());
-  }
-
-  void visit (AST::StructExprStruct &struct_expr) override
-  {
-    HIR::PathInExpression *path
-      = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
-    HIR::PathInExpression copied_path (*path);
-    delete path;
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::StructExprStruct (mapping, copied_path,
-					    struct_expr.get_inner_attrs (),
-					    struct_expr.get_outer_attrs (),
-					    struct_expr.get_locus ());
-  }
-
-  void visit (AST::StructExprStructFields &struct_expr) override
-  {
-    // bit of a hack for now
-    HIR::PathInExpression *path
-      = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
-    HIR::PathInExpression copied_path (*path);
-    delete path;
-
-    HIR::StructBase *base = nullptr;
-    if (struct_expr.has_struct_base ())
-      {
-	HIR::Expr *translated_base = ASTLoweringExpr::translate (
-	  struct_expr.get_struct_base ().get_base_struct ().get ());
-	base
-	  = new HIR::StructBase (std::unique_ptr<HIR::Expr> (translated_base));
-      }
-
-    auto const &in_fields = struct_expr.get_fields ();
-    std::vector<std::unique_ptr<HIR::StructExprField> > fields;
-    for (auto &field : in_fields)
-      {
-	HIR::StructExprField *translated
-	  = ASTLowerStructExprField::translate (field.get ());
-	fields.push_back (std::unique_ptr<HIR::StructExprField> (translated));
-      }
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::StructExprStructFields (
-      mapping, copied_path, std::move (fields), struct_expr.get_locus (), base,
-      struct_expr.get_inner_attrs (), struct_expr.get_outer_attrs ());
-  }
-
-  void visit (AST::GroupedExpr &expr) override
-  {
-    HIR::Expr *paren_expr
-      = ASTLoweringExpr::translate (expr.get_expr_in_parens ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::GroupedExpr (mapping, std::unique_ptr<HIR::Expr> (paren_expr),
-			      expr.get_inner_attrs (), expr.get_outer_attrs (),
-			      expr.get_locus ());
-  }
-
-  void visit (AST::FieldAccessExpr &expr) override
-  {
-    HIR::Expr *receiver
-      = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-    translated
-      = new HIR::FieldAccessExpr (mapping,
-				  std::unique_ptr<HIR::Expr> (receiver),
-				  expr.get_field_name (),
-				  expr.get_outer_attrs (), expr.get_locus ());
-  }
-
-  void visit (AST::LoopExpr &expr) override
-  {
-    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::WhileLoopExpr &expr) override
-  {
-    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::ForLoopExpr &expr) override
-  {
-    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::BreakExpr &expr) override
-  {
-    HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
-    HIR::Expr *break_expr
-      = expr.has_break_expr ()
-	  ? ASTLoweringExpr::translate (expr.get_break_expr ().get ())
-	  : nullptr;
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::BreakExpr (mapping, expr.get_locus (),
-				     std ::move (break_label),
-				     std::unique_ptr<HIR::Expr> (break_expr),
-				     expr.get_outer_attrs ());
-  }
-
-  void visit (AST::ContinueExpr &expr) override
-  {
-    HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::ContinueExpr (mapping, expr.get_locus (),
-					std ::move (break_label),
-					expr.get_outer_attrs ());
-  }
-
-  void visit (AST::BorrowExpr &expr) override
-  {
-    HIR::Expr *borrow_lvalue
-      = ASTLoweringExpr::translate (expr.get_borrowed_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::BorrowExpr *borrow_expr
-      = new HIR::BorrowExpr (mapping,
-			     std::unique_ptr<HIR::Expr> (borrow_lvalue),
-			     expr.get_is_mut () ? Mutability::Mut
-						: Mutability::Imm,
-			     expr.get_outer_attrs (), expr.get_locus ());
-
-    if (expr.get_is_double_borrow ())
-      {
-	NodeId artifical_bouble_borrow_id = mappings->get_next_node_id ();
-	Analysis::NodeMapping mapping (crate_num, artifical_bouble_borrow_id,
-				       mappings->get_next_hir_id (crate_num),
-				       UNKNOWN_LOCAL_DEFID);
-
-	borrow_expr
-	  = new HIR::BorrowExpr (mapping,
-				 std::unique_ptr<HIR::Expr> (borrow_expr),
-				 expr.get_is_mut () ? Mutability::Mut
-						    : Mutability::Imm,
-				 expr.get_outer_attrs (), expr.get_locus ());
-      }
-
-    translated = borrow_expr;
-  }
-
-  void visit (AST::DereferenceExpr &expr) override
-  {
-    HIR::Expr *dref_lvalue
-      = ASTLoweringExpr::translate (expr.get_dereferenced_expr ().get ());
-
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated
-      = new HIR::DereferenceExpr (mapping,
-				  std::unique_ptr<HIR::Expr> (dref_lvalue),
-				  expr.get_outer_attrs (), expr.get_locus ());
-  }
-
-  void visit (AST::MatchExpr &expr) override
-  {
-    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
-  }
-
-  void visit (AST::RangeFromToExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::Expr *range_from
-      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
-    HIR::Expr *range_to
-      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
-
-    translated
-      = new HIR::RangeFromToExpr (mapping,
-				  std::unique_ptr<HIR::Expr> (range_from),
-				  std::unique_ptr<HIR::Expr> (range_to),
-				  expr.get_locus ());
-  }
-
-  void visit (AST::RangeFromExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::Expr *range_from
-      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
-
-    translated
-      = new HIR::RangeFromExpr (mapping,
-				std::unique_ptr<HIR::Expr> (range_from),
-				expr.get_locus ());
-  }
-
-  void visit (AST::RangeToExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::Expr *range_to
-      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
-
-    translated
-      = new HIR::RangeToExpr (mapping, std::unique_ptr<HIR::Expr> (range_to),
-			      expr.get_locus ());
-  }
-
-  void visit (AST::RangeFullExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    translated = new HIR::RangeFullExpr (mapping, expr.get_locus ());
-  }
-
-  void visit (AST::RangeFromToInclExpr &expr) override
-  {
-    auto crate_num = mappings->get_current_crate ();
-    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
-				   mappings->get_next_hir_id (crate_num),
-				   UNKNOWN_LOCAL_DEFID);
-
-    HIR::Expr *range_from
-      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
-    HIR::Expr *range_to
-      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
-
-    translated
-      = new HIR::RangeFromToInclExpr (mapping,
-				      std::unique_ptr<HIR::Expr> (range_from),
-				      std::unique_ptr<HIR::Expr> (range_to),
-				      expr.get_locus ());
-  }
+  static HIR::Expr *translate (AST::Expr *expr, bool *terminated = nullptr);
+
+  void visit (AST::TupleIndexExpr &expr) override;
+  void visit (AST::TupleExpr &expr) override;
+  void visit (AST::IfExpr &expr) override;
+  void visit (AST::IfExprConseqElse &expr) override;
+  void visit (AST::IfExprConseqIf &expr) override;
+  void visit (AST::BlockExpr &expr) override;
+  void visit (AST::UnsafeBlockExpr &expr) override;
+  void visit (AST::PathInExpression &expr) override;
+  void visit (AST::QualifiedPathInExpression &expr) override;
+  void visit (AST::ReturnExpr &expr) override;
+  void visit (AST::CallExpr &expr) override;
+  void visit (AST::MethodCallExpr &expr) override;
+  void visit (AST::AssignmentExpr &expr) override;
+  void visit (AST::IdentifierExpr &expr) override;
+  void visit (AST::ArrayExpr &expr) override;
+  void visit (AST::ArrayIndexExpr &expr) override;
+  void visit (AST::ArrayElemsValues &elems) override;
+  void visit (AST::ArrayElemsCopied &elems) override;
+  void visit (AST::LiteralExpr &expr) override;
+  void visit (AST::ArithmeticOrLogicalExpr &expr) override;
+  void visit (AST::ComparisonExpr &expr) override;
+  void visit (AST::LazyBooleanExpr &expr) override;
+  void visit (AST::NegationExpr &expr) override;
+  void visit (AST::TypeCastExpr &expr) override;
+  void visit (AST::CompoundAssignmentExpr &expr) override;
+  void visit (AST::StructExprStruct &struct_expr) override;
+  void visit (AST::StructExprStructFields &struct_expr) override;
+  void visit (AST::GroupedExpr &expr) override;
+  void visit (AST::FieldAccessExpr &expr) override;
+  void visit (AST::LoopExpr &expr) override;
+  void visit (AST::WhileLoopExpr &expr) override;
+  void visit (AST::ForLoopExpr &expr) override;
+  void visit (AST::BreakExpr &expr) override;
+  void visit (AST::ContinueExpr &expr) override;
+  void visit (AST::BorrowExpr &expr) override;
+  void visit (AST::DereferenceExpr &expr) override;
+  void visit (AST::MatchExpr &expr) override;
+  void visit (AST::RangeFromToExpr &expr) override;
+  void visit (AST::RangeFromExpr &expr) override;
+  void visit (AST::RangeToExpr &expr) override;
+  void visit (AST::RangeFullExpr &expr) override;
+  void visit (AST::RangeFromToInclExpr &expr) override;
+  void visit (AST::ClosureExprInner &expr) override;
+  void visit (AST::ClosureExprInnerTyped &expr) override;
 
 private:
-  ASTLoweringExpr ()
-    : ASTLoweringBase (), translated (nullptr),
-      translated_array_elems (nullptr), terminated (false)
-  {}
+  ASTLoweringExpr ();
 
   HIR::Expr *translated;
   HIR::ArrayElems *translated_array_elems;
diff --git a/gcc/rust/hir/rust-ast-lower.cc b/gcc/rust/hir/rust-ast-lower.cc
index 4f1bfd1f0c1..fdf8abe3ed3 100644
--- a/gcc/rust/hir/rust-ast-lower.cc
+++ b/gcc/rust/hir/rust-ast-lower.cc
@@ -22,9 +22,12 @@
 #include "rust-ast-lower-expr.h"
 #include "rust-ast-lower-block.h"
 #include "rust-ast-lower-type.h"
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-struct-field-expr.h"
 
 namespace Rust {
 namespace HIR {
+using HIR::ClosureParam;
 
 Visibility
 translate_visibility (const AST::Visibility &vis)
@@ -473,5 +476,25 @@ ASTLowerQualPathInExpression::visit (AST::QualifiedPathInExpression &expr)
 						   expr.get_locus (),
 						   expr.get_outer_attrs ());
 }
+
+ClosureParam
+ASTLoweringBase::lower_closure_param (AST::ClosureParam &param)
+{
+  HIR::Pattern *param_pattern
+    = ASTLoweringPattern::translate (param.get_pattern ().get ());
+
+  HIR::Type *param_type
+    = param.has_type_given ()
+	? ASTLoweringType::translate (param.get_type ().get ())
+	: nullptr;
+
+  return HIR::ClosureParam (std::unique_ptr<HIR::Pattern> (param_pattern),
+			    param.get_locus (),
+			    param.has_type_given ()
+			      ? std::unique_ptr<HIR::Type> (param_type)
+			      : nullptr,
+			    param.get_outer_attrs ());
+}
+
 } // namespace HIR
 } // namespace Rust
diff --git a/gcc/rust/hir/rust-hir-dump.cc b/gcc/rust/hir/rust-hir-dump.cc
index 6bbd7ef3a7f..261a36daa05 100644
--- a/gcc/rust/hir/rust-hir-dump.cc
+++ b/gcc/rust/hir/rust-hir-dump.cc
@@ -197,7 +197,7 @@ void
 Dump::visit (FieldAccessExpr &)
 {}
 void
-Dump::visit (ClosureExprInner &)
+Dump::visit (ClosureExpr &)
 {}
 void
 Dump::visit (BlockExpr &block_expr)
@@ -212,9 +212,7 @@ Dump::visit (BlockExpr &block_expr)
   stream << "]";
   indent--;
 }
-void
-Dump::visit (ClosureExprInnerTyped &)
-{}
+
 void
 Dump::visit (ContinueExpr &)
 {}
diff --git a/gcc/rust/hir/rust-hir-dump.h b/gcc/rust/hir/rust-hir-dump.h
index ead8d774b03..8715a95ada8 100644
--- a/gcc/rust/hir/rust-hir-dump.h
+++ b/gcc/rust/hir/rust-hir-dump.h
@@ -78,9 +78,8 @@ private:
   virtual void visit (CallExpr &) override;
   virtual void visit (MethodCallExpr &) override;
   virtual void visit (FieldAccessExpr &) override;
-  virtual void visit (ClosureExprInner &) override;
+  virtual void visit (ClosureExpr &) override;
   virtual void visit (BlockExpr &) override;
-  virtual void visit (ClosureExprInnerTyped &) override;
   virtual void visit (ContinueExpr &) override;
   virtual void visit (BreakExpr &) override;
   virtual void visit (RangeFromToExpr &) override;
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h
index 564d5215724..4c5caf17ac3 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.h
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -1985,21 +1985,22 @@ protected:
 struct ClosureParam
 {
 private:
+  std::vector<AST::Attribute> outer_attrs;
   std::unique_ptr<Pattern> pattern;
-
-  // bool has_type_given;
   std::unique_ptr<Type> type;
-
-  // TODO: should this store location data?
+  Location locus;
 
 public:
   // Returns whether the type of the parameter has been given.
   bool has_type_given () const { return type != nullptr; }
 
   // Constructor for closure parameter
-  ClosureParam (std::unique_ptr<Pattern> param_pattern,
-		std::unique_ptr<Type> param_type = nullptr)
-    : pattern (std::move (param_pattern)), type (std::move (param_type))
+  ClosureParam (std::unique_ptr<Pattern> param_pattern, Location locus,
+		std::unique_ptr<Type> param_type = nullptr,
+		std::vector<AST::Attribute> outer_attrs = {})
+    : outer_attrs (std::move (outer_attrs)),
+      pattern (std::move (param_pattern)), type (std::move (param_type)),
+      locus (locus)
   {}
 
   // Copy constructor required due to cloning as a result of unique_ptrs
@@ -2007,6 +2008,8 @@ public:
     : pattern (other.pattern->clone_pattern ())
   {
     // guard to protect from null pointer dereference
+    if (other.pattern != nullptr)
+      pattern = other.pattern->clone_pattern ();
     if (other.type != nullptr)
       type = other.type->clone_type ();
   }
@@ -2016,8 +2019,17 @@ public:
   // Assignment operator must be overloaded to clone as well
   ClosureParam &operator= (ClosureParam const &other)
   {
-    pattern = other.pattern->clone_pattern ();
-    type = other.type->clone_type ();
+    outer_attrs = other.outer_attrs;
+
+    // guard to protect from null pointer dereference
+    if (other.pattern != nullptr)
+      pattern = other.pattern->clone_pattern ();
+    else
+      pattern = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
 
     return *this;
   }
@@ -2026,31 +2038,79 @@ public:
   ClosureParam (ClosureParam &&other) = default;
   ClosureParam &operator= (ClosureParam &&other) = default;
 
-  // Returns whether closure parameter is in an error state.
-  bool is_error () const { return pattern == nullptr; }
+  std::string as_string () const;
 
-  // Creates an error state closure parameter.
-  static ClosureParam create_error () { return ClosureParam (nullptr); }
+  const std::vector<AST::Attribute> &get_outer_attrs () const
+  {
+    return outer_attrs;
+  }
+  std::vector<AST::Attribute> &get_outer_attrs () { return outer_attrs; }
 
-  std::string as_string () const;
+  std::unique_ptr<Pattern> &get_pattern ()
+  {
+    rust_assert (pattern != nullptr);
+    return pattern;
+  }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type_given ());
+    return type;
+  }
+
+  Location get_locus () const { return locus; }
 };
 
 // Base closure definition expression HIR node - abstract
 class ClosureExpr : public ExprWithoutBlock
 {
+private:
   bool has_move;
   std::vector<ClosureParam> params;
   Location locus;
+  std::unique_ptr<Type> return_type;
+  std::unique_ptr<Expr> expr;
 
-protected:
+public:
   ClosureExpr (Analysis::NodeMapping mappings,
-	       std::vector<ClosureParam> closure_params, bool has_move,
+	       std::vector<ClosureParam> closure_params,
+	       std::unique_ptr<Type> closure_return_type,
+	       std::unique_ptr<Expr> closure_expr, bool has_move,
 	       AST::AttrVec outer_attribs, Location locus)
     : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
-      has_move (has_move), params (std::move (closure_params)), locus (locus)
+      has_move (has_move), params (std::move (closure_params)), locus (locus),
+      return_type (std::move (closure_return_type)),
+      expr (std::move (closure_expr))
   {}
 
-public:
+  // Copy constructor requires cloning
+  ClosureExpr (ClosureExpr const &other)
+    : ExprWithoutBlock (other.get_mappings (), other.get_outer_attrs ())
+  {
+    return_type
+      = other.has_return_type () ? other.return_type->clone_type () : nullptr;
+    expr = other.expr->clone_expr ();
+    params = other.params;
+    has_move = other.has_move;
+  }
+
+  // Overload assignment operator to clone unique_ptrs
+  ClosureExpr &operator= (ClosureExpr const &other)
+  {
+    mappings = other.mappings;
+    return_type
+      = other.has_return_type () ? other.return_type->clone_type () : nullptr;
+    expr = other.expr->clone_expr ();
+    params = other.params;
+    has_move = other.has_move;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureExpr (ClosureExpr &&other) = default;
+  ClosureExpr &operator= (ClosureExpr &&other) = default;
+
   std::string as_string () const override;
 
   Location get_locus () const override final { return locus; }
@@ -2059,47 +2119,17 @@ public:
   {
     return ExprType::Closure;
   }
-};
 
-// Represents a non-type-specified closure expression HIR node
-class ClosureExprInner : public ClosureExpr
-{
-  std::unique_ptr<Expr> closure_inner;
+  bool get_has_move () const { return has_move; }
 
-public:
-  std::string as_string () const override;
+  bool has_return_type () const { return return_type != nullptr; }
 
-  // Constructor for a ClosureExprInner
-  ClosureExprInner (Analysis::NodeMapping mappings,
-		    std::unique_ptr<Expr> closure_inner_expr,
-		    std::vector<ClosureParam> closure_params, Location locus,
-		    bool is_move = false,
-		    AST::AttrVec outer_attribs = AST::AttrVec ())
-    : ClosureExpr (std::move (mappings), std::move (closure_params), is_move,
-		   std::move (outer_attribs), locus),
-      closure_inner (std::move (closure_inner_expr))
-  {}
-
-  // Copy constructor must be defined to allow copying via cloning of unique_ptr
-  ClosureExprInner (ClosureExprInner const &other)
-    : ClosureExpr (other), closure_inner (other.closure_inner->clone_expr ())
-  {}
-
-  // Overload assignment operator to clone closure_inner
-  ClosureExprInner &operator= (ClosureExprInner const &other)
+  std::unique_ptr<Type> &get_return_type ()
   {
-    ClosureExpr::operator= (other);
-    closure_inner = other.closure_inner->clone_expr ();
-    // params = other.params;
-    // has_move = other.has_move;
-    // outer_attrs = other.outer_attrs;
-
-    return *this;
-  }
-
-  // move constructors
-  ClosureExprInner (ClosureExprInner &&other) = default;
-  ClosureExprInner &operator= (ClosureExprInner &&other) = default;
+    rust_assert (has_return_type ());
+    return return_type;
+  };
+  std::unique_ptr<Expr> &get_expr () { return expr; }
 
   void accept_vis (HIRFullVisitor &vis) override;
   void accept_vis (HIRExpressionVisitor &vis) override;
@@ -2107,16 +2137,16 @@ public:
 protected:
   /* Use covariance to implement clone function as returning this object rather
    * than base */
-  ClosureExprInner *clone_expr_impl () const override
+  ClosureExpr *clone_expr_impl () const override
   {
-    return new ClosureExprInner (*this);
+    return new ClosureExpr (*this);
   }
 
   /* Use covariance to implement clone function as returning this object rather
    * than base */
-  ClosureExprInner *clone_expr_without_block_impl () const override
+  ClosureExpr *clone_expr_without_block_impl () const override
   {
-    return new ClosureExprInner (*this);
+    return new ClosureExpr (*this);
   }
 };
 
@@ -2239,71 +2269,6 @@ protected:
   }
 };
 
-// Represents a type-specified closure expression HIR node
-class ClosureExprInnerTyped : public ClosureExpr
-{
-  std::unique_ptr<Type> return_type;
-  std::unique_ptr<BlockExpr>
-    expr; // only used because may be polymorphic in future
-
-public:
-  std::string as_string () const override;
-
-  // Constructor potentially with a move
-  ClosureExprInnerTyped (Analysis::NodeMapping mappings,
-			 std::unique_ptr<Type> closure_return_type,
-			 std::unique_ptr<BlockExpr> closure_expr,
-			 std::vector<ClosureParam> closure_params,
-			 Location locus, bool is_move = false,
-			 AST::AttrVec outer_attribs = AST::AttrVec ())
-    : ClosureExpr (std::move (mappings), std::move (closure_params), is_move,
-		   std::move (outer_attribs), locus),
-      return_type (std::move (closure_return_type)),
-      expr (std::move (closure_expr))
-  {}
-
-  // Copy constructor requires cloning
-  ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
-    : ClosureExpr (other), return_type (other.return_type->clone_type ()),
-      expr (other.expr->clone_block_expr ())
-  {}
-
-  // Overload assignment operator to clone unique_ptrs
-  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other)
-  {
-    ClosureExpr::operator= (other);
-    return_type = other.return_type->clone_type ();
-    expr = other.expr->clone_block_expr ();
-    // params = other.params;
-    // has_move = other.has_move;
-    // outer_attrs = other.outer_attrs;
-
-    return *this;
-  }
-
-  // move constructors
-  ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default;
-  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default;
-
-  void accept_vis (HIRFullVisitor &vis) override;
-  void accept_vis (HIRExpressionVisitor &vis) override;
-
-protected:
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
-  ClosureExprInnerTyped *clone_expr_impl () const override
-  {
-    return new ClosureExprInnerTyped (*this);
-  }
-
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
-  ClosureExprInnerTyped *clone_expr_without_block_impl () const override
-  {
-    return new ClosureExprInnerTyped (*this);
-  }
-};
-
 // HIR node representing continue expression within loops
 class ContinueExpr : public ExprWithoutBlock
 {
diff --git a/gcc/rust/hir/tree/rust-hir-full-decls.h b/gcc/rust/hir/tree/rust-hir-full-decls.h
index 3cb3ffeecee..70ee7538298 100644
--- a/gcc/rust/hir/tree/rust-hir-full-decls.h
+++ b/gcc/rust/hir/tree/rust-hir-full-decls.h
@@ -94,9 +94,7 @@ class MethodCallExpr;
 class FieldAccessExpr;
 struct ClosureParam;
 class ClosureExpr;
-class ClosureExprInner;
 class BlockExpr;
-class ClosureExprInnerTyped;
 class ContinueExpr;
 class BreakExpr;
 class RangeExpr;
diff --git a/gcc/rust/hir/tree/rust-hir-full-test.cc b/gcc/rust/hir/tree/rust-hir-full-test.cc
index 48af16a45e6..f6e27b9a35f 100644
--- a/gcc/rust/hir/tree/rust-hir-full-test.cc
+++ b/gcc/rust/hir/tree/rust-hir-full-test.cc
@@ -1133,15 +1133,8 @@ ClosureExpr::as_string () const
 	}
     }
 
-  return str;
-}
-
-std::string
-ClosureExprInnerTyped::as_string () const
-{
-  std::string str = ClosureExpr::as_string ();
-
-  str += "\n Return type: " + return_type->as_string ();
+  str += "\n Return type: "
+	 + (has_return_type () ? return_type->as_string () : "none");
 
   str += "\n Body: " + expr->as_string ();
 
@@ -1515,16 +1508,6 @@ UnsafeBlockExpr::as_string () const
   return str + "\n" + indent_spaces (out) + "}\n" + expr->as_string ();
 }
 
-std::string
-ClosureExprInner::as_string () const
-{
-  std::string str = ClosureExpr::as_string ();
-
-  str += "\n Expression: " + closure_inner->as_string ();
-
-  return str;
-}
-
 std::string
 IfExpr::as_string () const
 {
@@ -4038,7 +4021,7 @@ FieldAccessExpr::accept_vis (HIRFullVisitor &vis)
 }
 
 void
-ClosureExprInner::accept_vis (HIRFullVisitor &vis)
+ClosureExpr::accept_vis (HIRFullVisitor &vis)
 {
   vis.visit (*this);
 }
@@ -4049,12 +4032,6 @@ BlockExpr::accept_vis (HIRFullVisitor &vis)
   vis.visit (*this);
 }
 
-void
-ClosureExprInnerTyped::accept_vis (HIRFullVisitor &vis)
-{
-  vis.visit (*this);
-}
-
 void
 ContinueExpr::accept_vis (HIRFullVisitor &vis)
 {
@@ -4986,7 +4963,7 @@ IfExpr::accept_vis (HIRExpressionVisitor &vis)
 }
 
 void
-ClosureExprInner::accept_vis (HIRExpressionVisitor &vis)
+ClosureExpr::accept_vis (HIRExpressionVisitor &vis)
 {
   vis.visit (*this);
 }
@@ -5075,12 +5052,6 @@ QualifiedPathInExpression::accept_vis (HIRPatternVisitor &vis)
   vis.visit (*this);
 }
 
-void
-ClosureExprInnerTyped::accept_vis (HIRExpressionVisitor &vis)
-{
-  vis.visit (*this);
-}
-
 void
 ExprStmtWithBlock::accept_vis (HIRStmtVisitor &vis)
 {
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.h b/gcc/rust/hir/tree/rust-hir-visitor.h
index 187f0794ac1..ba6cad7614b 100644
--- a/gcc/rust/hir/tree/rust-hir-visitor.h
+++ b/gcc/rust/hir/tree/rust-hir-visitor.h
@@ -63,9 +63,8 @@ public:
   virtual void visit (CallExpr &expr) = 0;
   virtual void visit (MethodCallExpr &expr) = 0;
   virtual void visit (FieldAccessExpr &expr) = 0;
-  virtual void visit (ClosureExprInner &expr) = 0;
   virtual void visit (BlockExpr &expr) = 0;
-  virtual void visit (ClosureExprInnerTyped &expr) = 0;
+  virtual void visit (ClosureExpr &expr) = 0;
   virtual void visit (ContinueExpr &expr) = 0;
   virtual void visit (BreakExpr &expr) = 0;
   virtual void visit (RangeFromToExpr &expr) = 0;
@@ -206,9 +205,8 @@ public:
   virtual void visit (CallExpr &) override {}
   virtual void visit (MethodCallExpr &) override {}
   virtual void visit (FieldAccessExpr &) override {}
-  virtual void visit (ClosureExprInner &) override {}
+  virtual void visit (ClosureExpr &) override {}
   virtual void visit (BlockExpr &) override {}
-  virtual void visit (ClosureExprInnerTyped &) override {}
   virtual void visit (ContinueExpr &) override {}
   virtual void visit (BreakExpr &) override {}
   virtual void visit (RangeFromToExpr &) override {}
@@ -419,8 +417,7 @@ public:
 
   virtual void visit (HIR::QualifiedPathInExpression &expr) = 0;
   virtual void visit (HIR::PathInExpression &expr) = 0;
-  virtual void visit (ClosureExprInnerTyped &) = 0;
-  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (ClosureExpr &) = 0;
   virtual void visit (StructExprStructFields &) = 0;
   virtual void visit (StructExprStruct &) = 0;
   virtual void visit (LiteralExpr &expr) = 0;
diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h
index aa305f1590c..314aafb32d3 100644
--- a/gcc/rust/hir/tree/rust-hir.h
+++ b/gcc/rust/hir/tree/rust-hir.h
@@ -238,6 +238,7 @@ class ExprWithoutBlock;
 // Base expression HIR node - abstract
 class Expr : public Node
 {
+protected:
   AST::AttrVec outer_attrs;
   Analysis::NodeMapping mappings;
 
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index 4e377d52a0f..6b8c63b77f6 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -1418,6 +1418,12 @@ TypeCheckExpr::visit (HIR::MatchExpr &expr)
     }
 }
 
+void
+TypeCheckExpr::visit (HIR::ClosureExpr &expr)
+{
+  gcc_unreachable ();
+}
+
 bool
 TypeCheckExpr::resolve_operator_overload (
   Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
index 8a37512ec6e..7f787fa9766 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -69,10 +69,9 @@ public:
   void visit (HIR::RangeFullExpr &expr) override;
   void visit (HIR::RangeFromToInclExpr &expr) override;
   void visit (HIR::WhileLoopExpr &expr) override;
+  void visit (HIR::ClosureExpr &expr) override;
 
   // TODO
-  void visit (HIR::ClosureExprInnerTyped &) override {}
-  void visit (HIR::ClosureExprInner &expr) override {}
   void visit (HIR::ErrorPropagationExpr &expr) override {}
   void visit (HIR::RangeToInclExpr &expr) override {}
   void visit (HIR::WhileLetLoopExpr &expr) override {}
