diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h
new file mode 100644
index 00000000000..83278529646
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -0,0 +1,4194 @@
+// 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/>.
+
+#ifndef RUST_HIR_EXPR_H
+#define RUST_HIR_EXPR_H
+
+#include "rust-common.h"
+#include "rust-ast-full-decls.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+#include "operator.h"
+
+namespace Rust {
+namespace HIR {
+
+// HIR node for an expression with an accompanying block - abstract
+class ExprWithBlock : public Expr
+{
+  // TODO: should this mean that a BlockExpr should be a member variable?
+protected:
+  ExprWithBlock (Analysis::NodeMapping mappings,
+		 AST::AttrVec outer_attrs = AST::AttrVec ())
+    : Expr (std::move (mappings), std::move (outer_attrs))
+  {}
+
+  // pure virtual clone implementation
+  virtual ExprWithBlock *clone_expr_with_block_impl () const = 0;
+
+  // prevent having to define multiple clone expressions
+  ExprWithBlock *clone_expr_impl () const override
+  {
+    return clone_expr_with_block_impl ();
+  }
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
+  {
+    return std::unique_ptr<ExprWithBlock> (clone_expr_with_block_impl ());
+  }
+
+  BlockType get_block_expr_type () const final override
+  {
+    return BlockType::WITH_BLOCK;
+  };
+};
+
+// Literals? Or literal base?
+class LiteralExpr : public ExprWithoutBlock
+{
+  Literal literal;
+  Location locus;
+
+public:
+  std::string as_string () const override
+  {
+    return "( " + literal.as_string () + " (" + get_mappings ().as_string ()
+	   + "))";
+  }
+
+  Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
+
+  LiteralExpr (Analysis::NodeMapping mappings, std::string value_as_string,
+	       Literal::LitType type, PrimitiveCoreType type_hint,
+	       Location locus, AST::AttrVec outer_attrs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      literal (std::move (value_as_string), type, type_hint), locus (locus)
+  {}
+
+  LiteralExpr (Analysis::NodeMapping mappings, Literal literal, Location locus,
+	       AST::AttrVec outer_attrs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      literal (std::move (literal)), locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<LiteralExpr> clone_literal_expr () const
+  {
+    return std::unique_ptr<LiteralExpr> (clone_literal_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Literal &get_literal () { return literal; }
+  const Literal &get_literal () const { return literal; }
+
+  ExprType get_expression_type () const override final { return ExprType::Lit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LiteralExpr *clone_expr_impl () const override
+  {
+    return new LiteralExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LiteralExpr *clone_expr_without_block_impl () const override
+  {
+    return new LiteralExpr (*this);
+  }
+
+  /* not virtual as currently no subclasses of LiteralExpr, but could be in
+   * future */
+  /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
+  {
+    return new LiteralExpr (*this);
+  }
+};
+
+/* Represents an expression using unary or binary operators as HIR node. Can be
+ * overloaded. */
+class OperatorExpr : public ExprWithoutBlock
+{
+  // TODO: create binary and unary operator subclasses?
+public:
+  Location locus;
+
+protected:
+  /* Variable must be protected to allow derived classes to use it as a first
+   * class citizen */
+  std::unique_ptr<Expr> main_or_left_expr;
+
+  // Constructor (only for initialisation of expr purposes)
+  OperatorExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> main_or_left_expr,
+		AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      locus (locus), main_or_left_expr (std::move (main_or_left_expr))
+  {}
+
+  // Copy constructor (only for initialisation of expr purposes)
+  OperatorExpr (OperatorExpr const &other)
+    : ExprWithoutBlock (other), locus (other.locus),
+      main_or_left_expr (other.main_or_left_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy expr
+  OperatorExpr &operator= (OperatorExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    main_or_left_expr = other.main_or_left_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  OperatorExpr (OperatorExpr &&other) = default;
+  OperatorExpr &operator= (OperatorExpr &&other) = default;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<Expr> &get_expr () { return main_or_left_expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Operator;
+  }
+};
+
+/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
+ * overloaded. */
+class BorrowExpr : public OperatorExpr
+{
+  Mutability mut;
+  bool double_borrow;
+
+public:
+  std::string as_string () const override;
+
+  BorrowExpr (Analysis::NodeMapping mappings,
+	      std::unique_ptr<Expr> borrow_lvalue, Mutability mut,
+	      bool is_double_borrow, AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (borrow_lvalue),
+		    std::move (outer_attribs), locus),
+      mut (mut), double_borrow (is_double_borrow)
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Mutability get_mut () const { return mut; }
+  bool is_mut () const { return mut == Mutability::Mut; }
+  bool get_is_double_borrow () const { return double_borrow; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BorrowExpr *clone_expr_impl () const override
+  {
+    return new BorrowExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BorrowExpr *clone_expr_without_block_impl () const override
+  {
+    return new BorrowExpr (*this);
+  }
+};
+
+// Unary prefix * deference operator
+class DereferenceExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  DereferenceExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> deref_lvalue,
+		   AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (deref_lvalue),
+		    std::move (outer_attribs), locus)
+  {}
+
+  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 */
+  DereferenceExpr *clone_expr_impl () const override
+  {
+    return new DereferenceExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  DereferenceExpr *clone_expr_without_block_impl () const override
+  {
+    return new DereferenceExpr (*this);
+  }
+};
+
+// Unary postfix ? error propogation operator. Cannot be overloaded.
+class ErrorPropagationExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  ErrorPropagationExpr (Analysis::NodeMapping mappings,
+			std::unique_ptr<Expr> potential_error_value,
+			AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (potential_error_value),
+		    std::move (outer_attribs), locus)
+  {}
+
+  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 */
+  ErrorPropagationExpr *clone_expr_impl () const override
+  {
+    return new ErrorPropagationExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ErrorPropagationExpr *clone_expr_without_block_impl () const override
+  {
+    return new ErrorPropagationExpr (*this);
+  }
+};
+
+// Unary prefix - or ! negation or NOT operators.
+class NegationExpr : public OperatorExpr
+{
+public:
+  using ExprType = NegationOperator;
+
+private:
+  /* Note: overload negation via std::ops::Neg and not via std::ops::Not
+   * Negation only works for signed integer and floating-point types, NOT only
+   * works for boolean and integer types (via bitwise NOT) */
+  ExprType expr_type;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  NegationExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> negated_value, ExprType expr_kind,
+		AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (negated_value),
+		    std::move (outer_attribs), locus),
+      expr_type (expr_kind)
+  {}
+
+  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 */
+  NegationExpr *clone_expr_impl () const override
+  {
+    return new NegationExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NegationExpr *clone_expr_without_block_impl () const override
+  {
+    return new NegationExpr (*this);
+  }
+};
+
+// Infix binary operators. +, -, *, /, %, &, |, ^, <<, >>
+class ArithmeticOrLogicalExpr : public OperatorExpr
+{
+public:
+  using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  ArithmeticOrLogicalExpr (Analysis::NodeMapping mappings,
+			   std::unique_ptr<Expr> left_value,
+			   std::unique_ptr<Expr> right_value,
+			   ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_value),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor - probably required due to unique pointer
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default;
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other)
+    = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArithmeticOrLogicalExpr *clone_expr_impl () const override
+  {
+    return new ArithmeticOrLogicalExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArithmeticOrLogicalExpr (*this);
+  }
+};
+
+// Infix binary comparison operators. ==, !=, <, <=, >, >=
+class ComparisonExpr : public OperatorExpr
+{
+public:
+  using ExprType = ComparisonOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor requires pointers for polymorphism
+  ComparisonExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> left_value,
+		  std::unique_ptr<Expr> right_value, ExprType comparison_kind,
+		  Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_value),
+		    AST::AttrVec (), locus),
+      expr_type (comparison_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  ComparisonExpr (ComparisonExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  ComparisonExpr &operator= (ComparisonExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ComparisonExpr (ComparisonExpr &&other) = default;
+  ComparisonExpr &operator= (ComparisonExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+  ExprType get_kind () { return expr_type; }
+
+  /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2)
+   * maybe? */
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ComparisonExpr *clone_expr_impl () const override
+  {
+    return new ComparisonExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ComparisonExpr *clone_expr_without_block_impl () const override
+  {
+    return new ComparisonExpr (*this);
+  }
+};
+
+// Infix binary lazy boolean logical operators && and ||.
+class LazyBooleanExpr : public OperatorExpr
+{
+public:
+  using ExprType = LazyBooleanOperator;
+
+private:
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  // Constructor calls OperatorExpr's protected constructor
+  LazyBooleanExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> left_bool_expr,
+		   std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
+		   Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_bool_expr),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (right_bool_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  LazyBooleanExpr (LazyBooleanExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  LazyBooleanExpr &operator= (LazyBooleanExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  LazyBooleanExpr (LazyBooleanExpr &&other) = default;
+  LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LazyBooleanExpr *clone_expr_impl () const override
+  {
+    return new LazyBooleanExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LazyBooleanExpr *clone_expr_without_block_impl () const override
+  {
+    return new LazyBooleanExpr (*this);
+  }
+};
+
+// Binary infix "as" chir expression.
+class TypeCastExpr : public OperatorExpr
+{
+  std::unique_ptr<Type> type_to_convert_to;
+
+  // Note: only certain type casts allowed, outlined in reference
+public:
+  std::string as_string () const override;
+
+  // Constructor requires calling protected constructor of OperatorExpr
+  TypeCastExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> expr_to_cast,
+		std::unique_ptr<Type> type_to_cast_to, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (expr_to_cast),
+		    AST::AttrVec (), locus),
+      type_to_convert_to (std::move (type_to_cast_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also requires calling protected constructor
+  TypeCastExpr (TypeCastExpr const &other)
+    : OperatorExpr (other),
+      type_to_convert_to (other.type_to_convert_to->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  TypeCastExpr &operator= (TypeCastExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    type_to_convert_to = other.type_to_convert_to->clone_type ();
+
+    return *this;
+  }
+
+  // move constructors as not supported in c++03
+  TypeCastExpr (TypeCastExpr &&other) = default;
+  TypeCastExpr &operator= (TypeCastExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_casted_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  std::unique_ptr<Type> &get_type_to_convert_to ()
+  {
+    rust_assert (type_to_convert_to != nullptr);
+    return type_to_convert_to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypeCastExpr *clone_expr_impl () const override
+  {
+    return new TypeCastExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypeCastExpr *clone_expr_without_block_impl () const override
+  {
+    return new TypeCastExpr (*this);
+  }
+};
+
+// Binary assignment expression.
+class AssignmentExpr : public OperatorExpr
+{
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Call OperatorExpr constructor to initialise left_expr
+  AssignmentExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> value_to_assign_to,
+		  std::unique_ptr<Expr> value_to_assign, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (value_to_assign_to),
+		    AST::AttrVec (), locus),
+      right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Call OperatorExpr constructor in copy constructor, as well as clone
+  AssignmentExpr (AssignmentExpr const &other)
+    : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr right_expr
+  AssignmentExpr &operator= (AssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  AssignmentExpr (AssignmentExpr &&other) = default;
+  AssignmentExpr &operator= (AssignmentExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AssignmentExpr *clone_expr_impl () const override
+  {
+    return new AssignmentExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new AssignmentExpr (*this);
+  }
+};
+
+class CompoundAssignmentExpr : public OperatorExpr
+{
+public:
+  using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Use pointers in constructor to enable polymorphism
+  CompoundAssignmentExpr (Analysis::NodeMapping mappings,
+			  std::unique_ptr<Expr> value_to_assign_to,
+			  std::unique_ptr<Expr> value_to_assign,
+			  ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (value_to_assign_to),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Have clone in copy constructor
+  CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default;
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CompoundAssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new CompoundAssignmentExpr (*this);
+  }
+};
+
+// Expression in parentheses (i.e. like literally just any 3 + (2 * 6))
+class GroupedExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+  std::unique_ptr<Expr> expr_in_parens;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  GroupedExpr (Analysis::NodeMapping mappings,
+	       std::unique_ptr<Expr> parenthesised_expr,
+	       AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	       Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      expr_in_parens (std::move (parenthesised_expr)), locus (locus)
+  {}
+
+  // Copy constructor includes clone for expr_in_parens
+  GroupedExpr (GroupedExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      expr_in_parens (other.expr_in_parens->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone expr_in_parens
+  GroupedExpr &operator= (GroupedExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    expr_in_parens = other.expr_in_parens->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  GroupedExpr (GroupedExpr &&other) = default;
+  GroupedExpr &operator= (GroupedExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_expr_in_parens ()
+  {
+    rust_assert (expr_in_parens != nullptr);
+    return expr_in_parens;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Grouped;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedExpr *clone_expr_impl () const override
+  {
+    return new GroupedExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedExpr *clone_expr_without_block_impl () const override
+  {
+    return new GroupedExpr (*this);
+  }
+};
+
+// Base array initialisation internal element representation thing (abstract)
+// aka ArrayElements
+class ArrayElems
+{
+public:
+  enum ArrayExprType
+  {
+    VALUES,
+    COPIED,
+  };
+
+  ArrayElems (Analysis::NodeMapping mappings) : mappings (mappings){};
+
+  virtual ~ArrayElems () {}
+
+  // Unique pointer custom clone ArrayElems function
+  std::unique_ptr<ArrayElems> clone_array_elems () const
+  {
+    return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual ArrayExprType get_array_expr_type () const = 0;
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+protected:
+  // pure virtual clone implementation
+  virtual ArrayElems *clone_array_elems_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+};
+
+// Value array elements
+class ArrayElemsValues : public ArrayElems
+{
+  std::vector<std::unique_ptr<Expr> > values;
+
+  // TODO: should this store location data?
+
+public:
+  ArrayElemsValues (Analysis::NodeMapping mappings,
+		    std::vector<std::unique_ptr<Expr> > elems)
+    : ArrayElems (mappings), values (std::move (elems))
+  {}
+
+  // copy constructor with vector clone
+  ArrayElemsValues (ArrayElemsValues const &other) : ArrayElems (other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator with vector clone
+  ArrayElemsValues &operator= (ArrayElemsValues const &other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsValues (ArrayElemsValues &&other) = default;
+  ArrayElemsValues &operator= (ArrayElemsValues &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  size_t get_num_elements () const { return values.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_values () { return values; }
+
+  ArrayElems::ArrayExprType get_array_expr_type () const override final
+  {
+    return ArrayElems::ArrayExprType::VALUES;
+  }
+
+protected:
+  ArrayElemsValues *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsValues (*this);
+  }
+};
+
+// Copied array element and number of copies
+class ArrayElemsCopied : public ArrayElems
+{
+  std::unique_ptr<Expr> elem_to_copy;
+  std::unique_ptr<Expr> num_copies;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayElemsCopied (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Expr> copied_elem,
+		    std::unique_ptr<Expr> copy_amount)
+    : ArrayElems (mappings), elem_to_copy (std::move (copied_elem)),
+      num_copies (std::move (copy_amount))
+  {}
+
+  // Copy constructor required due to unique_ptr - uses custom clone
+  ArrayElemsCopied (ArrayElemsCopied const &other)
+    : ArrayElems (other), elem_to_copy (other.elem_to_copy->clone_expr ()),
+      num_copies (other.num_copies->clone_expr ())
+  {}
+
+  // Overloaded assignment operator for deep copying
+  ArrayElemsCopied &operator= (ArrayElemsCopied const &other)
+  {
+    elem_to_copy = other.elem_to_copy->clone_expr ();
+    num_copies = other.num_copies->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsCopied (ArrayElemsCopied &&other) = default;
+  ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Expr *get_elem_to_copy () { return elem_to_copy.get (); }
+
+  Expr *get_num_copies_expr () { return num_copies.get (); }
+
+  ArrayElems::ArrayExprType get_array_expr_type () const override final
+  {
+    return ArrayElems::ArrayExprType::COPIED;
+  }
+
+protected:
+  ArrayElemsCopied *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsCopied (*this);
+  }
+};
+
+// Array definition-ish expression
+class ArrayExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+  std::unique_ptr<ArrayElems> internal_elements;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  // Returns whether array expr has array elems or if it is just empty.
+  bool has_array_elems () const { return internal_elements != nullptr; }
+
+  // Constructor requires ArrayElems pointer
+  ArrayExpr (Analysis::NodeMapping mappings,
+	     std::unique_ptr<ArrayElems> array_elems,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      internal_elements (std::move (array_elems)), locus (locus)
+  {}
+
+  // Copy constructor requires cloning ArrayElems for polymorphism to hold
+  ArrayExpr (ArrayExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    if (other.has_array_elems ())
+      internal_elements = other.internal_elements->clone_array_elems ();
+  }
+
+  // Overload assignment operator to clone internal_elements
+  ArrayExpr &operator= (ArrayExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    if (other.has_array_elems ())
+      internal_elements = other.internal_elements->clone_array_elems ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayExpr (ArrayExpr &&other) = default;
+  ArrayExpr &operator= (ArrayExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ArrayElems *get_internal_elements () { return internal_elements.get (); };
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Array;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayExpr *clone_expr_impl () const override { return new ArrayExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayExpr (*this);
+  }
+};
+
+class ArrayIndexExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> array_expr;
+  std::unique_ptr<Expr> index_expr;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ArrayIndexExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> array_expr,
+		  std::unique_ptr<Expr> array_index_expr,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      array_expr (std::move (array_expr)),
+      index_expr (std::move (array_index_expr)), locus (locus)
+  {}
+
+  // Copy constructor requires special cloning due to unique_ptr
+  ArrayIndexExpr (ArrayIndexExpr const &other)
+    : ExprWithoutBlock (other), array_expr (other.array_expr->clone_expr ()),
+      index_expr (other.index_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone unique_ptrs
+  ArrayIndexExpr &operator= (ArrayIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    array_expr = other.array_expr->clone_expr ();
+    index_expr = other.index_expr->clone_expr ();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayIndexExpr (ArrayIndexExpr &&other) = default;
+  ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_array_expr () { return array_expr.get (); }
+  Expr *get_index_expr () { return index_expr.get (); }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::ArrayIndex;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayIndexExpr *clone_expr_impl () const override
+  {
+    return new ArrayIndexExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayIndexExpr (*this);
+  }
+};
+
+// HIR representation of a tuple
+class TupleExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+
+  std::vector<std::unique_ptr<Expr> > tuple_elems;
+  // replaces (inlined version of) TupleElements
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  TupleExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Expr> > tuple_elements,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      tuple_elems (std::move (tuple_elements)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  TupleExpr (TupleExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator to vector clone
+  TupleExpr &operator= (TupleExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleExpr (TupleExpr &&other) = default;
+  TupleExpr &operator= (TupleExpr &&other) = default;
+
+  /* Note: syntactically, can disambiguate single-element tuple from parens with
+   * comma, i.e. (0,) rather than (0) */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  const std::vector<std::unique_ptr<Expr> > &get_tuple_elems () const
+  {
+    return tuple_elems;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_tuple_elems ()
+  {
+    return tuple_elems;
+  }
+
+  bool is_unit () const { return tuple_elems.size () == 0; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Tuple;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleExpr *clone_expr_impl () const override { return new TupleExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleExpr (*this);
+  }
+};
+
+class TupleIndexExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> tuple_expr;
+  TupleIndex tuple_index;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  TupleIndex get_tuple_index () const { return tuple_index; }
+
+  TupleIndexExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> tuple_expr, TupleIndex index,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus)
+  {}
+
+  // Copy constructor requires a clone for tuple_expr
+  TupleIndexExpr (TupleIndexExpr const &other)
+    : ExprWithoutBlock (other), tuple_expr (other.tuple_expr->clone_expr ()),
+      tuple_index (other.tuple_index), locus (other.locus)
+  {}
+
+  // Overload assignment operator in order to clone
+  TupleIndexExpr &operator= (TupleIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    tuple_expr = other.tuple_expr->clone_expr ();
+    tuple_index = other.tuple_index;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleIndexExpr (TupleIndexExpr &&other) = default;
+  TupleIndexExpr &operator= (TupleIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_tuple_expr ()
+  {
+    rust_assert (tuple_expr != nullptr);
+    return tuple_expr;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::TupleIdx;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleIndexExpr *clone_expr_impl () const override
+  {
+    return new TupleIndexExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleIndexExpr (*this);
+  }
+};
+
+// Base struct/tuple/union value creator HIR node (abstract)
+class StructExpr : public ExprWithoutBlock
+{
+protected:
+  PathInExpression struct_name;
+
+  // Protected constructor to allow initialising struct_name
+  StructExpr (Analysis::NodeMapping mappings, PathInExpression struct_path,
+	      AST::AttrVec outer_attribs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      struct_name (std::move (struct_path))
+  {}
+
+public:
+  PathInExpression &get_struct_name () { return struct_name; }
+
+  std::string as_string () const override;
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Struct;
+  }
+};
+
+// Actual HIR node of the struct creator (with no fields). Not abstract!
+class StructExprStruct : public StructExpr
+{
+  AST::AttrVec inner_attrs;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  // Constructor has to call protected constructor of base class
+  StructExprStruct (Analysis::NodeMapping mappings,
+		    PathInExpression struct_path, AST::AttrVec inner_attribs,
+		    AST::AttrVec outer_attribs, Location locus)
+    : StructExpr (std::move (mappings), std::move (struct_path),
+		  std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  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 */
+  StructExprStruct *clone_expr_impl () const override
+  {
+    return new StructExprStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStruct *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStruct (*this);
+  }
+};
+
+/* HIR node representing expression used to fill a struct's fields from another
+ * struct */
+struct StructBase
+{
+public:
+  std::unique_ptr<Expr> base_struct;
+
+  // TODO: should this store location data?
+  StructBase (std::unique_ptr<Expr> base_struct_ptr)
+    : base_struct (std::move (base_struct_ptr))
+  {}
+
+  // Copy constructor requires clone
+  StructBase (StructBase const &other)
+  {
+    /* HACK: gets around base_struct pointer being null (e.g. if no struct base
+     * exists) */
+    if (other.base_struct != nullptr)
+      other.base_struct->clone_expr ();
+  }
+
+  // Destructor
+  ~StructBase () = default;
+
+  // Overload assignment operator to clone base_struct
+  StructBase &operator= (StructBase const &other)
+  {
+    base_struct = other.base_struct->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  StructBase (StructBase &&other) = default;
+  StructBase &operator= (StructBase &&other) = default;
+
+  // Returns a null expr-ed StructBase - error state
+  static StructBase error () { return StructBase (nullptr); }
+
+  // Returns whether StructBase is in error state
+  bool is_invalid () const { return base_struct == nullptr; }
+
+  std::string as_string () const;
+
+  Expr *get_base () { return base_struct.get (); }
+};
+
+/* Base HIR node for a single struct expression field (in struct instance
+ * creation) - abstract */
+class StructExprField
+{
+public:
+  enum StructExprFieldKind
+  {
+    IDENTIFIER_VALUE,
+    IDENTIFIER,
+    INDEX_VALUE,
+  };
+
+  virtual ~StructExprField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructExprField> clone_struct_expr_field () const
+  {
+    return std::unique_ptr<StructExprField> (clone_struct_expr_field_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRExpressionVisitor &vis) = 0;
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+  Location get_locus () { return locus; }
+
+  virtual StructExprFieldKind get_kind () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual StructExprField *clone_struct_expr_field_impl () const = 0;
+
+  StructExprField (Analysis::NodeMapping mapping, Location locus)
+    : mappings (mapping), locus (locus)
+  {}
+
+  Analysis::NodeMapping mappings;
+  Location locus;
+};
+
+// Identifier-only variant of StructExprField HIR node
+class StructExprFieldIdentifier : public StructExprField
+{
+private:
+  Identifier field_name;
+
+  // TODO: should this store location data?
+public:
+  StructExprFieldIdentifier (Analysis::NodeMapping mapping,
+			     Identifier field_identifier, Location locus)
+    : StructExprField (mapping, locus),
+      field_name (std::move (field_identifier))
+  {}
+
+  std::string as_string () const override { return field_name; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Identifier get_field_name () const { return field_name; }
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::IDENTIFIER;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifier (*this);
+  }
+};
+
+/* Base HIR node for a single struct expression field with an assigned value -
+ * abstract */
+class StructExprFieldWithVal : public StructExprField
+{
+  std::unique_ptr<Expr> value;
+
+protected:
+  StructExprFieldWithVal (Analysis::NodeMapping mapping,
+			  std::unique_ptr<Expr> field_value, Location locus)
+    : StructExprField (mapping, locus), value (std::move (field_value))
+  {}
+
+  // Copy constructor requires clone
+  StructExprFieldWithVal (StructExprFieldWithVal const &other)
+    : StructExprField (other.mappings, other.locus),
+      value (other.value->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
+  {
+    value = other.value->clone_expr ();
+    mappings = other.mappings;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprFieldWithVal (StructExprFieldWithVal &&other) = default;
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default;
+
+public:
+  std::string as_string () const override;
+
+  Expr *get_value () { return value.get (); }
+};
+
+// Identifier and value variant of StructExprField HIR node
+class StructExprFieldIdentifierValue : public StructExprFieldWithVal
+{
+public:
+  Identifier field_name;
+
+  // TODO: should this store location data?
+
+  StructExprFieldIdentifierValue (Analysis::NodeMapping mapping,
+				  Identifier field_identifier,
+				  std::unique_ptr<Expr> field_value,
+				  Location locus)
+    : StructExprFieldWithVal (mapping, std::move (field_value), locus),
+      field_name (std::move (field_identifier))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::IDENTIFIER_VALUE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifierValue (*this);
+  }
+};
+
+// Tuple index and value variant of StructExprField HIR node
+class StructExprFieldIndexValue : public StructExprFieldWithVal
+{
+public:
+  TupleIndex index;
+
+  // TODO: should this store location data?
+
+  StructExprFieldIndexValue (Analysis::NodeMapping mapping,
+			     TupleIndex tuple_index,
+			     std::unique_ptr<Expr> field_value, Location locus)
+    : StructExprFieldWithVal (mapping, std::move (field_value), locus),
+      index (tuple_index)
+  {}
+
+  std::string as_string () const override;
+
+  TupleIndex get_tuple_index () const { return index; };
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::INDEX_VALUE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIndexValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIndexValue (*this);
+  }
+};
+
+// HIR node of a struct creator with fields
+class StructExprStructFields : public StructExprStruct
+{
+public:
+  // std::vector<StructExprField> fields;
+  std::vector<std::unique_ptr<StructExprField> > fields;
+
+  // bool has_struct_base;
+  // FIXME make unique_ptr
+  StructBase *struct_base;
+
+  // For unions there is just one field, the index
+  // is set when type checking
+  int union_index = -1;
+
+  std::string as_string () const override;
+
+  bool has_struct_base () const { return struct_base != nullptr; }
+
+  // Constructor for StructExprStructFields when no struct base is used
+  StructExprStructFields (
+    Analysis::NodeMapping mappings, PathInExpression struct_path,
+    std::vector<std::unique_ptr<StructExprField> > expr_fields, Location locus,
+    StructBase *base_struct, AST::AttrVec inner_attribs = AST::AttrVec (),
+    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : StructExprStruct (std::move (mappings), std::move (struct_path),
+			std::move (inner_attribs), std::move (outer_attribs),
+			locus),
+      fields (std::move (expr_fields)), struct_base (base_struct)
+  {}
+
+  // copy constructor with vector clone
+  StructExprStructFields (StructExprStructFields const &other)
+    : StructExprStruct (other), struct_base (other.struct_base),
+      union_index (other.union_index)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+  }
+
+  // overloaded assignment operator with vector clone
+  StructExprStructFields &operator= (StructExprStructFields const &other)
+  {
+    StructExprStruct::operator= (other);
+    struct_base = other.struct_base;
+    union_index = other.union_index;
+
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprStructFields (StructExprStructFields &&other) = default;
+  StructExprStructFields &operator= (StructExprStructFields &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::vector<std::unique_ptr<StructExprField> > &get_fields ()
+  {
+    return fields;
+  };
+
+  const std::vector<std::unique_ptr<StructExprField> > &get_fields () const
+  {
+    return fields;
+  };
+
+  void set_fields_as_owner (
+    std::vector<std::unique_ptr<StructExprField> > new_fields)
+  {
+    fields = std::move (new_fields);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructFields *clone_expr_impl () const override
+  {
+    return new StructExprStructFields (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructFields *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructFields (*this);
+  }
+};
+
+// HIR node of the functional update struct creator
+class StructExprStructBase : public StructExprStruct
+{
+  StructBase struct_base;
+
+public:
+  std::string as_string () const override;
+
+  /*inline StructBase get_struct_base() const {
+      return struct_base;
+  }*/
+
+  StructExprStructBase (Analysis::NodeMapping mappings,
+			PathInExpression struct_path, StructBase base_struct,
+			AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+			Location locus)
+    : StructExprStruct (std::move (mappings), std::move (struct_path),
+			std::move (inner_attribs), std::move (outer_attribs),
+			locus),
+      struct_base (std::move (base_struct))
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructBase *get_struct_base () { return &struct_base; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructBase *clone_expr_impl () const override
+  {
+    return new StructExprStructBase (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructBase *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructBase (*this);
+  }
+};
+
+// Function call expression HIR node
+class CallExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> function;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  CallExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> function_expr,
+	    std::vector<std::unique_ptr<Expr> > function_params,
+	    AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      function (std::move (function_expr)),
+      params (std::move (function_params)), locus (locus)
+  {}
+
+  // copy constructor requires clone
+  CallExpr (CallExpr const &other)
+    : ExprWithoutBlock (other), function (other.function->clone_expr ()),
+      locus (other.locus)
+  /*, params(other.params),*/ {
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone
+  CallExpr &operator= (CallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    function = other.function->clone_expr ();
+    locus = other.locus;
+    // params = other.params;
+    // outer_attrs = other.outer_attrs;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  CallExpr (CallExpr &&other) = default;
+  CallExpr &operator= (CallExpr &&other) = default;
+
+  // Returns whether function call has parameters.
+  bool has_params () const { return !params.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_fnexpr () { return function.get (); }
+
+  size_t num_params () const { return params.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_arguments () { return params; }
+
+  const std::vector<std::unique_ptr<Expr> > &get_arguments () const
+  {
+    return params;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Call;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CallExpr *clone_expr_impl () const override { return new CallExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CallExpr *clone_expr_without_block_impl () const override
+  {
+    return new CallExpr (*this);
+  }
+};
+
+// Method call expression HIR node
+class MethodCallExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> receiver;
+  PathExprSegment method_name;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  MethodCallExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> call_receiver,
+		  PathExprSegment method_path,
+		  std::vector<std::unique_ptr<Expr> > method_params,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      receiver (std::move (call_receiver)),
+      method_name (std::move (method_path)), params (std::move (method_params)),
+      locus (locus)
+  {}
+
+  // copy constructor required due to cloning
+  MethodCallExpr (MethodCallExpr const &other)
+    : ExprWithoutBlock (other), receiver (other.receiver->clone_expr ()),
+      method_name (other.method_name), locus (other.locus)
+  /*, params(other.params),*/ {
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone receiver object
+  MethodCallExpr &operator= (MethodCallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    receiver = other.receiver->clone_expr ();
+    method_name = other.method_name;
+    locus = other.locus;
+    // params = other.params;
+    // outer_attrs = other.outer_attrs;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  MethodCallExpr (MethodCallExpr &&other) = default;
+  MethodCallExpr &operator= (MethodCallExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_receiver () { return receiver; }
+
+  PathExprSegment get_method_name () const { return method_name; };
+
+  size_t num_params () const { return params.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_arguments () { return params; }
+
+  const std::vector<std::unique_ptr<Expr> > &get_arguments () const
+  {
+    return params;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::MethodCall;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MethodCallExpr *clone_expr_impl () const override
+  {
+    return new MethodCallExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MethodCallExpr *clone_expr_without_block_impl () const override
+  {
+    return new MethodCallExpr (*this);
+  }
+};
+
+// aka FieldExpression
+// Struct or union field access expression HIR node
+class FieldAccessExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> receiver;
+  Identifier field;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  FieldAccessExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> field_access_receiver,
+		   Identifier field_name, AST::AttrVec outer_attribs,
+		   Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      receiver (std::move (field_access_receiver)),
+      field (std::move (field_name)), locus (locus)
+  {}
+
+  // Copy constructor required due to unique_ptr cloning
+  FieldAccessExpr (FieldAccessExpr const &other)
+    : ExprWithoutBlock (other), receiver (other.receiver->clone_expr ()),
+      field (other.field), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  FieldAccessExpr &operator= (FieldAccessExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    receiver = other.receiver->clone_expr ();
+    field = other.field;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  FieldAccessExpr (FieldAccessExpr &&other) = default;
+  FieldAccessExpr &operator= (FieldAccessExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_receiver_expr ()
+  {
+    rust_assert (receiver != nullptr);
+    return receiver;
+  }
+
+  Identifier get_field_name () const { return field; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::FieldAccess;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  FieldAccessExpr *clone_expr_impl () const override
+  {
+    return new FieldAccessExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  FieldAccessExpr *clone_expr_without_block_impl () const override
+  {
+    return new FieldAccessExpr (*this);
+  }
+};
+
+// Closure parameter data structure
+struct ClosureParam
+{
+private:
+  std::unique_ptr<Pattern> pattern;
+
+  // bool has_type_given;
+  std::unique_ptr<Type> type;
+
+  // TODO: should this store location data?
+
+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))
+  {}
+
+  // Copy constructor required due to cloning as a result of unique_ptrs
+  ClosureParam (ClosureParam const &other)
+    : pattern (other.pattern->clone_pattern ())
+  {
+    // guard to protect from null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  ~ClosureParam () = default;
+
+  // Assignment operator must be overloaded to clone as well
+  ClosureParam &operator= (ClosureParam const &other)
+  {
+    pattern = other.pattern->clone_pattern ();
+    type = other.type->clone_type ();
+
+    return *this;
+  }
+
+  // move constructors
+  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; }
+
+  // Creates an error state closure parameter.
+  static ClosureParam create_error () { return ClosureParam (nullptr); }
+
+  std::string as_string () const;
+};
+
+// Base closure definition expression HIR node - abstract
+class ClosureExpr : public ExprWithoutBlock
+{
+  bool has_move;
+  std::vector<ClosureParam> params;
+  Location locus;
+
+protected:
+  ClosureExpr (Analysis::NodeMapping mappings,
+	       std::vector<ClosureParam> closure_params, 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)
+  {}
+
+public:
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Closure;
+  }
+};
+
+// Represents a non-type-specified closure expression HIR node
+class ClosureExprInner : public ClosureExpr
+{
+  std::unique_ptr<Expr> closure_inner;
+
+public:
+  std::string as_string () const override;
+
+  // 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)
+  {
+    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;
+
+  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 */
+  ClosureExprInner *clone_expr_impl () const override
+  {
+    return new ClosureExprInner (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInner *clone_expr_without_block_impl () const override
+  {
+    return new ClosureExprInner (*this);
+  }
+};
+
+// A block HIR node
+class BlockExpr : public ExprWithBlock
+{
+public:
+  AST::AttrVec inner_attrs;
+  std::vector<std::unique_ptr<Stmt> > statements;
+  std::unique_ptr<Expr> expr;
+  bool tail_reachable;
+  Location start_locus;
+  Location end_locus;
+
+  std::string as_string () const override;
+
+  // Returns whether the block contains statements.
+  bool has_statements () const { return !statements.empty (); }
+
+  // Returns whether the block contains an expression
+  bool has_expr () const { return expr != nullptr; }
+
+  bool is_tail_reachable () const { return tail_reachable; }
+
+  BlockExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Stmt> > block_statements,
+	     std::unique_ptr<Expr> block_expr, bool tail_reachable,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location start_locus, Location end_locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      statements (std::move (block_statements)), expr (std::move (block_expr)),
+      tail_reachable (tail_reachable), start_locus (start_locus),
+      end_locus (end_locus)
+  {}
+
+  // Copy constructor with clone
+  BlockExpr (BlockExpr const &other)
+    : ExprWithBlock (other), /*statements(other.statements),*/
+      inner_attrs (other.inner_attrs), start_locus (other.start_locus),
+      end_locus (other.end_locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+  }
+
+  // Overloaded assignment operator to clone pointer
+  BlockExpr &operator= (BlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    // statements = other.statements;
+    expr = other.expr->clone_expr ();
+    inner_attrs = other.inner_attrs;
+    start_locus = other.end_locus;
+    end_locus = other.end_locus;
+    // outer_attrs = other.outer_attrs;
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+
+    return *this;
+  }
+
+  // move constructors
+  BlockExpr (BlockExpr &&other) = default;
+  BlockExpr &operator= (BlockExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<BlockExpr> clone_block_expr () const
+  {
+    return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
+  }
+
+  Location get_locus () const override final { return start_locus; }
+
+  Location get_start_locus () const { return start_locus; }
+
+  Location get_end_locus () const { return end_locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  bool is_final_stmt (Stmt *stmt) { return statements.back ().get () == stmt; }
+
+  std::unique_ptr<Expr> &get_final_expr () { return expr; }
+
+  std::vector<std::unique_ptr<Stmt> > &get_statements () { return statements; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Block;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BlockExpr *clone_expr_impl () const override
+  {
+    return clone_block_expr_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BlockExpr *clone_expr_with_block_impl () const override
+  {
+    return clone_block_expr_impl ();
+  }
+
+  /* This is the base method as not an abstract class - not virtual but could be
+   * in future if required. */
+  /*virtual*/ BlockExpr *clone_block_expr_impl () const
+  {
+    return new BlockExpr (*this);
+  }
+};
+
+// 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
+{
+  Lifetime label;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the continue expr has a label.
+  bool has_label () const { return !label.is_error (); }
+
+  // Constructor for a ContinueExpr with a label.
+  ContinueExpr (Analysis::NodeMapping mappings, Location locus, Lifetime label,
+		AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      label (std::move (label)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Lifetime &get_label () { return label; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Continue;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ContinueExpr *clone_expr_impl () const override
+  {
+    return new ContinueExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ContinueExpr *clone_expr_without_block_impl () const override
+  {
+    return new ContinueExpr (*this);
+  }
+};
+
+// HIR node representing break expression within loops
+class BreakExpr : public ExprWithoutBlock
+{
+  // bool has_label;
+  Lifetime label;
+
+  // bool has_break_expr;
+  std::unique_ptr<Expr> break_expr;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the break expression has a label or not.
+  bool has_label () const { return !label.is_error (); }
+
+  /* Returns whether the break expression has an expression used in the break or
+   * not. */
+  bool has_break_expr () const { return break_expr != nullptr; }
+
+  // Constructor for a break expression
+  BreakExpr (Analysis::NodeMapping mappings, Location locus,
+	     Lifetime break_label,
+	     std::unique_ptr<Expr> expr_in_break = nullptr,
+	     AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      label (std::move (break_label)), break_expr (std::move (expr_in_break)),
+      locus (locus)
+  {}
+
+  // Copy constructor defined to use clone for unique pointer
+  BreakExpr (BreakExpr const &other)
+    : ExprWithoutBlock (other), label (other.label), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.break_expr != nullptr)
+      break_expr = other.break_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique pointer
+  BreakExpr &operator= (BreakExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    label = other.label;
+    break_expr = other.break_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  BreakExpr (BreakExpr &&other) = default;
+  BreakExpr &operator= (BreakExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Lifetime &get_label () { return label; }
+
+  std::unique_ptr<Expr> &get_expr () { return break_expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Break;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BreakExpr *clone_expr_impl () const override { return new BreakExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BreakExpr *clone_expr_without_block_impl () const override
+  {
+    return new BreakExpr (*this);
+  }
+};
+
+// Base range expression HIR node object - abstract
+class RangeExpr : public ExprWithoutBlock
+{
+  Location locus;
+
+protected:
+  // outer attributes not allowed before range expressions
+  RangeExpr (Analysis::NodeMapping mappings, Location locus)
+    : ExprWithoutBlock (std::move (mappings), AST::AttrVec ()), locus (locus)
+  {}
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Range;
+  }
+};
+
+// Range from (inclusive) and to (exclusive) expression HIR node object
+// aka RangeExpr; constructs a std::ops::Range object
+class RangeFromToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> range_from,
+		   std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+
+  // Copy constructor with cloning
+  RangeFromToExpr (RangeFromToExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ()),
+      to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique pointers
+  RangeFromToExpr &operator= (RangeFromToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToExpr (RangeFromToExpr &&other) = default;
+  RangeFromToExpr &operator= (RangeFromToExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToExpr *clone_expr_impl () const override
+  {
+    return new RangeFromToExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToExpr (*this);
+  }
+};
+
+// Range from (inclusive) expression HIR node object
+// constructs a std::ops::RangeFrom object
+class RangeFromExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromExpr (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Expr> range_from, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from))
+  {}
+
+  // Copy constructor with clone
+  RangeFromExpr (RangeFromExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  RangeFromExpr &operator= (RangeFromExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromExpr (RangeFromExpr &&other) = default;
+  RangeFromExpr &operator= (RangeFromExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromExpr *clone_expr_impl () const override
+  {
+    return new RangeFromExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromExpr (*this);
+  }
+};
+
+// Range to (exclusive) expression HIR node object
+// constructs a std::ops::RangeTo object
+class RangeToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  // outer attributes not allowed
+  RangeToExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> range_to,
+	       Location locus)
+    : RangeExpr (std::move (mappings), locus), to (std::move (range_to))
+  {}
+
+  // Copy constructor with clone
+  RangeToExpr (RangeToExpr const &other)
+    : RangeExpr (other), to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  RangeToExpr &operator= (RangeToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToExpr (RangeToExpr &&other) = default;
+  RangeToExpr &operator= (RangeToExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToExpr *clone_expr_impl () const override
+  {
+    return new RangeToExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToExpr (*this);
+  }
+};
+
+// Full range expression HIR node object
+// constructs a std::ops::RangeFull object
+class RangeFullExpr : public RangeExpr
+{
+public:
+  std::string as_string () const override;
+
+  RangeFullExpr (Analysis::NodeMapping mappings, Location locus)
+    : RangeExpr (std::move (mappings), locus)
+  {}
+  // outer attributes not allowed
+
+  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 */
+  RangeFullExpr *clone_expr_impl () const override
+  {
+    return new RangeFullExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFullExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFullExpr (*this);
+  }
+};
+
+// Range from (inclusive) and to (inclusive) expression HIR node object
+// aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object
+class RangeFromToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToInclExpr (Analysis::NodeMapping mappings,
+		       std::unique_ptr<Expr> range_from,
+		       std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeFromToInclExpr (RangeFromToInclExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ()),
+      to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to use clone
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToInclExpr (RangeFromToInclExpr &&other) = default;
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToInclExpr *clone_expr_impl () const override
+  {
+    return new RangeFromToInclExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToInclExpr (*this);
+  }
+};
+
+// Range to (inclusive) expression HIR node object
+// aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object
+class RangeToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeToInclExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeToInclExpr (RangeToInclExpr const &other)
+    : RangeExpr (other), to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone pointer
+  RangeToInclExpr &operator= (RangeToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToInclExpr (RangeToInclExpr &&other) = default;
+  RangeToInclExpr &operator= (RangeToInclExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_to_expr () { return to; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToInclExpr *clone_expr_impl () const override
+  {
+    return new RangeToInclExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToInclExpr (*this);
+  }
+};
+
+// Return expression HIR node representation
+class ReturnExpr : public ExprWithoutBlock
+{
+public:
+  std::unique_ptr<Expr> return_expr;
+
+  Location locus;
+
+  std::string as_string () const override;
+
+  /* Returns whether the object has an expression returned (i.e. not void return
+   * type). */
+  bool has_return_expr () const { return return_expr != nullptr; }
+
+  // Constructor for ReturnExpr.
+  ReturnExpr (Analysis::NodeMapping mappings, Location locus,
+	      std::unique_ptr<Expr> returned_expr = nullptr,
+	      AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      return_expr (std::move (returned_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  ReturnExpr (ReturnExpr const &other)
+    : ExprWithoutBlock (other), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.return_expr != nullptr)
+      return_expr = other.return_expr->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone return_expr pointer
+  ReturnExpr &operator= (ReturnExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    return_expr = other.return_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ReturnExpr (ReturnExpr &&other) = default;
+  ReturnExpr &operator= (ReturnExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_expr () { return return_expr.get (); }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Return;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReturnExpr *clone_expr_impl () const override
+  {
+    return new ReturnExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReturnExpr *clone_expr_without_block_impl () const override
+  {
+    return new ReturnExpr (*this);
+  }
+};
+
+// An unsafe block HIR node
+class UnsafeBlockExpr : public ExprWithBlock
+{
+  // Or just have it extend BlockExpr
+  std::unique_ptr<BlockExpr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UnsafeBlockExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<BlockExpr> block_expr,
+		   AST::AttrVec outer_attribs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      expr (std::move (block_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UnsafeBlockExpr (UnsafeBlockExpr const &other)
+    : ExprWithBlock (other), expr (other.expr->clone_block_expr ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    expr = other.expr->clone_block_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<BlockExpr> &get_block_expr () { return expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::UnsafeBlock;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  UnsafeBlockExpr *clone_expr_impl () const override
+  {
+    return new UnsafeBlockExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  UnsafeBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new UnsafeBlockExpr (*this);
+  }
+};
+
+// Loop label expression HIR node used with break and continue expressions
+// TODO: inline?
+class LoopLabel /*: public Node*/
+{
+  Lifetime label; // or type LIFETIME_OR_LABEL
+
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const;
+
+  LoopLabel (Analysis::NodeMapping mapping, Lifetime loop_label, Location locus)
+    : label (std::move (loop_label)), locus (locus), mappings (mapping)
+  {}
+
+  // Returns whether the LoopLabel is in an error state.
+  bool is_error () const { return label.is_error (); }
+
+  Location get_locus () const { return locus; }
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+  Lifetime &get_lifetime () { return label; }
+};
+
+// Base loop expression HIR node - aka LoopExpr
+class BaseLoopExpr : public ExprWithBlock
+{
+protected:
+  LoopLabel loop_label;
+  std::unique_ptr<BlockExpr> loop_block;
+
+private:
+  Location locus;
+
+protected:
+  // Constructor for BaseLoopExpr
+  BaseLoopExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<BlockExpr> loop_block, Location locus,
+		LoopLabel loop_label,
+		AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
+      locus (locus)
+  {}
+
+  // Copy constructor for BaseLoopExpr with clone
+  BaseLoopExpr (BaseLoopExpr const &other)
+    : ExprWithBlock (other), loop_label (other.loop_label),
+      loop_block (other.loop_block->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  BaseLoopExpr &operator= (BaseLoopExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    loop_block = other.loop_block->clone_block_expr ();
+    loop_label = other.loop_label;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  BaseLoopExpr (BaseLoopExpr &&other) = default;
+  BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::BaseLoop;
+  }
+
+public:
+  bool has_loop_label () const { return !loop_label.is_error (); }
+
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<HIR::BlockExpr> &get_loop_block () { return loop_block; };
+
+  LoopLabel &get_loop_label () { return loop_label; }
+};
+
+// 'Loop' expression (i.e. the infinite loop) HIR node
+class LoopExpr : public BaseLoopExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor for LoopExpr
+  LoopExpr (Analysis::NodeMapping mappings,
+	    std::unique_ptr<BlockExpr> loop_block, Location locus,
+	    LoopLabel loop_label, AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs))
+  {}
+
+  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 */
+  LoopExpr *clone_expr_impl () const override { return new LoopExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new LoopExpr (*this);
+  }
+};
+
+// While loop expression HIR node (predicate loop)
+class WhileLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Expr> condition;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for while loop with loop label
+  WhileLoopExpr (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Expr> loop_condition,
+		 std::unique_ptr<BlockExpr> loop_block, Location locus,
+		 LoopLabel loop_label,
+		 AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      condition (std::move (loop_condition))
+  {}
+
+  // Copy constructor with clone
+  WhileLoopExpr (WhileLoopExpr const &other)
+    : BaseLoopExpr (other), condition (other.condition->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  WhileLoopExpr &operator= (WhileLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    condition = other.condition->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLoopExpr (WhileLoopExpr &&other) = default;
+  WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_predicate_expr () { return condition; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLoopExpr *clone_expr_impl () const override
+  {
+    return new WhileLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLoopExpr (*this);
+  }
+};
+
+// While let loop expression HIR node (predicate pattern loop)
+class WhileLetLoopExpr : public BaseLoopExpr
+{
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> condition;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with a loop label
+  WhileLetLoopExpr (Analysis::NodeMapping mappings,
+		    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		    std::unique_ptr<Expr> condition,
+		    std::unique_ptr<BlockExpr> loop_block, Location locus,
+		    LoopLabel loop_label,
+		    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      condition (std::move (condition))
+  {}
+
+  // Copy constructor with clone
+  WhileLetLoopExpr (WhileLetLoopExpr const &other)
+    : BaseLoopExpr (other),
+      /*match_arm_patterns(other.match_arm_patterns),*/ condition (
+	other.condition->clone_expr ())
+  {
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone pointers
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    condition = other.condition->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_cond () { return condition; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLetLoopExpr *clone_expr_impl () const override
+  {
+    return new WhileLetLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLetLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLetLoopExpr (*this);
+  }
+};
+
+// For loop expression HIR node (iterator loop)
+class ForLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Pattern> pattern;
+  std::unique_ptr<Expr> iterator_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with loop label
+  ForLoopExpr (Analysis::NodeMapping mappings,
+	       std::unique_ptr<Pattern> loop_pattern,
+	       std::unique_ptr<Expr> iterator_expr,
+	       std::unique_ptr<BlockExpr> loop_body, Location locus,
+	       LoopLabel loop_label,
+	       AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_body), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      pattern (std::move (loop_pattern)),
+      iterator_expr (std::move (iterator_expr))
+  {}
+
+  // Copy constructor with clone
+  ForLoopExpr (ForLoopExpr const &other)
+    : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
+      iterator_expr (other.iterator_expr->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ForLoopExpr &operator= (ForLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    pattern = other.pattern->clone_pattern ();
+    iterator_expr = other.iterator_expr->clone_expr ();
+    /*loop_block = other.loop_block->clone_block_expr();
+    loop_label = other.loop_label;
+    outer_attrs = other.outer_attrs;*/
+
+    return *this;
+  }
+
+  // move constructors
+  ForLoopExpr (ForLoopExpr &&other) = default;
+  ForLoopExpr &operator= (ForLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_iterator_expr () { return iterator_expr; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ForLoopExpr *clone_expr_impl () const override
+  {
+    return new ForLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ForLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new ForLoopExpr (*this);
+  }
+};
+
+// forward decl for IfExpr
+class IfLetExpr;
+
+// Base if expression with no "else" or "if let" HIR node
+class IfExpr : public ExprWithBlock
+{
+  std::unique_ptr<Expr> condition;
+  std::unique_ptr<BlockExpr> if_block;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> condition,
+	  std::unique_ptr<BlockExpr> if_block, Location locus)
+    : ExprWithBlock (std::move (mappings), AST::AttrVec ()),
+      condition (std::move (condition)), if_block (std::move (if_block)),
+      locus (locus)
+  {}
+  // outer attributes are never allowed on IfExprs
+
+  // Copy constructor with clone
+  IfExpr (IfExpr const &other)
+    : ExprWithBlock (other), condition (other.condition->clone_expr ()),
+      if_block (other.if_block->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone expressions
+  IfExpr &operator= (IfExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    condition = other.condition->clone_expr ();
+    if_block = other.if_block->clone_block_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  IfExpr (IfExpr &&other) = default;
+  IfExpr &operator= (IfExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfExpr> clone_if_expr () const
+  {
+    return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
+  }
+
+  /* Note that multiple "else if"s are handled via nested HIRs rather than a
+   * vector of else ifs - i.e. not like a switch statement. TODO - is this a
+   * better approach? or does it not parse correctly and have downsides? */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_if_condition (HIRFullVisitor &vis) { condition->accept_vis (vis); }
+  void vis_if_block (HIRFullVisitor &vis) { if_block->accept_vis (vis); }
+
+  Expr *get_if_condition () { return condition.get (); }
+  BlockExpr *get_if_block () { return if_block.get (); }
+
+  ExprType get_expression_type () const final override { return ExprType::If; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExpr *clone_expr_impl () const override { return new IfExpr (*this); }
+
+  // Base clone function but still concrete as concrete base class
+  virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExpr *clone_expr_with_block_impl () const override
+  {
+    return new IfExpr (*this);
+  }
+};
+
+// If expression with an ending "else" expression HIR node (trailing)
+class IfExprConseqElse : public IfExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqElse (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Expr> condition,
+		    std::unique_ptr<BlockExpr> if_block,
+		    std::unique_ptr<BlockExpr> else_block, Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      else_block (std::move (else_block))
+  {}
+  // again, outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqElse (IfExprConseqElse const &other)
+    : IfExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // Overloaded assignment operator with cloning
+  IfExprConseqElse &operator= (IfExprConseqElse const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqElse (IfExprConseqElse &&other) = default;
+  IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_else_block (HIRFullVisitor &vis) { else_block->accept_vis (vis); }
+
+  BlockExpr *get_else_block () { return else_block.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_expr_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+};
+
+// If expression with an ending "else if" expression HIR node
+class IfExprConseqIf : public IfExpr
+{
+  std::unique_ptr<IfExpr> conseq_if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIf (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> condition,
+		  std::unique_ptr<BlockExpr> if_block,
+		  std::unique_ptr<IfExpr> conseq_if_expr, Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      conseq_if_expr (std::move (conseq_if_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIf (IfExprConseqIf const &other)
+    : IfExpr (other), conseq_if_expr (other.conseq_if_expr->clone_if_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIf &operator= (IfExprConseqIf const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    conseq_if_expr = other.conseq_if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIf (IfExprConseqIf &&other) = default;
+  IfExprConseqIf &operator= (IfExprConseqIf &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_conseq_if_expr (HIRFullVisitor &vis)
+  {
+    conseq_if_expr->accept_vis (vis);
+  }
+
+  IfExpr *get_conseq_if_expr () { return conseq_if_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_expr_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+};
+
+// Basic "if let" expression HIR node with no else
+class IfLetExpr : public ExprWithBlock
+{
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> value;
+  std::unique_ptr<BlockExpr> if_block;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	     std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+	     Location locus)
+    : ExprWithBlock (std::move (mappings), AST::AttrVec ()),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      value (std::move (value)), if_block (std::move (if_block)), locus (locus)
+  {}
+  // outer attributes not allowed on if let exprs either
+
+  // copy constructor with clone
+  IfLetExpr (IfLetExpr const &other)
+    : ExprWithBlock (other),
+      /*match_arm_patterns(other.match_arm_patterns),*/ value (
+	other.value->clone_expr ()),
+      if_block (other.if_block->clone_block_expr ()), locus (other.locus)
+  {
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // overload assignment operator to clone
+  IfLetExpr &operator= (IfLetExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    value = other.value->clone_expr ();
+    if_block = other.if_block->clone_block_expr ();
+    locus = other.locus;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExpr (IfLetExpr &&other) = default;
+  IfLetExpr &operator= (IfLetExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfLetExpr> clone_if_let_expr () const
+  {
+    return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (value != nullptr);
+    return value;
+  }
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  BlockExpr *get_if_block () { return if_block.get (); }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::IfLet;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExpr *clone_expr_impl () const override { return new IfLetExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExpr *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExpr (*this);
+  }
+
+  // Base clone function but still concrete as concrete base class
+  virtual IfLetExpr *clone_if_let_expr_impl () const
+  {
+    return new IfLetExpr (*this);
+  }
+};
+
+// If expression with an ending "else if let" expression HIR node
+class IfExprConseqIfLet : public IfExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIfLet (Analysis::NodeMapping mappings,
+		     std::unique_ptr<Expr> condition,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfLetExpr> conseq_if_let_expr,
+		     Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      if_let_expr (std::move (conseq_if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIfLet (IfExprConseqIfLet const &other)
+    : IfExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIfLet (IfExprConseqIfLet &&other) = default;
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet &&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 */
+  IfExprConseqIfLet *clone_expr_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else" expression at the
+ * end */
+class IfLetExprConseqElse : public IfLetExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqElse (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<BlockExpr> else_block, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      else_block (std::move (else_block))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqElse (IfLetExprConseqElse const &other)
+    : IfLetExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse &&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 */
+  IfLetExprConseqElse *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else if" expression at the
+ * end */
+class IfLetExprConseqIf : public IfLetExpr
+{
+  std::unique_ptr<IfExpr> if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIf (Analysis::NodeMapping mappings,
+		     std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		     std::unique_ptr<Expr> value,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfExpr> if_expr, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      if_expr (std::move (if_expr))
+  {}
+  // again, outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIf (IfLetExprConseqIf const &other)
+    : IfLetExpr (other), if_expr (other.if_expr->clone_if_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_expr = other.if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIf (IfLetExprConseqIf &&other) = default;
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf &&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 */
+  IfLetExprConseqIf *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else if let" expression at
+ * the end */
+class IfLetExprConseqIfLet : public IfLetExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIfLet (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<IfLetExpr> if_let_expr, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      if_let_expr (std::move (if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet const &other)
+    : IfLetExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet &&other) = default;
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet &&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 */
+  IfLetExprConseqIfLet *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+};
+
+// Match arm expression
+struct MatchArm
+{
+private:
+  AST::AttrVec outer_attrs;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns;
+  std::unique_ptr<Expr> guard_expr;
+  Location locus;
+
+public:
+  // Returns whether the MatchArm has a match arm guard expression
+  bool has_match_arm_guard () const { return guard_expr != nullptr; }
+
+  // Constructor for match arm with a guard expression
+  MatchArm (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	    Location locus, std::unique_ptr<Expr> guard_expr = nullptr,
+	    AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      guard_expr (std::move (guard_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
+  {
+    // guard to protect from null pointer dereference
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    locus = other.locus;
+  }
+
+  ~MatchArm () = default;
+
+  // Overload assignment operator to clone
+  MatchArm &operator= (MatchArm const &other)
+  {
+    outer_attrs = other.outer_attrs;
+
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+
+    match_arm_patterns.clear ();
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  MatchArm (MatchArm &&other) = default;
+  MatchArm &operator= (MatchArm &&other) = default;
+
+  // Returns whether match arm is in an error state.
+  bool is_error () const { return match_arm_patterns.empty (); }
+
+  // Creates a match arm in an error state.
+  static MatchArm create_error ()
+  {
+    Location locus = Location ();
+    return MatchArm (std::vector<std::unique_ptr<Pattern> > (), locus);
+  }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  std::unique_ptr<Expr> &get_guard_expr ()
+  {
+    rust_assert (has_match_arm_guard ());
+    return guard_expr;
+  }
+
+  Location get_locus () const { return locus; }
+};
+
+/* A "match case" - a correlated match arm and resulting expression. Not
+ * abstract. */
+struct MatchCase
+{
+private:
+  Analysis::NodeMapping mappings;
+  MatchArm arm;
+  std::unique_ptr<Expr> expr;
+
+public:
+  MatchCase (Analysis::NodeMapping mappings, MatchArm arm,
+	     std::unique_ptr<Expr> expr)
+    : mappings (mappings), arm (std::move (arm)), expr (std::move (expr))
+  {}
+
+  MatchCase (const MatchCase &other)
+    : mappings (other.mappings), arm (other.arm),
+      expr (other.expr->clone_expr ())
+  {}
+
+  MatchCase &operator= (const MatchCase &other)
+  {
+    mappings = other.mappings;
+    arm = other.arm;
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+
+  MatchCase (MatchCase &&other) = default;
+  MatchCase &operator= (MatchCase &&other) = default;
+
+  ~MatchCase () = default;
+
+  std::string as_string () const;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  MatchArm &get_arm () { return arm; }
+  std::unique_ptr<Expr> &get_expr () { return expr; }
+};
+
+// Match expression HIR node
+class MatchExpr : public ExprWithBlock
+{
+  std::unique_ptr<Expr> branch_value;
+  AST::AttrVec inner_attrs;
+  std::vector<MatchCase> match_arms;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  bool has_match_arms () const { return !match_arms.empty (); }
+
+  MatchExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> branch_value,
+	     std::vector<MatchCase> match_arms, AST::AttrVec inner_attrs,
+	     AST::AttrVec outer_attrs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attrs)),
+      branch_value (std::move (branch_value)),
+      inner_attrs (std::move (inner_attrs)),
+      match_arms (std::move (match_arms)), locus (locus)
+  {}
+
+  // Copy constructor requires clone due to unique_ptr
+  MatchExpr (MatchExpr const &other)
+    : ExprWithBlock (other), branch_value (other.branch_value->clone_expr ()),
+      inner_attrs (other.inner_attrs), match_arms (other.match_arms),
+      locus (other.locus)
+  {
+    /*match_arms.reserve (other.match_arms.size ());
+    for (const auto &e : other.match_arms)
+      match_arms.push_back (e->clone_match_case ());*/
+  }
+
+  // Overloaded assignment operator to clone due to unique_ptr
+  MatchExpr &operator= (MatchExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    branch_value = other.branch_value->clone_expr ();
+    inner_attrs = other.inner_attrs;
+    match_arms = other.match_arms;
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    /*match_arms.reserve (other.match_arms.size ());
+    for (const auto &e : other.match_arms)
+      match_arms.push_back (e->clone_match_case ());*/
+
+    return *this;
+  }
+
+  // move constructors
+  MatchExpr (MatchExpr &&other) = default;
+  MatchExpr &operator= (MatchExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (branch_value != nullptr);
+    return branch_value;
+  }
+
+  const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
+  std::vector<MatchCase> &get_match_cases () { return match_arms; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Match;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MatchExpr *clone_expr_impl () const override { return new MatchExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MatchExpr *clone_expr_with_block_impl () const override
+  {
+    return new MatchExpr (*this);
+  }
+};
+
+// Await expression HIR node (pseudo-member variable access)
+class AwaitExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> awaited_expr;
+  Location locus;
+
+public:
+  // TODO: ensure outer attributes are actually allowed
+  AwaitExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> awaited_expr,
+	     AST::AttrVec outer_attrs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      awaited_expr (std::move (awaited_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AwaitExpr (AwaitExpr const &other)
+    : ExprWithoutBlock (other),
+      awaited_expr (other.awaited_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // overloaded assignment operator with clone
+  AwaitExpr &operator= (AwaitExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    awaited_expr = other.awaited_expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  AwaitExpr (AwaitExpr &&other) = default;
+  AwaitExpr &operator= (AwaitExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Await;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AwaitExpr *clone_expr_without_block_impl () const override
+  {
+    return new AwaitExpr (*this);
+  }
+};
+
+// Async block expression HIR node (block expr that evaluates to a future)
+class AsyncBlockExpr : public ExprWithBlock
+{
+  bool has_move;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  AsyncBlockExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<BlockExpr> block_expr, bool has_move,
+		  AST::AttrVec outer_attrs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attrs)),
+      has_move (has_move), block_expr (std::move (block_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AsyncBlockExpr (AsyncBlockExpr const &other)
+    : ExprWithBlock (other), has_move (other.has_move),
+      block_expr (other.block_expr->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // overloaded assignment operator to clone
+  AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    has_move = other.has_move;
+    block_expr = other.block_expr->clone_block_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  AsyncBlockExpr (AsyncBlockExpr &&other) = default;
+  AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::AsyncBlock;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AsyncBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new AsyncBlockExpr (*this);
+  }
+};
+
+// this is a utility helper class for type-checking and code-generation
+class OperatorExprMeta
+{
+public:
+  OperatorExprMeta (HIR::CompoundAssignmentExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::ArithmeticOrLogicalExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::NegationExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::DereferenceExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::ArrayIndexExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_array_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  const Analysis::NodeMapping &get_mappings () const { return node_mappings; }
+
+  const Analysis::NodeMapping &get_lvalue_mappings () const
+  {
+    return lvalue_mappings;
+  }
+
+  Location get_locus () const { return locus; }
+
+private:
+  const Analysis::NodeMapping node_mappings;
+  const Analysis::NodeMapping lvalue_mappings;
+  Location locus;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h
new file mode 100644
index 00000000000..394b04f6c7f
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-item.h
@@ -0,0 +1,3207 @@
+// 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/>.
+
+#ifndef RUST_HIR_ITEM_H
+#define RUST_HIR_ITEM_H
+
+#include "rust-abi.h"
+#include "rust-ast-full-decls.h"
+#include "rust-common.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+
+namespace Rust {
+namespace HIR {
+// forward decls
+class BlockExpr;
+class TypePath;
+
+// A type generic parameter (as opposed to a lifetime generic parameter)
+class TypeParam : public GenericParam
+{
+  // bool has_outer_attribute;
+  // std::unique_ptr<Attribute> outer_attr;
+  AST::Attribute outer_attr;
+
+  Identifier type_representation;
+
+  // bool has_type_param_bounds;
+  // TypeParamBounds type_param_bounds;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  Location locus;
+
+public:
+  // Returns whether the type of the type param has been specified.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the type param has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether the type param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  TypeParam (Analysis::NodeMapping mappings, Identifier type_representation,
+	     Location locus = Location (),
+	     std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds
+	     = std::vector<std::unique_ptr<TypeParamBound>> (),
+	     std::unique_ptr<Type> type = nullptr,
+	     AST::Attribute outer_attr = AST::Attribute::create_empty ())
+    : GenericParam (mappings), outer_attr (std::move (outer_attr)),
+      type_representation (std::move (type_representation)),
+      type_param_bounds (std::move (type_param_bounds)),
+      type (std::move (type)), locus (locus)
+  {}
+
+  // Copy constructor uses clone
+  TypeParam (TypeParam const &other)
+    : GenericParam (other.mappings), outer_attr (other.outer_attr),
+      type_representation (other.type_representation), locus (other.locus)
+  {
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeParam &operator= (TypeParam const &other)
+  {
+    type_representation = other.type_representation;
+    outer_attr = other.outer_attr;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+  // move constructors
+  TypeParam (TypeParam &&other) = default;
+  TypeParam &operator= (TypeParam &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Identifier get_type_representation () const { return type_representation; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  Analysis::NodeMapping get_type_mappings () const
+  {
+    rust_assert (type != nullptr);
+    return type->get_mappings ();
+  }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TypeParam *clone_generic_param_impl () const override
+  {
+    return new TypeParam (*this);
+  }
+};
+
+/* "where" clause item base. Abstract - use LifetimeWhereClauseItem,
+ * TypeBoundWhereClauseItem */
+class WhereClauseItem
+{
+public:
+  enum ItemType
+  {
+    LIFETIME,
+    TYPE_BOUND,
+  };
+
+  virtual ~WhereClauseItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<WhereClauseItem> clone_where_clause_item () const
+  {
+    return std::unique_ptr<WhereClauseItem> (clone_where_clause_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const = 0;
+
+  virtual ItemType get_item_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual WhereClauseItem *clone_where_clause_item_impl () const = 0;
+};
+
+// A lifetime where clause item
+class LifetimeWhereClauseItem : public WhereClauseItem
+{
+  Lifetime lifetime;
+  std::vector<Lifetime> lifetime_bounds;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  LifetimeWhereClauseItem (Analysis::NodeMapping mappings, Lifetime lifetime,
+			   std::vector<Lifetime> lifetime_bounds,
+			   Location locus)
+    : lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)), locus (locus),
+      mappings (std::move (mappings))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  std::vector<Lifetime> &get_lifetime_bounds () { return lifetime_bounds; }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  };
+
+  ItemType get_item_type () const override final
+  {
+    return WhereClauseItem::ItemType::LIFETIME;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  LifetimeWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new LifetimeWhereClauseItem (*this);
+  }
+};
+
+// A type bound where clause item
+class TypeBoundWhereClauseItem : public WhereClauseItem
+{
+  std::vector<LifetimeParam> for_lifetimes;
+  std::unique_ptr<Type> bound_type;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  Analysis::NodeMapping mappings;
+  Location locus;
+
+public:
+  // Returns whether the item has ForLifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  // Returns whether the item has type param bounds
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TypeBoundWhereClauseItem (
+    Analysis::NodeMapping mappings, std::vector<LifetimeParam> for_lifetimes,
+    std::unique_ptr<Type> bound_type,
+    std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+    Location locus)
+    : for_lifetimes (std::move (for_lifetimes)),
+      bound_type (std::move (bound_type)),
+      type_param_bounds (std::move (type_param_bounds)),
+      mappings (std::move (mappings)), locus (locus)
+  {}
+
+  // Copy constructor requires clone
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other)
+    : for_lifetimes (other.for_lifetimes),
+      bound_type (other.bound_type->clone_type ()), mappings (other.mappings)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overload assignment operator to clone
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other)
+  {
+    mappings = other.mappings;
+    for_lifetimes = other.for_lifetimes;
+    bound_type = other.bound_type->clone_type ();
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default;
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<LifetimeParam> &get_for_lifetimes () { return for_lifetimes; }
+
+  std::unique_ptr<Type> &get_bound_type () { return bound_type; }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  };
+
+  ItemType get_item_type () const override final
+  {
+    return WhereClauseItem::ItemType::TYPE_BOUND;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TypeBoundWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new TypeBoundWhereClauseItem (*this);
+  }
+};
+
+// A where clause
+struct WhereClause
+{
+private:
+  std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items;
+
+  // should this store location info?
+
+public:
+  WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items)
+    : where_clause_items (std::move (where_clause_items))
+  {}
+
+  // copy constructor with vector clone
+  WhereClause (WhereClause const &other)
+  {
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+  }
+
+  // overloaded assignment operator with vector clone
+  WhereClause &operator= (WhereClause const &other)
+  {
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhereClause (WhereClause &&other) = default;
+  WhereClause &operator= (WhereClause &&other) = default;
+
+  // Creates a WhereClause with no items.
+  static WhereClause create_empty ()
+  {
+    return WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> ());
+  }
+
+  // Returns whether the WhereClause has no items.
+  bool is_empty () const { return where_clause_items.empty (); }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<WhereClauseItem>> &get_items ()
+  {
+    return where_clause_items;
+  }
+  const std::vector<std::unique_ptr<WhereClauseItem>> &get_items () const
+  {
+    return where_clause_items;
+  }
+};
+
+// A self parameter in a method
+struct SelfParam
+{
+public:
+  enum ImplicitSelfKind
+  {
+    IMM,     // self
+    MUT,     // mut self
+    IMM_REF, // &self
+    MUT_REF, // &mut self
+    NONE
+  };
+
+private:
+  ImplicitSelfKind self_kind;
+  Lifetime lifetime;
+  std::unique_ptr<Type> type;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+  SelfParam (Analysis::NodeMapping mappings, ImplicitSelfKind self_kind,
+	     Lifetime lifetime, Type *type)
+    : self_kind (self_kind), lifetime (std::move (lifetime)), type (type),
+      mappings (mappings)
+  {}
+
+public:
+  // Type-based self parameter (not ref, no lifetime)
+  SelfParam (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     bool is_mut, Location locus)
+    : self_kind (is_mut ? ImplicitSelfKind::MUT : ImplicitSelfKind::IMM),
+      lifetime (
+	Lifetime (mappings, AST::Lifetime::LifetimeType::NAMED, "", locus)),
+      type (std::move (type)), locus (locus), mappings (mappings)
+  {}
+
+  // Lifetime-based self parameter (is ref, no type)
+  SelfParam (Analysis::NodeMapping mappings, Lifetime lifetime, bool is_mut,
+	     Location locus)
+    : self_kind (is_mut ? ImplicitSelfKind::MUT_REF
+			: ImplicitSelfKind::IMM_REF),
+      lifetime (std::move (lifetime)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  SelfParam (SelfParam const &other)
+    : self_kind (other.self_kind), lifetime (other.lifetime),
+      locus (other.locus), mappings (other.mappings)
+  {
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overload assignment operator to use clone
+  SelfParam &operator= (SelfParam const &other)
+  {
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+
+    self_kind = other.self_kind;
+    lifetime = other.lifetime;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  SelfParam (SelfParam &&other) = default;
+  SelfParam &operator= (SelfParam &&other) = default;
+
+  static SelfParam error ()
+  {
+    return SelfParam (Analysis::NodeMapping::get_error (),
+		      ImplicitSelfKind::NONE, Lifetime::error (), nullptr);
+  }
+
+  // Returns whether the self-param has a type field.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the self-param has a valid lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Returns whether the self-param is in an error state.
+  bool is_error () const { return self_kind == ImplicitSelfKind::NONE; }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  ImplicitSelfKind get_self_kind () const { return self_kind; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type ());
+    return type;
+  }
+
+  Analysis::NodeMapping get_mappings () { return mappings; }
+
+  Mutability get_mut () const
+  {
+    return (self_kind == ImplicitSelfKind::MUT
+	    || self_kind == ImplicitSelfKind::MUT_REF)
+	     ? Mutability::Mut
+	     : Mutability::Imm;
+  }
+
+  bool is_mut () const
+  {
+    return self_kind == ImplicitSelfKind::MUT
+	   || self_kind == ImplicitSelfKind::MUT_REF;
+  }
+
+  bool is_ref () const
+  {
+    return self_kind == ImplicitSelfKind::IMM_REF
+	   || self_kind == ImplicitSelfKind::MUT_REF;
+  }
+};
+
+// Qualifiers for function, i.e. const, unsafe, extern etc.
+struct FunctionQualifiers
+{
+private:
+  AsyncConstStatus const_status;
+  Unsafety unsafety;
+  bool has_extern;
+  ABI abi;
+
+public:
+  FunctionQualifiers (AsyncConstStatus const_status, Unsafety unsafety,
+		      bool has_extern, ABI abi)
+    : const_status (const_status), unsafety (unsafety), has_extern (has_extern),
+      abi (abi)
+  {}
+
+  std::string as_string () const;
+
+  AsyncConstStatus get_status () const { return const_status; }
+
+  bool is_const () const { return const_status == AsyncConstStatus::CONST_FN; }
+  bool is_unsafe () const { return unsafety == Unsafety::Unsafe; }
+
+  ABI get_abi () const { return abi; }
+};
+
+// A function parameter
+struct FunctionParam
+{
+  std::unique_ptr<Pattern> param_name;
+  std::unique_ptr<Type> type;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  FunctionParam (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Pattern> param_name,
+		 std::unique_ptr<Type> param_type, Location locus)
+    : param_name (std::move (param_name)), type (std::move (param_type)),
+      locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor uses clone
+  FunctionParam (FunctionParam const &other)
+    : param_name (other.param_name->clone_pattern ()),
+      type (other.type->clone_type ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to use clone
+  FunctionParam &operator= (FunctionParam const &other)
+  {
+    param_name = other.param_name->clone_pattern ();
+    type = other.type->clone_type ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  FunctionParam (FunctionParam &&other) = default;
+  FunctionParam &operator= (FunctionParam &&other) = default;
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  Pattern *get_param_name () { return param_name.get (); }
+
+  Type *get_type () { return type.get (); }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// Visibility of an item
+struct Visibility
+{
+public:
+  enum VisType
+  {
+    PRIVATE,
+    PUBLIC,
+    RESTRICTED,
+    ERROR,
+  };
+
+private:
+  VisType vis_type;
+  HIR::SimplePath path;
+
+  // should this store location info?
+
+public:
+  Visibility (VisType vis_type,
+	      HIR::SimplePath path = HIR::SimplePath::create_empty ())
+    : vis_type (vis_type), path (std::move (path))
+  {}
+
+  // Returns whether visibility is in an error state.
+  bool is_error () const { return vis_type == ERROR; }
+
+  // Does the current visibility refer to a simple `pub <item>` entirely public
+  bool is_public () const { return vis_type == PUBLIC; }
+
+  // Is the current visibility public restricted to a certain path
+  bool is_restricted () const { return vis_type == RESTRICTED; }
+
+  // Creates an error visibility.
+  static Visibility create_error ()
+  {
+    return Visibility (ERROR, HIR::SimplePath::create_empty ());
+  }
+
+  VisType get_vis_type () const { return vis_type; }
+
+  const HIR::SimplePath &get_path () const
+  {
+    rust_assert (!is_error ());
+    return path;
+  }
+
+  std::string as_string () const;
+};
+
+// Item that supports visibility - abstract base class
+class VisItem : public Item
+{
+  Visibility visibility;
+
+protected:
+  // Visibility constructor
+  VisItem (Analysis::NodeMapping mappings, Visibility visibility,
+	   AST::AttrVec outer_attrs = AST::AttrVec ())
+    : Item (std::move (mappings), std::move (outer_attrs)),
+      visibility (std::move (visibility))
+  {}
+
+  // Visibility copy constructor
+  VisItem (VisItem const &other) : Item (other), visibility (other.visibility)
+  {}
+
+  // Overload assignment operator to clone
+  VisItem &operator= (VisItem const &other)
+  {
+    Item::operator= (other);
+    visibility = other.visibility;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  VisItem (VisItem &&other) = default;
+  VisItem &operator= (VisItem &&other) = default;
+
+public:
+  using HIR::Stmt::accept_vis;
+
+  BaseKind get_hir_kind () override final { return VIS_ITEM; }
+
+  /* Does the item have some kind of public visibility (non-default
+   * visibility)? */
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  virtual void accept_vis (HIRVisItemVisitor &vis) = 0;
+
+  Visibility &get_visibility () { return visibility; }
+  const Visibility &get_visibility () const { return visibility; }
+
+  std::string as_string () const override;
+};
+
+// Rust module item - abstract base class
+class Module : public VisItem
+{
+  Identifier module_name;
+  Location locus;
+  // bool has_inner_attrs;
+  AST::AttrVec inner_attrs;
+  // bool has_items;
+  std::vector<std::unique_ptr<Item>> items;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the module has items in its body.
+  bool has_items () const { return !items.empty (); }
+
+  // Returns whether the module has any inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Full constructor
+  Module (Analysis::NodeMapping mappings, Identifier module_name,
+	  Location locus, std::vector<std::unique_ptr<Item>> items,
+	  Visibility visibility = Visibility::create_error (),
+	  AST::AttrVec inner_attrs = AST::AttrVec (),
+	  AST::AttrVec outer_attrs = AST::AttrVec ())
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      module_name (module_name), locus (locus),
+      inner_attrs (std::move (inner_attrs)), items (std::move (items))
+  {}
+
+  // Copy constructor with vector clone
+  Module (Module const &other)
+    : VisItem (other), inner_attrs (other.inner_attrs)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Module &operator= (Module const &other)
+  {
+    VisItem::operator= (other);
+    inner_attrs = other.inner_attrs;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  Module (Module &&other) = default;
+  Module &operator= (Module &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Item>> &get_items () { return items; };
+
+  /* Override that runs the function recursively on all items contained within
+   * the module. */
+  void add_crate_name (std::vector<std::string> &names) const override;
+
+  Location get_locus () const override final { return locus; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Module; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Module *clone_item_impl () const override { return new Module (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual Module* clone_statement_impl() const override {
+      return new Module(*this);
+  }*/
+};
+
+// Rust extern crate declaration HIR node
+class ExternCrate : public VisItem
+{
+  // this is either an identifier or "self", with self parsed to string
+  std::string referenced_crate;
+  // bool has_as_clause;
+  // AsClause as_clause;
+  // this is either an identifier or "_", with _ parsed to string
+  std::string as_clause_name;
+
+  Location locus;
+
+  /* e.g.
+      "extern crate foo as _"
+      "extern crate foo"
+      "extern crate std as cool_std"  */
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern crate declaration has an as clause.
+  bool has_as_clause () const { return !as_clause_name.empty (); }
+
+  /* Returns whether extern crate declaration references the current crate
+   * (i.e. self). */
+  bool references_self () const { return referenced_crate == "self"; }
+
+  // Constructor
+  ExternCrate (Analysis::NodeMapping mappings, std::string referenced_crate,
+	       Visibility visibility, AST::AttrVec outer_attrs, Location locus,
+	       std::string as_clause_name = std::string ())
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      referenced_crate (std::move (referenced_crate)),
+      as_clause_name (std::move (as_clause_name)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  ItemKind get_item_kind () const override { return ItemKind::ExternCrate; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  // Override that adds extern crate name in decl to passed list of names.
+  void add_crate_name (std::vector<std::string> &names) const override
+  {
+    names.push_back (referenced_crate);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternCrate *clone_item_impl () const override
+  {
+    return new ExternCrate (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual ExternCrate* clone_statement_impl() const override {
+      return new ExternCrate(*this);
+  }*/
+};
+
+// The path-ish thing referred to in a use declaration - abstract base class
+class UseTree
+{
+  Location locus;
+
+public:
+  virtual ~UseTree () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<UseTree> clone_use_tree () const
+  {
+    return std::unique_ptr<UseTree> (clone_use_tree_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual UseTree *clone_use_tree_impl () const = 0;
+
+  UseTree (Location locus) : locus (locus) {}
+};
+
+// Use tree with a glob (wildcard) operator
+class UseTreeGlob : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType glob_type;
+  AST::SimplePath path;
+
+public:
+  UseTreeGlob (PathType glob_type, AST::SimplePath path, Location locus)
+    : UseTree (locus), glob_type (glob_type), path (std::move (path))
+  {
+    if (this->glob_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	gcc_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  /* Returns whether has path. Should be made redundant by PathType
+   * PATH_PREFIXED. */
+  bool has_path () const { return !path.is_empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  /* TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory
+   * methods? */
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeGlob *clone_use_tree_impl () const override
+  {
+    return new UseTreeGlob (*this);
+  }
+};
+
+// Use tree with a list of paths with a common prefix
+class UseTreeList : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType path_type;
+  AST::SimplePath path;
+
+  std::vector<std::unique_ptr<UseTree>> trees;
+
+public:
+  UseTreeList (PathType path_type, AST::SimplePath path,
+	       std::vector<std::unique_ptr<UseTree>> trees, Location locus)
+    : UseTree (locus), path_type (path_type), path (std::move (path)),
+      trees (std::move (trees))
+  {
+    if (this->path_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	gcc_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  // copy constructor with vector clone
+  UseTreeList (UseTreeList const &other)
+    : UseTree (other), path_type (other.path_type), path (other.path)
+  {
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+  }
+
+  // overloaded assignment operator with vector clone
+  UseTreeList &operator= (UseTreeList const &other)
+  {
+    UseTree::operator= (other);
+    path_type = other.path_type;
+    path = other.path;
+
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+
+    return *this;
+  }
+
+  // move constructors
+  UseTreeList (UseTreeList &&other) = default;
+  UseTreeList &operator= (UseTreeList &&other) = default;
+
+  // Returns whether has path. Should be made redundant by path_type.
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has inner tree elements.
+  bool has_trees () const { return !trees.empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeList *clone_use_tree_impl () const override
+  {
+    return new UseTreeList (*this);
+  }
+};
+
+// Use tree where it rebinds the module name as something else
+class UseTreeRebind : public UseTree
+{
+public:
+  enum NewBindType
+  {
+    NONE,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  AST::SimplePath path;
+
+  NewBindType bind_type;
+  Identifier identifier; // only if NewBindType is IDENTIFIER
+
+public:
+  UseTreeRebind (NewBindType bind_type, AST::SimplePath path, Location locus,
+		 Identifier identifier = std::string ())
+    : UseTree (locus), path (std::move (path)), bind_type (bind_type),
+      identifier (std::move (identifier))
+  {}
+
+  // Returns whether has path (this should always be true).
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has identifier (or, rather, is allowed to).
+  bool has_identifier () const { return bind_type == IDENTIFIER; }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  virtual UseTreeRebind *clone_use_tree_impl () const override
+  {
+    return new UseTreeRebind (*this);
+  }
+};
+
+// Rust use declaration (i.e. for modules) HIR node
+class UseDeclaration : public VisItem
+{
+  std::unique_ptr<UseTree> use_tree;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UseDeclaration (Analysis::NodeMapping mappings,
+		  std::unique_ptr<UseTree> use_tree, Visibility visibility,
+		  AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      use_tree (std::move (use_tree)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UseDeclaration (UseDeclaration const &other)
+    : VisItem (other), use_tree (other.use_tree->clone_use_tree ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  UseDeclaration &operator= (UseDeclaration const &other)
+  {
+    VisItem::operator= (other);
+    use_tree = other.use_tree->clone_use_tree ();
+    // visibility = other.visibility->clone_visibility();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  UseDeclaration (UseDeclaration &&other) = default;
+  UseDeclaration &operator= (UseDeclaration &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+  ItemKind get_item_kind () const override { return ItemKind::UseDeclaration; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseDeclaration *clone_item_impl () const override
+  {
+    return new UseDeclaration (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual UseDeclaration* clone_statement_impl() const override {
+      return new UseDeclaration(*this);
+  }*/
+};
+
+class LetStmt;
+
+// Rust function declaration HIR node
+class Function : public VisItem, public ImplItem
+{
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  std::unique_ptr<BlockExpr> function_body;
+  SelfParam self;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether function has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function has regular parameters.
+  bool has_function_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type - if not, it is void.
+  bool has_function_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::FUNCTION;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Function; }
+
+  // Mega-constructor with all possible fields
+  Function (Analysis::NodeMapping mappings, Identifier function_name,
+	    FunctionQualifiers qualifiers,
+	    std::vector<std::unique_ptr<GenericParam>> generic_params,
+	    std::vector<FunctionParam> function_params,
+	    std::unique_ptr<Type> return_type, WhereClause where_clause,
+	    std::unique_ptr<BlockExpr> function_body, Visibility vis,
+	    AST::AttrVec outer_attrs, SelfParam self, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_body (std::move (function_body)), self (std::move (self)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  Function (Function const &other)
+    : VisItem (other), qualifiers (other.qualifiers),
+      function_name (other.function_name),
+      function_params (other.function_params),
+      where_clause (other.where_clause),
+      function_body (other.function_body->clone_block_expr ()),
+      self (other.self), locus (other.locus)
+  {
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  Function &operator= (Function const &other)
+  {
+    VisItem::operator= (other);
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    where_clause = other.where_clause;
+    function_body = other.function_body->clone_block_expr ();
+    locus = other.locus;
+    self = other.self;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Function (Function &&other) = default;
+  Function &operator= (Function &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition ()
+  {
+    rust_assert (function_body != nullptr);
+    return function_body;
+  }
+
+  const FunctionQualifiers &get_qualifiers () const { return qualifiers; }
+
+  Identifier get_function_name () const { return function_name; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  bool is_method () const { return !self.is_error (); }
+
+  SelfParam &get_self_param () { return self; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_item_impl () const override { return new Function (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_inherent_impl_item_impl () const override
+  {
+    return new Function (*this);
+  }
+};
+
+// Rust type alias (i.e. typedef) HIR node
+class TypeAlias : public VisItem, public ImplItem
+{
+  Identifier new_type_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::unique_ptr<Type> existing_type;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether type alias has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether type alias has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::TYPE_ALIAS;
+  }
+
+  // Mega-constructor with all possible fields
+  TypeAlias (Analysis::NodeMapping mappings, Identifier new_type_name,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     WhereClause where_clause, std::unique_ptr<Type> existing_type,
+	     Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      new_type_name (std::move (new_type_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)),
+      existing_type (std::move (existing_type)), locus (locus)
+  {}
+
+  // Copy constructor
+  TypeAlias (TypeAlias const &other)
+    : VisItem (other), new_type_name (other.new_type_name),
+      where_clause (other.where_clause),
+      existing_type (other.existing_type->clone_type ()), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeAlias &operator= (TypeAlias const &other)
+  {
+    VisItem::operator= (other);
+    new_type_name = other.new_type_name;
+    where_clause = other.where_clause;
+    existing_type = other.existing_type->clone_type ();
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeAlias (TypeAlias &&other) = default;
+  TypeAlias &operator= (TypeAlias &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  std::unique_ptr<Type> &get_type_aliased ()
+  {
+    rust_assert (existing_type != nullptr);
+    return existing_type;
+  }
+
+  Identifier get_new_type_name () const { return new_type_name; }
+
+  ItemKind get_item_kind () const override { return ItemKind::TypeAlias; }
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_item_impl () const override { return new TypeAlias (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_inherent_impl_item_impl () const override
+  {
+    return new TypeAlias (*this);
+  }
+};
+
+// Rust base struct declaration HIR node - abstract base class
+class Struct : public VisItem
+{
+protected:
+  // protected to enable access by derived classes - allows better as_string
+  Identifier struct_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  Location locus;
+
+public:
+  Identifier get_identifier () const { return struct_name; }
+
+  // Returns whether struct has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether struct has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Location get_locus () const override final { return locus; }
+  ItemKind get_item_kind () const override { return ItemKind::Struct; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+protected:
+  Struct (Analysis::NodeMapping mappings, Identifier struct_name,
+	  std::vector<std::unique_ptr<GenericParam>> generic_params,
+	  WhereClause where_clause, Visibility vis, Location locus,
+	  AST::AttrVec outer_attrs = AST::AttrVec ())
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      struct_name (std::move (struct_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Struct (Struct const &other)
+    : VisItem (other), struct_name (other.struct_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Struct &operator= (Struct const &other)
+  {
+    VisItem::operator= (other);
+    struct_name = other.struct_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Struct (Struct &&other) = default;
+  Struct &operator= (Struct &&other) = default;
+};
+
+// A single field in a struct
+struct StructField
+{
+public:
+  // bool has_outer_attributes;
+  AST::AttrVec outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier field_name;
+  std::unique_ptr<Type> field_type;
+
+  Analysis::NodeMapping mappings;
+
+  Location locus;
+
+  // Returns whether struct field has any outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  // Returns whether struct field has a non-private (non-default) visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  StructField (Analysis::NodeMapping mappings, Identifier field_name,
+	       std::unique_ptr<Type> field_type, Visibility vis, Location locus,
+	       AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_name (std::move (field_name)), field_type (std::move (field_type)),
+      mappings (mappings), locus (locus)
+  {}
+
+  // Copy constructor
+  StructField (StructField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      field_name (other.field_name),
+      field_type (other.field_type->clone_type ()), mappings (other.mappings)
+  {}
+
+  ~StructField () = default;
+
+  // Overloaded assignment operator to clone
+  StructField &operator= (StructField const &other)
+  {
+    field_name = other.field_name;
+    field_type = other.field_type->clone_type ();
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  StructField (StructField &&other) = default;
+  StructField &operator= (StructField &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_field_name () const { return field_name; }
+
+  std::unique_ptr<Type> &get_field_type ()
+  {
+    rust_assert (field_type != nullptr);
+    return field_type;
+  }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Location get_locus () { return locus; }
+
+  Visibility &get_visibility () { return visibility; }
+};
+
+// Rust struct declaration with true struct type HIR node
+class StructStruct : public Struct
+{
+public:
+  std::vector<StructField> fields;
+  bool is_unit;
+
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  StructStruct (Analysis::NodeMapping mappings, std::vector<StructField> fields,
+		Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, bool is_unit, Visibility vis,
+		AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      fields (std::move (fields)), is_unit (is_unit)
+  {}
+
+  // Unit struct constructor
+  StructStruct (Analysis::NodeMapping mappings, Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, Visibility vis,
+		AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      is_unit (true)
+  {}
+  // TODO: can a unit struct have generic fields? assuming yes for now.
+
+  /* Returns whether the struct is a unit struct - struct defined without
+   * fields. This is important because it also means an implicit constant of its
+   * type is defined. */
+  bool is_unit_struct () const { return is_unit; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<StructField> &get_fields () { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  StructStruct *clone_item_impl () const override
+  {
+    return new StructStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual StructStruct* clone_statement_impl() const override {
+      return new StructStruct(*this);
+  }*/
+};
+
+// A single field in a tuple
+struct TupleField
+{
+private:
+  // bool has_outer_attributes;
+  AST::AttrVec outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  std::unique_ptr<Type> field_type;
+
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Returns whether tuple field has outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  /* Returns whether tuple field has a non-default visibility (i.e. a public
+   * one) */
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Complete constructor
+  TupleField (Analysis::NodeMapping mapping, std::unique_ptr<Type> field_type,
+	      Visibility vis, Location locus,
+	      AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_type (std::move (field_type)), locus (locus), mappings (mapping)
+  {}
+
+  // Copy constructor with clone
+  TupleField (TupleField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      field_type (other.field_type->clone_type ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  ~TupleField () = default;
+
+  // Overloaded assignment operator to clone
+  TupleField &operator= (TupleField const &other)
+  {
+    field_type = other.field_type->clone_type ();
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleField (TupleField &&other) = default;
+  TupleField &operator= (TupleField &&other) = default;
+
+  // Returns whether tuple field is in an error state.
+  bool is_error () const { return field_type == nullptr; }
+
+  std::string as_string () const;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Location get_locus () const { return locus; }
+
+  std::unique_ptr<HIR::Type> &get_field_type () { return field_type; }
+};
+
+// Rust tuple declared using struct keyword HIR node
+class TupleStruct : public Struct
+{
+  std::vector<TupleField> fields;
+
+public:
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  TupleStruct (Analysis::NodeMapping mappings, std::vector<TupleField> fields,
+	       Identifier struct_name,
+	       std::vector<std::unique_ptr<GenericParam>> generic_params,
+	       WhereClause where_clause, Visibility vis,
+	       AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      fields (std::move (fields))
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<TupleField> &get_fields () { return fields; }
+  const std::vector<TupleField> &get_fields () const { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TupleStruct *clone_item_impl () const override
+  {
+    return new TupleStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual TupleStruct* clone_statement_impl() const override {
+      return new TupleStruct(*this);
+  }*/
+};
+
+/* An item used in an "enum" tagged union - not abstract: base represents a
+   name-only enum. Syntactically EnumItem's can have a Visibility. But not
+   Semantically. So check there is no Visibility when lowering and make this
+   an Item, not an VisItem.  */
+class EnumItem : public Item
+{
+  Identifier variant_name;
+  Location locus;
+
+public:
+  virtual ~EnumItem () {}
+
+  enum EnumItemKind
+  {
+    Named,
+    Tuple,
+    Struct,
+    Discriminant,
+  };
+
+  EnumItem (Analysis::NodeMapping mappings, Identifier variant_name,
+	    AST::AttrVec outer_attrs, Location locus)
+    : Item (std::move (mappings), std::move (outer_attrs)),
+      variant_name (std::move (variant_name)), locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<EnumItem> clone_enum_item () const
+  {
+    return std::unique_ptr<EnumItem> (clone_item_impl ());
+  }
+
+  virtual std::string as_string () const override;
+  virtual EnumItemKind get_enum_item_kind () const { return Named; };
+
+  // not pure virtual as not abstract
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  // void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Location get_locus () const override { return locus; }
+
+  Identifier get_identifier () const { return variant_name; }
+
+  ItemKind get_item_kind () const override { return ItemKind::EnumItem; }
+
+protected:
+  EnumItem *clone_item_impl () const override { return new EnumItem (*this); }
+};
+
+// A tuple item used in an "enum" tagged union
+class EnumItemTuple : public EnumItem
+{
+  // bool has_tuple_fields;
+  std::vector<TupleField> tuple_fields;
+
+public:
+  // Returns whether tuple enum item has tuple fields.
+  bool has_tuple_fields () const { return !tuple_fields.empty (); }
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Tuple;
+  }
+
+  EnumItemTuple (Analysis::NodeMapping mappings, Identifier variant_name,
+		 std::vector<TupleField> tuple_fields, AST::AttrVec outer_attrs,
+		 Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      tuple_fields (std::move (tuple_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::vector<TupleField> &get_tuple_fields () { return tuple_fields; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemTuple *clone_item_impl () const override
+  {
+    return new EnumItemTuple (*this);
+  }
+};
+
+// A struct item used in an "enum" tagged union
+class EnumItemStruct : public EnumItem
+{
+  // bool has_struct_fields;
+  std::vector<StructField> struct_fields;
+
+public:
+  // Returns whether struct enum item has struct fields.
+  bool has_struct_fields () const { return !struct_fields.empty (); }
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Struct;
+  }
+
+  EnumItemStruct (Analysis::NodeMapping mappings, Identifier variant_name,
+		  std::vector<StructField> struct_fields,
+		  AST::AttrVec outer_attrs, Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      struct_fields (std::move (struct_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::vector<StructField> &get_struct_fields () { return struct_fields; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemStruct *clone_item_impl () const override
+  {
+    return new EnumItemStruct (*this);
+  }
+};
+
+// A discriminant (numbered enum) item used in an "enum" tagged union
+class EnumItemDiscriminant : public EnumItem
+{
+  std::unique_ptr<Expr> expression;
+
+public:
+  EnumItemDiscriminant (Analysis::NodeMapping mappings, Identifier variant_name,
+			std::unique_ptr<Expr> expr, AST::AttrVec outer_attrs,
+			Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      expression (std::move (expr))
+  {}
+
+  // Copy constructor with clone
+  EnumItemDiscriminant (EnumItemDiscriminant const &other)
+    : EnumItem (other), expression (other.expression->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other)
+  {
+    EnumItem::operator= (other);
+    expression = other.expression->clone_expr ();
+    // variant_name = other.variant_name;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  EnumItemDiscriminant (EnumItemDiscriminant &&other) = default;
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default;
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Discriminant;
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_discriminant_expression () { return expression; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemDiscriminant *clone_item_impl () const override
+  {
+    return new EnumItemDiscriminant (*this);
+  }
+};
+
+// HIR node for Rust "enum" - tagged union
+class Enum : public VisItem
+{
+  Identifier enum_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<std::unique_ptr<EnumItem>> items;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether "enum" has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether "enum" has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  /* Returns whether enum is a "zero-variant" (no possible variant) enum,
+   * which cannot be instantiated. */
+  bool is_zero_variant () const { return items.empty (); }
+
+  // Mega-constructor
+  Enum (Analysis::NodeMapping mappings, Identifier enum_name, Visibility vis,
+	std::vector<std::unique_ptr<GenericParam>> generic_params,
+	WhereClause where_clause, std::vector<std::unique_ptr<EnumItem>> items,
+	AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      enum_name (std::move (enum_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), items (std::move (items)),
+      locus (locus)
+  {}
+
+  // TODO: constructor with less arguments
+
+  // Copy constructor with vector clone
+  Enum (Enum const &other)
+    : VisItem (other), enum_name (other.enum_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Enum &operator= (Enum const &other)
+  {
+    VisItem::operator= (other);
+    enum_name = other.enum_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Enum (Enum &&other) = default;
+  Enum &operator= (Enum &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Identifier get_identifier () const { return enum_name; }
+  ItemKind get_item_kind () const override { return ItemKind::Enum; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  const std::vector<std::unique_ptr<EnumItem>> &get_variants () const
+  {
+    return items;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Enum *clone_item_impl () const override { return new Enum (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual Enum* clone_statement_impl() const override {
+      return new Enum(*this);
+  }*/
+};
+
+// Rust untagged union used for C compat HIR node
+class Union : public VisItem
+{
+  Identifier union_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<StructField> variants;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether union has generic params.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether union has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Union (Analysis::NodeMapping mappings, Identifier union_name, Visibility vis,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 WhereClause where_clause, std::vector<StructField> variants,
+	 AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      union_name (std::move (union_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), variants (std::move (variants)),
+      locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  Union (Union const &other)
+    : VisItem (other), union_name (other.union_name),
+      where_clause (other.where_clause), variants (other.variants),
+      locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // overloaded assignment operator with vector clone
+  Union &operator= (Union const &other)
+  {
+    VisItem::operator= (other);
+    union_name = other.union_name;
+    where_clause = other.where_clause;
+    variants = other.variants;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Union (Union &&other) = default;
+  Union &operator= (Union &&other) = default;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  Identifier get_identifier () const { return union_name; }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<StructField> &get_variants () { return variants; }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Union; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Union *clone_item_impl () const override { return new Union (*this); }
+};
+
+class ConstantItem : public VisItem, public ImplItem
+{
+  Identifier identifier;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> const_expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ConstantItem (Analysis::NodeMapping mappings, Identifier ident,
+		Visibility vis, std::unique_ptr<Type> type,
+		std::unique_ptr<Expr> const_expr, AST::AttrVec outer_attrs,
+		Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      identifier (std::move (ident)), type (std::move (type)),
+      const_expr (std::move (const_expr)), locus (locus)
+  {}
+
+  ConstantItem (ConstantItem const &other)
+    : VisItem (other), identifier (other.identifier),
+      type (other.type->clone_type ()),
+      const_expr (other.const_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone
+  ConstantItem &operator= (ConstantItem const &other)
+  {
+    VisItem::operator= (other);
+    identifier = other.identifier;
+    type = other.type->clone_type ();
+    const_expr = other.const_expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ConstantItem (ConstantItem &&other) = default;
+  ConstantItem &operator= (ConstantItem &&other) = default;
+
+  // Returns whether constant item is an "unnamed" (wildcard underscore used
+  // as identifier) constant.
+  bool is_unnamed () const { return identifier == std::string ("_"); }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Type *get_type () { return type.get (); }
+
+  Expr *get_expr () { return const_expr.get (); }
+
+  std::string get_identifier () { return identifier; }
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::CONSTANT;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Constant; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_inherent_impl_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+};
+
+/* Static item HIR node - items within module scope with fixed storage
+ * duration? */
+class StaticItem : public VisItem
+{
+  Mutability mut;
+  Identifier name;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  StaticItem (Analysis::NodeMapping mappings, Identifier name, Mutability mut,
+	      std::unique_ptr<Type> type, std::unique_ptr<Expr> expr,
+	      Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      mut (mut), name (std::move (name)), type (std::move (type)),
+      expr (std::move (expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  StaticItem (StaticItem const &other)
+    : VisItem (other), mut (other.mut), name (other.name),
+      type (other.type->clone_type ()), expr (other.expr->clone_expr ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  StaticItem &operator= (StaticItem const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    expr = other.expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  StaticItem (StaticItem &&other) = default;
+  StaticItem &operator= (StaticItem &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Identifier get_identifier () const { return name; }
+
+  Mutability get_mut () const { return mut; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  Expr *get_expr () { return expr.get (); }
+
+  Type *get_type () { return type.get (); }
+
+  ItemKind get_item_kind () const override { return ItemKind::Static; }
+
+protected:
+  StaticItem *clone_item_impl () const override
+  {
+    return new StaticItem (*this);
+  }
+};
+
+// Function declaration in traits
+struct TraitFunctionDecl
+{
+private:
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  SelfParam self;
+
+public:
+  // Mega-constructor
+  TraitFunctionDecl (Identifier function_name, FunctionQualifiers qualifiers,
+		     std::vector<std::unique_ptr<GenericParam>> generic_params,
+		     SelfParam self, std::vector<FunctionParam> function_params,
+		     std::unique_ptr<Type> return_type,
+		     WhereClause where_clause)
+    : qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)), self (std::move (self))
+  {}
+
+  // Copy constructor with clone
+  TraitFunctionDecl (TraitFunctionDecl const &other)
+    : qualifiers (other.qualifiers), function_name (other.function_name),
+      function_params (other.function_params),
+      return_type (other.return_type->clone_type ()),
+      where_clause (other.where_clause), self (other.self)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  ~TraitFunctionDecl () = default;
+
+  // Overloaded assignment operator with clone
+  TraitFunctionDecl &operator= (TraitFunctionDecl const &other)
+  {
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+    return_type = other.return_type->clone_type ();
+    where_clause = other.where_clause;
+    self = other.self;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitFunctionDecl (TraitFunctionDecl &&other) = default;
+  TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default;
+
+  std::string as_string () const;
+
+  // Returns whether function decl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function decl has regular parameters.
+  bool has_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type (otherwise is void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  bool is_method () const { return !self.is_error (); }
+
+  SelfParam &get_self () { return self; }
+
+  Identifier get_function_name () const { return function_name; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+
+  const FunctionQualifiers &get_qualifiers () const { return qualifiers; }
+};
+
+// Actual trait item function declaration within traits
+class TraitItemFunc : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+  TraitFunctionDecl decl;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  // Returns whether function has a definition or is just a declaration.
+  bool has_definition () const { return block_expr != nullptr; }
+
+  TraitItemFunc (Analysis::NodeMapping mappings, TraitFunctionDecl decl,
+		 std::unique_ptr<BlockExpr> block_expr,
+		 AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      decl (std::move (decl)), block_expr (std::move (block_expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TraitItemFunc (TraitItemFunc const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      decl (other.decl), locus (other.locus)
+  {
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  TraitItemFunc &operator= (TraitItemFunc const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    decl = other.decl;
+    locus = other.locus;
+    mappings = other.mappings;
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemFunc (TraitItemFunc &&other) = default;
+  TraitItemFunc &operator= (TraitItemFunc &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  TraitFunctionDecl &get_decl () { return decl; }
+
+  const TraitFunctionDecl &get_decl () const { return decl; }
+
+  bool has_block_defined () const { return block_expr != nullptr; }
+
+  std::unique_ptr<BlockExpr> &get_block_expr ()
+  {
+    rust_assert (has_block_defined ());
+    return block_expr;
+  }
+
+  const std::string trait_identifier () const override final
+  {
+    return decl.get_function_name ();
+  }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::FUNC;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemFunc *clone_trait_item_impl () const override
+  {
+    return new TraitItemFunc (*this);
+  }
+};
+
+// Constant item within traits
+class TraitItemConst : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+  Identifier name;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> expr;
+  Location locus;
+
+public:
+  // Whether the constant item has an associated expression.
+  bool has_expression () const { return expr != nullptr; }
+
+  TraitItemConst (Analysis::NodeMapping mappings, Identifier name,
+		  std::unique_ptr<Type> type, std::unique_ptr<Expr> expr,
+		  AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)), type (std::move (type)), expr (std::move (expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clones
+  TraitItemConst (TraitItemConst const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      name (other.name), type (other.type->clone_type ()),
+      expr (other.expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  TraitItemConst &operator= (TraitItemConst const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    type = other.type->clone_type ();
+    expr = other.expr->clone_expr ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemConst (TraitItemConst &&other) = default;
+  TraitItemConst &operator= (TraitItemConst &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  Identifier get_name () const { return name; }
+
+  bool has_expr () const { return expr != nullptr; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (has_expr ());
+    return expr;
+  }
+
+  const std::string trait_identifier () const override final { return name; }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::CONST;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemConst *clone_trait_item_impl () const override
+  {
+    return new TraitItemConst (*this);
+  }
+};
+
+// Type items within traits
+class TraitItemType : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+
+  Identifier name;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+  Location locus;
+
+public:
+  // Returns whether trait item type has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TraitItemType (Analysis::NodeMapping mappings, Identifier name,
+		 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+		 AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)),
+      type_param_bounds (std::move (type_param_bounds)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  TraitItemType (TraitItemType const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      name (other.name), locus (other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TraitItemType &operator= (TraitItemType const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // default move constructors
+  TraitItemType (TraitItemType &&other) = default;
+  TraitItemType &operator= (TraitItemType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  Identifier get_name () const { return name; }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::string trait_identifier () const override final { return name; }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::TYPE;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemType *clone_trait_item_impl () const override
+  {
+    return new TraitItemType (*this);
+  }
+};
+
+// Rust trait item declaration HIR node
+class Trait : public VisItem
+{
+  Unsafety unsafety;
+  Identifier name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  WhereClause where_clause;
+  std::vector<std::unique_ptr<TraitItem>> trait_items;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether trait has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether trait has type parameter bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether trait has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether trait has trait items.
+  bool has_trait_items () const { return !trait_items.empty (); }
+
+  std::vector<std::unique_ptr<TraitItem>> &get_trait_items ()
+  {
+    return trait_items;
+  }
+
+  Identifier get_name () const { return name; }
+
+  // Mega-constructor
+  Trait (Analysis::NodeMapping mappings, Identifier name, Unsafety unsafety,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+	 WhereClause where_clause,
+	 std::vector<std::unique_ptr<TraitItem>> trait_items, Visibility vis,
+	 AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      unsafety (unsafety), name (std::move (name)),
+      generic_params (std::move (generic_params)),
+      type_param_bounds (std::move (type_param_bounds)),
+      where_clause (std::move (where_clause)),
+      trait_items (std::move (trait_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Trait (Trait const &other)
+    : VisItem (other), unsafety (other.unsafety), name (other.name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Trait &operator= (Trait const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    unsafety = other.unsafety;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+
+    return *this;
+  }
+
+  // default move constructors
+  Trait (Trait &&other) = default;
+  Trait &operator= (Trait &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Trait; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Trait *clone_item_impl () const override { return new Trait (*this); }
+};
+
+class ImplBlock : public VisItem
+{
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::unique_ptr<Type> impl_type;
+  std::unique_ptr<TypePath> trait_ref;
+  WhereClause where_clause;
+  Polarity polarity;
+  AST::AttrVec inner_attrs;
+  Location locus;
+  std::vector<std::unique_ptr<ImplItem>> impl_items;
+
+public:
+  ImplBlock (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<ImplItem>> impl_items,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     std::unique_ptr<Type> impl_type,
+	     std::unique_ptr<TypePath> trait_ref, WhereClause where_clause,
+	     Polarity polarity, Visibility vis, AST::AttrVec inner_attrs,
+	     AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      generic_params (std::move (generic_params)),
+      impl_type (std::move (impl_type)), trait_ref (std::move (trait_ref)),
+      where_clause (std::move (where_clause)), polarity (polarity),
+      inner_attrs (std::move (inner_attrs)), locus (locus),
+      impl_items (std::move (impl_items))
+  {}
+
+  ImplBlock (ImplBlock const &other)
+    : VisItem (other), impl_type (other.impl_type->clone_type ()),
+      where_clause (other.where_clause), polarity (other.polarity),
+      inner_attrs (other.inner_attrs), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+  }
+
+  ImplBlock &operator= (ImplBlock const &other)
+  {
+    VisItem::operator= (other);
+    impl_type = other.impl_type->clone_type ();
+    where_clause = other.where_clause;
+    polarity = other.polarity;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+
+    return *this;
+  }
+
+  ImplBlock (ImplBlock &&other) = default;
+  ImplBlock &operator= (ImplBlock &&other) = default;
+
+  std::string as_string () const override;
+
+  // Returns whether inherent impl block has inherent impl items.
+  bool has_impl_items () const { return !impl_items.empty (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<ImplItem>> &get_impl_items ()
+  {
+    return impl_items;
+  };
+
+  const std::vector<std::unique_ptr<ImplItem>> &get_impl_items () const
+  {
+    return impl_items;
+  };
+
+  // Returns whether impl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether impl has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns the polarity of the impl.
+  Polarity get_polarity () const { return polarity; }
+
+  // Returns whether impl has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<Type> &get_type () { return impl_type; };
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  bool has_trait_ref () const { return trait_ref != nullptr; }
+
+  std::unique_ptr<TypePath> &get_trait_ref ()
+  {
+    rust_assert (has_trait_ref ());
+    return trait_ref;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Impl; }
+
+protected:
+  ImplBlock *clone_item_impl () const override { return new ImplBlock (*this); }
+};
+
+// Abstract base class for an item used inside an extern block
+class ExternalItem : public Node
+{
+  Analysis::NodeMapping mappings;
+  AST::AttrVec outer_attrs;
+  Visibility visibility;
+  Identifier item_name;
+  Location locus;
+
+public:
+  enum class ExternKind
+  {
+    Static,
+    Function,
+  };
+
+  virtual ~ExternalItem () {}
+
+  BaseKind get_hir_kind () override final { return EXTERNAL; }
+
+  virtual ExternKind get_extern_kind () = 0;
+
+  // Returns whether item has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether item has non-default visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ExternalItem> clone_external_item () const
+  {
+    return std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+  }
+
+  virtual std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRExternalItemVisitor &vis) = 0;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Identifier get_item_name () const { return item_name; }
+
+  AST::AttrVec &get_outer_attrs () { return outer_attrs; }
+
+protected:
+  ExternalItem (Analysis::NodeMapping mappings, Identifier item_name,
+		Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : mappings (mappings), outer_attrs (std::move (outer_attrs)),
+      visibility (std::move (vis)), item_name (std::move (item_name)),
+      locus (locus)
+  {}
+
+  // Copy constructor
+  ExternalItem (ExternalItem const &other)
+    : mappings (other.mappings), outer_attrs (other.outer_attrs),
+      visibility (other.visibility), item_name (other.item_name),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  ExternalItem &operator= (ExternalItem const &other)
+  {
+    mappings = other.mappings;
+    item_name = other.item_name;
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalItem (ExternalItem &&other) = default;
+  ExternalItem &operator= (ExternalItem &&other) = default;
+
+  // Clone function implementation as pure virtual method
+  virtual ExternalItem *clone_external_item_impl () const = 0;
+};
+
+// A static item used in an extern block
+class ExternalStaticItem : public ExternalItem
+{
+  Mutability mut;
+  std::unique_ptr<Type> item_type;
+
+public:
+  ExternalStaticItem (Analysis::NodeMapping mappings, Identifier item_name,
+		      std::unique_ptr<Type> item_type, Mutability mut,
+		      Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : ExternalItem (std::move (mappings), std::move (item_name),
+		    std::move (vis), std::move (outer_attrs), locus),
+      mut (mut), item_type (std::move (item_type))
+  {}
+
+  // Copy constructor
+  ExternalStaticItem (ExternalStaticItem const &other)
+    : ExternalItem (other), mut (other.mut),
+      item_type (other.item_type->clone_type ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExternalStaticItem &operator= (ExternalStaticItem const &other)
+  {
+    ExternalItem::operator= (other);
+    item_type = other.item_type->clone_type ();
+    mut = other.mut;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalStaticItem (ExternalStaticItem &&other) = default;
+  ExternalStaticItem &operator= (ExternalStaticItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExternalItemVisitor &vis) override;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  Mutability get_mut () { return mut; }
+
+  std::unique_ptr<Type> &get_item_type () { return item_type; }
+
+  ExternKind get_extern_kind () override { return ExternKind::Static; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalStaticItem *clone_external_item_impl () const override
+  {
+    return new ExternalStaticItem (*this);
+  }
+};
+
+// A named function parameter used in external functions
+struct NamedFunctionParam
+{
+private:
+  Identifier name;
+  std::unique_ptr<Type> param_type;
+  Analysis::NodeMapping mappings;
+
+public:
+  bool has_name () const { return name != "_"; }
+
+  NamedFunctionParam (Analysis::NodeMapping mappings, Identifier name,
+		      std::unique_ptr<Type> param_type)
+    : name (std::move (name)), param_type (std::move (param_type)),
+      mappings (std::move (mappings))
+  {}
+
+  // Copy constructor
+  NamedFunctionParam (NamedFunctionParam const &other)
+    : name (other.name), param_type (other.param_type->clone_type ()),
+      mappings (other.mappings)
+  {}
+
+  ~NamedFunctionParam () = default;
+
+  // Overloaded assignment operator to clone
+  NamedFunctionParam &operator= (NamedFunctionParam const &other)
+  {
+    mappings = other.mappings;
+    name = other.name;
+    param_type = other.param_type->clone_type ();
+    // has_name = other.has_name;
+
+    return *this;
+  }
+
+  // move constructors
+  NamedFunctionParam (NamedFunctionParam &&other) = default;
+  NamedFunctionParam &operator= (NamedFunctionParam &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_param_name () const { return name; }
+
+  std::unique_ptr<Type> &get_type () { return param_type; }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+};
+
+// A function item used in an extern block
+class ExternalFunctionItem : public ExternalItem
+{
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_return_type;
+  // FunctionReturnType return_type;
+  std::unique_ptr<Type> return_type; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<NamedFunctionParam> function_params;
+  bool has_variadics;
+
+public:
+  // Returns whether item has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether item has a return type (otherwise void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether item has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ExternalFunctionItem (
+    Analysis::NodeMapping mappings, Identifier item_name,
+    std::vector<std::unique_ptr<GenericParam>> generic_params,
+    std::unique_ptr<Type> return_type, WhereClause where_clause,
+    std::vector<NamedFunctionParam> function_params, bool has_variadics,
+    Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : ExternalItem (std::move (mappings), std::move (item_name),
+		    std::move (vis), std::move (outer_attrs), locus),
+      generic_params (std::move (generic_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_params (std::move (function_params)),
+      has_variadics (has_variadics)
+  {}
+
+  // Copy constructor with clone
+  ExternalFunctionItem (ExternalFunctionItem const &other)
+    : ExternalItem (other), return_type (other.return_type->clone_type ()),
+      where_clause (other.where_clause),
+      function_params (other.function_params),
+      has_variadics (other.has_variadics)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with clone
+  ExternalFunctionItem &operator= (ExternalFunctionItem const &other)
+  {
+    ExternalItem::operator= (other);
+    return_type = other.return_type->clone_type ();
+    where_clause = other.where_clause;
+    function_params = other.function_params;
+    has_variadics = other.has_variadics;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalFunctionItem (ExternalFunctionItem &&other) = default;
+  ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExternalItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  std::unique_ptr<Type> &get_return_type () { return return_type; }
+
+  std::vector<NamedFunctionParam> &get_function_params ()
+  {
+    return function_params;
+  }
+
+  bool is_variadic () const { return has_variadics; }
+
+  ExternKind get_extern_kind () override { return ExternKind::Function; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalFunctionItem *clone_external_item_impl () const override
+  {
+    return new ExternalFunctionItem (*this);
+  }
+};
+
+// An extern block HIR node
+class ExternBlock : public VisItem
+{
+  ABI abi;
+  AST::AttrVec inner_attrs;
+  std::vector<std::unique_ptr<ExternalItem>> extern_items;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern block has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Returns whether extern block has extern items.
+  bool has_extern_items () const { return !extern_items.empty (); }
+
+  ABI get_abi () const { return abi; }
+
+  ExternBlock (Analysis::NodeMapping mappings, ABI abi,
+	       std::vector<std::unique_ptr<ExternalItem>> extern_items,
+	       Visibility vis, AST::AttrVec inner_attrs,
+	       AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      abi (abi), inner_attrs (std::move (inner_attrs)),
+      extern_items (std::move (extern_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  ExternBlock (ExternBlock const &other)
+    : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  ExternBlock &operator= (ExternBlock const &other)
+  {
+    VisItem::operator= (other);
+    abi = other.abi;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternBlock (ExternBlock &&other) = default;
+  ExternBlock &operator= (ExternBlock &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<ExternalItem>> &get_extern_items ()
+  {
+    return extern_items;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::ExternBlock; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternBlock *clone_item_impl () const override
+  {
+    return new ExternBlock (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual ExternBlock* clone_statement_impl() const override {
+      return new ExternBlock(*this);
+  }*/
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-path.h b/gcc/rust/hir/tree/rust-hir-path.h
new file mode 100644
index 00000000000..03cf5f5d2e8
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-path.h
@@ -0,0 +1,1013 @@
+// 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/>.
+
+#ifndef RUST_HIR_PATH_H
+#define RUST_HIR_PATH_H
+
+#include "rust-hir.h"
+
+namespace Rust {
+namespace HIR {
+
+// The "identifier" (not generic args) aspect of each path expression segment
+class PathIdentSegment
+{
+  std::string segment_name;
+
+  // TODO: should this have location info stored?
+
+  // only allow identifiers, "super", "self", "Self", "crate", or "$crate"
+public:
+  PathIdentSegment (std::string segment_name)
+    : segment_name (std::move (segment_name))
+  {}
+
+  /* TODO: insert check in constructor for this? Or is this a semantic error
+   * best handled then? */
+
+  /* TODO: does this require visitor? pretty sure this isn't polymorphic, but
+   * not entirely sure */
+
+  // Creates an error PathIdentSegment.
+  static PathIdentSegment create_error () { return PathIdentSegment (""); }
+
+  // Returns whether PathIdentSegment is in an error state.
+  bool is_error () const { return segment_name.empty (); }
+
+  std::string as_string () const { return segment_name; }
+};
+
+// A binding of an identifier to a type used in generic arguments in paths
+struct GenericArgsBinding
+{
+private:
+  Identifier identifier;
+  std::unique_ptr<Type> type;
+
+  Location locus;
+
+public:
+  // Returns whether binding is in an error state.
+  bool is_error () const
+  {
+    return type == nullptr;
+    // and also identifier is empty, but cheaper computation
+  }
+
+  // Creates an error state generic args binding.
+  static GenericArgsBinding create_error ()
+  {
+    return GenericArgsBinding ("", nullptr);
+  }
+
+  // Pointer type for type in constructor to enable polymorphism
+  GenericArgsBinding (Identifier ident, std::unique_ptr<Type> type_ptr,
+		      Location locus = Location ())
+    : identifier (std::move (ident)), type (std::move (type_ptr)), locus (locus)
+  {}
+
+  // Copy constructor has to deep copy the type as it is a unique pointer
+  GenericArgsBinding (GenericArgsBinding const &other)
+    : identifier (other.identifier), type (other.type->clone_type ()),
+      locus (other.locus)
+  {}
+
+  // default destructor
+  ~GenericArgsBinding () = default;
+
+  // Overload assignment operator to deep copy the pointed-to type
+  GenericArgsBinding &operator= (GenericArgsBinding const &other)
+  {
+    identifier = other.identifier;
+    type = other.type->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // move constructors
+  GenericArgsBinding (GenericArgsBinding &&other) = default;
+  GenericArgsBinding &operator= (GenericArgsBinding &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_identifier () const { return identifier; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  Location get_locus () const { return locus; }
+};
+
+class ConstGenericArg
+{
+  // FIXME: Do we need to disambiguate or no? We should be able to disambiguate
+  // at name-resolution, hence no need for ambiguities here
+
+public:
+  ConstGenericArg (std::unique_ptr<Expr> expression, Location locus)
+    : expression (std::move (expression)), locus (locus)
+  {}
+
+  ConstGenericArg (const ConstGenericArg &other) : locus (other.locus)
+  {
+    expression = other.expression->clone_expr ();
+  }
+
+  ConstGenericArg operator= (const ConstGenericArg &other)
+  {
+    expression = other.expression->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+private:
+  std::unique_ptr<Expr> expression;
+  Location locus;
+};
+
+// Generic arguments allowed in each path expression segment - inline?
+struct GenericArgs
+{
+  std::vector<Lifetime> lifetime_args;
+  std::vector<std::unique_ptr<Type> > type_args;
+  std::vector<GenericArgsBinding> binding_args;
+  std::vector<ConstGenericArg> const_args;
+  Location locus;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const
+  {
+    return !(lifetime_args.empty () && type_args.empty ()
+	     && binding_args.empty ());
+  }
+
+  GenericArgs (std::vector<Lifetime> lifetime_args,
+	       std::vector<std::unique_ptr<Type> > type_args,
+	       std::vector<GenericArgsBinding> binding_args,
+	       std::vector<ConstGenericArg> const_args, Location locus)
+    : lifetime_args (std::move (lifetime_args)),
+      type_args (std::move (type_args)),
+      binding_args (std::move (binding_args)),
+      const_args (std::move (const_args)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  GenericArgs (GenericArgs const &other)
+    : lifetime_args (other.lifetime_args), binding_args (other.binding_args),
+      const_args (other.const_args), locus (other.locus)
+  {
+    type_args.reserve (other.type_args.size ());
+
+    for (const auto &e : other.type_args)
+      type_args.push_back (e->clone_type ());
+  }
+
+  ~GenericArgs () = default;
+
+  // overloaded assignment operator to vector clone
+  GenericArgs &operator= (GenericArgs const &other)
+  {
+    lifetime_args = other.lifetime_args;
+    binding_args = other.binding_args;
+    const_args = other.const_args;
+    locus = other.locus;
+
+    type_args.reserve (other.type_args.size ());
+    for (const auto &e : other.type_args)
+      type_args.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  GenericArgs (GenericArgs &&other) = default;
+  GenericArgs &operator= (GenericArgs &&other) = default;
+
+  // Creates an empty GenericArgs (no arguments)
+  static GenericArgs create_empty (Location locus = Location ())
+  {
+    return GenericArgs ({}, {}, {}, {}, locus);
+  }
+
+  bool is_empty () const
+  {
+    return lifetime_args.size () == 0 && type_args.size () == 0
+	   && binding_args.size () == 0;
+  }
+
+  std::string as_string () const;
+
+  std::vector<Lifetime> &get_lifetime_args () { return lifetime_args; }
+
+  std::vector<std::unique_ptr<Type> > &get_type_args () { return type_args; }
+
+  std::vector<GenericArgsBinding> &get_binding_args () { return binding_args; }
+
+  std::vector<ConstGenericArg> &get_const_args () { return const_args; }
+
+  Location get_locus () const { return locus; }
+};
+
+/* A segment of a path in expression, including an identifier aspect and maybe
+ * generic args */
+class PathExprSegment
+{
+private:
+  Analysis::NodeMapping mappings;
+  PathIdentSegment segment_name;
+  GenericArgs generic_args;
+  Location locus;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  // Constructor for segment (from IdentSegment and GenericArgs)
+  PathExprSegment (Analysis::NodeMapping mappings,
+		   PathIdentSegment segment_name, Location locus = Location (),
+		   GenericArgs generic_args = GenericArgs::create_empty ())
+    : mappings (std::move (mappings)), segment_name (std::move (segment_name)),
+      generic_args (std::move (generic_args)), locus (locus)
+  {}
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  PathIdentSegment get_segment () const { return segment_name; }
+
+  GenericArgs &get_generic_args () { return generic_args; }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// HIR node representing a pattern that involves a "path" - abstract base class
+class PathPattern : public Pattern
+{
+  std::vector<PathExprSegment> segments;
+
+protected:
+  PathPattern (std::vector<PathExprSegment> segments)
+    : segments (std::move (segments))
+  {}
+
+  // Returns whether path has segments.
+  bool has_segments () const { return !segments.empty (); }
+
+  /* Converts path segments to their equivalent SimplePath segments if possible,
+   * and creates a SimplePath from them. */
+  AST::SimplePath
+  convert_to_simple_path (bool with_opening_scope_resolution) const;
+
+public:
+  /* Returns whether the path is a single segment (excluding qualified path
+   * initial as segment). */
+  bool is_single_segment () const { return segments.size () == 1; }
+
+  std::string as_string () const override;
+
+  void iterate_path_segments (std::function<bool (PathExprSegment &)> cb)
+  {
+    for (auto it = segments.begin (); it != segments.end (); it++)
+      {
+	if (!cb (*it))
+	  return;
+      }
+  }
+
+  size_t get_num_segments () const { return segments.size (); }
+
+  std::vector<PathExprSegment> &get_segments () { return segments; }
+
+  const std::vector<PathExprSegment> &get_segments () const { return segments; }
+
+  PathExprSegment &get_root_seg () { return segments.at (0); }
+
+  PathExprSegment get_final_segment () const { return segments.back (); }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::PATH;
+  }
+};
+
+/* HIR node representing a path-in-expression pattern (path that allows generic
+ * arguments) */
+class PathInExpression : public PathPattern, public PathExpr
+{
+  bool has_opening_scope_resolution;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  PathInExpression (Analysis::NodeMapping mappings,
+		    std::vector<PathExprSegment> path_segments,
+		    Location locus = Location (),
+		    bool has_opening_scope_resolution = false,
+		    std::vector<AST::Attribute> outer_attrs
+		    = std::vector<AST::Attribute> ())
+    : PathPattern (std::move (path_segments)),
+      PathExpr (std::move (mappings), std::move (outer_attrs)),
+      has_opening_scope_resolution (has_opening_scope_resolution), locus (locus)
+  {}
+
+  // Creates an error state path in expression.
+  static PathInExpression create_error ()
+  {
+    return PathInExpression (Analysis::NodeMapping::get_error (),
+			     std::vector<PathExprSegment> ());
+  }
+
+  // Returns whether path in expression is in an error state.
+  bool is_error () const { return !has_segments (); }
+
+  /* Converts PathInExpression to SimplePath if possible (i.e. no generic
+   * arguments). Otherwise returns an empty SimplePath. */
+  AST::SimplePath as_simple_path () const
+  {
+    /* delegate to parent class as can't access segments. however,
+     * QualifiedPathInExpression conversion to simple path wouldn't make sense,
+     * so the method in the parent class should be protected, not public. Have
+     * to pass in opening scope resolution as parent class has no access to it.
+     */
+    return convert_to_simple_path (has_opening_scope_resolution);
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  bool opening_scope_resolution () { return has_opening_scope_resolution; }
+
+  bool is_self () const
+  {
+    if (!is_single_segment ())
+      return false;
+
+    return get_final_segment ().get_segment ().as_string ().compare ("self")
+	   == 0;
+  }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return get_mappings ();
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  PathInExpression *clone_pattern_impl () const override
+  {
+    return new PathInExpression (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  PathInExpression *clone_expr_without_block_impl () const override
+  {
+    return new PathInExpression (*this);
+  }
+};
+
+/* Base class for segments used in type paths - not abstract (represents an
+ * ident-only segment) */
+class TypePathSegment
+{
+public:
+  enum SegmentType
+  {
+    REG,
+    GENERIC,
+    FUNCTION
+  };
+
+private:
+  Analysis::NodeMapping mappings;
+  PathIdentSegment ident_segment;
+  Location locus;
+
+protected:
+  bool has_separating_scope_resolution;
+  SegmentType type;
+
+  // Clone function implementation - not pure virtual as overrided by subclasses
+  virtual TypePathSegment *clone_type_path_segment_impl () const
+  {
+    return new TypePathSegment (*this);
+  }
+
+public:
+  virtual ~TypePathSegment () {}
+
+  virtual SegmentType get_type () const { return SegmentType::REG; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypePathSegment> clone_type_path_segment () const
+  {
+    return std::unique_ptr<TypePathSegment> (clone_type_path_segment_impl ());
+  }
+
+  TypePathSegment (Analysis::NodeMapping mappings,
+		   PathIdentSegment ident_segment,
+		   bool has_separating_scope_resolution, Location locus)
+    : mappings (std::move (mappings)),
+      ident_segment (std::move (ident_segment)), locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      type (SegmentType::REG)
+  {}
+
+  TypePathSegment (Analysis::NodeMapping mappings, std::string segment_name,
+		   bool has_separating_scope_resolution, Location locus)
+    : mappings (std::move (mappings)),
+      ident_segment (PathIdentSegment (std::move (segment_name))),
+      locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      type (SegmentType::REG)
+  {}
+
+  virtual std::string as_string () const { return ident_segment.as_string (); }
+
+  /* Returns whether the type path segment is in an error state. May be virtual
+   * in future. */
+  bool is_error () const { return ident_segment.is_error (); }
+
+  /* Returns whether segment is identifier only (as opposed to generic args or
+   * function). Overriden in derived classes with other segments. */
+  virtual bool is_ident_only () const { return true; }
+
+  Location get_locus () const { return locus; }
+
+  // not pure virtual as class not abstract
+  virtual void accept_vis (HIRFullVisitor &vis);
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  const PathIdentSegment &get_ident_segment () const { return ident_segment; }
+
+  bool is_generic_segment () const
+  {
+    return get_type () == SegmentType::GENERIC;
+  }
+};
+
+// Segment used in type path with generic args
+class TypePathSegmentGeneric : public TypePathSegment
+{
+  GenericArgs generic_args;
+
+public:
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  bool is_ident_only () const override { return false; }
+
+  // Constructor with PathIdentSegment and GenericArgs
+  TypePathSegmentGeneric (Analysis::NodeMapping mappings,
+			  PathIdentSegment ident_segment,
+			  bool has_separating_scope_resolution,
+			  GenericArgs generic_args, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      generic_args (std::move (generic_args))
+  {}
+
+  // Constructor from segment name and all args
+  TypePathSegmentGeneric (Analysis::NodeMapping mappings,
+			  std::string segment_name,
+			  bool has_separating_scope_resolution,
+			  std::vector<Lifetime> lifetime_args,
+			  std::vector<std::unique_ptr<Type> > type_args,
+			  std::vector<GenericArgsBinding> binding_args,
+			  std::vector<ConstGenericArg> const_args,
+			  Location locus)
+    : TypePathSegment (std::move (mappings), std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      generic_args (
+	GenericArgs (std::move (lifetime_args), std::move (type_args),
+		     std::move (binding_args), std::move (const_args), locus))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  GenericArgs &get_generic_args () { return generic_args; }
+
+  virtual SegmentType get_type () const override final
+  {
+    return SegmentType::GENERIC;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentGeneric *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentGeneric (*this);
+  }
+};
+
+// A function as represented in a type path
+struct TypePathFunction
+{
+private:
+  // TODO: remove
+  /*bool has_inputs;
+  TypePathFnInputs inputs;*/
+  // inlined from TypePathFnInputs
+  std::vector<std::unique_ptr<Type> > inputs;
+
+  // bool has_type;
+  std::unique_ptr<Type> return_type;
+
+  // FIXME: think of better way to mark as invalid than taking up storage
+  bool is_invalid;
+
+  // TODO: should this have location info?
+
+protected:
+  // Constructor only used to create invalid type path functions.
+  TypePathFunction (bool is_invalid) : is_invalid (is_invalid) {}
+
+public:
+  // Returns whether the return type of the function has been specified.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether the function has inputs.
+  bool has_inputs () const { return !inputs.empty (); }
+
+  // Returns whether function is in an error state.
+  bool is_error () const { return is_invalid; }
+
+  // Creates an error state function.
+  static TypePathFunction create_error () { return TypePathFunction (true); }
+
+  // Constructor
+  TypePathFunction (std::vector<std::unique_ptr<Type> > inputs,
+		    Type *type = nullptr)
+    : inputs (std::move (inputs)), return_type (type), is_invalid (false)
+  {}
+  // FIXME: deprecated
+
+  // Constructor
+  TypePathFunction (std::vector<std::unique_ptr<Type> > inputs,
+		    std::unique_ptr<Type> type = nullptr)
+    : inputs (std::move (inputs)), return_type (std::move (type)),
+      is_invalid (false)
+  {}
+
+  // Copy constructor with clone
+  TypePathFunction (TypePathFunction const &other)
+    : return_type (other.return_type->clone_type ()),
+      is_invalid (other.is_invalid)
+  {
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+  }
+
+  ~TypePathFunction () = default;
+
+  // Overloaded assignment operator to clone type
+  TypePathFunction &operator= (TypePathFunction const &other)
+  {
+    return_type = other.return_type->clone_type ();
+    is_invalid = other.is_invalid;
+
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePathFunction (TypePathFunction &&other) = default;
+  TypePathFunction &operator= (TypePathFunction &&other) = default;
+
+  std::string as_string () const;
+};
+
+// Segment used in type path with a function argument
+class TypePathSegmentFunction : public TypePathSegment
+{
+  TypePathFunction function_path;
+
+public:
+  // Constructor with PathIdentSegment and TypePathFn
+  TypePathSegmentFunction (Analysis::NodeMapping mappings,
+			   PathIdentSegment ident_segment,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  // Constructor with segment name and TypePathFn
+  TypePathSegmentFunction (Analysis::NodeMapping mappings,
+			   std::string segment_name,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  std::string as_string () const override;
+
+  bool is_ident_only () const override { return false; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  virtual SegmentType get_type () const override final
+  {
+    return SegmentType::FUNCTION;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentFunction *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentFunction (*this);
+  }
+};
+
+// Path used inside types
+class TypePath : public TypeNoBounds
+{
+public:
+  bool has_opening_scope_resolution;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypePath *clone_type_impl () const override { return new TypePath (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypePath *clone_type_no_bounds_impl () const override
+  {
+    return new TypePath (*this);
+  }
+
+public:
+  /* Returns whether the TypePath has an opening scope resolution operator (i.e.
+   * is global path or crate-relative path, not module-relative) */
+  bool has_opening_scope_resolution_op () const
+  {
+    return has_opening_scope_resolution;
+  }
+
+  // Returns whether the TypePath is in an invalid state.
+  bool is_error () const { return segments.empty (); }
+
+  // Creates an error state TypePath.
+  static TypePath create_error ()
+  {
+    return TypePath (Analysis::NodeMapping::get_error (),
+		     std::vector<std::unique_ptr<TypePathSegment> > (),
+		     Location ());
+  }
+
+  // Constructor
+  TypePath (Analysis::NodeMapping mappings,
+	    std::vector<std::unique_ptr<TypePathSegment> > segments,
+	    Location locus, bool has_opening_scope_resolution = false)
+    : TypeNoBounds (mappings, locus),
+      has_opening_scope_resolution (has_opening_scope_resolution),
+      segments (std::move (segments))
+  {}
+
+  // Copy constructor with vector clone
+  TypePath (TypePath const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      has_opening_scope_resolution (other.has_opening_scope_resolution)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+  }
+
+  // Overloaded assignment operator with clone
+  TypePath &operator= (TypePath const &other)
+  {
+    has_opening_scope_resolution = other.has_opening_scope_resolution;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePath (TypePath &&other) = default;
+  TypePath &operator= (TypePath &&other) = default;
+
+  std::string as_string () const override;
+
+  /* Converts TypePath to SimplePath if possible (i.e. no generic or function
+   * arguments). Otherwise returns an empty SimplePath. */
+  AST::SimplePath as_simple_path () const;
+
+  // Creates a trait bound with a clone of this type path as its only element.
+  TraitBound *to_trait_bound (bool in_parens) const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  size_t get_num_segments () const { return segments.size (); }
+
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+
+  std::unique_ptr<TypePathSegment> &get_final_segment ()
+  {
+    return segments.back ();
+  }
+};
+
+struct QualifiedPathType
+{
+private:
+  std::unique_ptr<Type> type;
+  std::unique_ptr<TypePath> trait;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  QualifiedPathType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+		     std::unique_ptr<TypePath> trait, Location locus)
+    : type (std::move (type)), trait (std::move (trait)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor uses custom deep copy for Type to preserve polymorphism
+  QualifiedPathType (QualifiedPathType const &other)
+    : type (other.type->clone_type ()),
+      trait (other.has_as_clause () ? std::unique_ptr<HIR::TypePath> (
+	       new HIR::TypePath (*other.trait))
+				    : nullptr),
+      locus (other.locus), mappings (other.mappings)
+  {}
+
+  // default destructor
+  ~QualifiedPathType () = default;
+
+  // overload assignment operator to use custom clone method
+  QualifiedPathType &operator= (QualifiedPathType const &other)
+  {
+    type = other.type->clone_type ();
+    locus = other.locus;
+    mappings = other.mappings;
+    trait
+      = other.has_as_clause ()
+	  ? std::unique_ptr<HIR::TypePath> (new HIR::TypePath (*other.trait))
+	  : nullptr;
+
+    return *this;
+  }
+
+  // move constructor
+  QualifiedPathType (QualifiedPathType &&other) = default;
+  QualifiedPathType &operator= (QualifiedPathType &&other) = default;
+
+  // Returns whether the qualified path type has a rebind as clause.
+  bool has_as_clause () const { return trait != nullptr; }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  std::unique_ptr<TypePath> &get_trait ()
+  {
+    rust_assert (has_as_clause ());
+    return trait;
+  }
+
+  bool trait_has_generic_args () const
+  {
+    rust_assert (has_as_clause ());
+    bool is_generic_seg = trait->get_final_segment ()->get_type ()
+			  == TypePathSegment::SegmentType::GENERIC;
+    if (!is_generic_seg)
+      return false;
+
+    TypePathSegmentGeneric *seg = static_cast<TypePathSegmentGeneric *> (
+      trait->get_final_segment ().get ());
+    return seg->has_generic_args ();
+  }
+
+  GenericArgs &get_trait_generic_args ()
+  {
+    rust_assert (trait_has_generic_args ());
+    TypePathSegmentGeneric *seg = static_cast<TypePathSegmentGeneric *> (
+      trait->get_final_segment ().get ());
+    return seg->get_generic_args ();
+  }
+};
+
+/* HIR node representing a qualified path-in-expression pattern (path that
+ * allows specifying trait functions) */
+class QualifiedPathInExpression : public PathPattern, public PathExpr
+{
+  QualifiedPathType path_type;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  QualifiedPathInExpression (Analysis::NodeMapping mappings,
+			     QualifiedPathType qual_path_type,
+			     std::vector<PathExprSegment> path_segments,
+			     Location locus = Location (),
+			     std::vector<AST::Attribute> outer_attrs
+			     = std::vector<AST::Attribute> ())
+    : PathPattern (std::move (path_segments)),
+      PathExpr (std::move (mappings), std::move (outer_attrs)),
+      path_type (std::move (qual_path_type)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  QualifiedPathType &get_path_type () { return path_type; }
+
+  Location get_locus () { return locus; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return get_mappings ();
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInExpression *clone_pattern_impl () const override
+  {
+    return new QualifiedPathInExpression (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInExpression *clone_expr_without_block_impl () const override
+  {
+    return new QualifiedPathInExpression (*this);
+  }
+};
+
+/* Represents a qualified path in a type; used for disambiguating trait function
+ * calls */
+class QualifiedPathInType : public TypeNoBounds
+{
+  QualifiedPathType path_type;
+  std::unique_ptr<TypePathSegment> associated_segment;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInType *clone_type_impl () const override
+  {
+    return new QualifiedPathInType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInType *clone_type_no_bounds_impl () const override
+  {
+    return new QualifiedPathInType (*this);
+  }
+
+public:
+  QualifiedPathInType (
+    Analysis::NodeMapping mappings, QualifiedPathType qual_path_type,
+    std::unique_ptr<TypePathSegment> associated_segment,
+    std::vector<std::unique_ptr<TypePathSegment> > path_segments,
+    Location locus = Location ())
+    : TypeNoBounds (mappings, locus), path_type (std::move (qual_path_type)),
+      associated_segment (std::move (associated_segment)),
+      segments (std::move (path_segments))
+  {}
+
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType elements
+   * as params */
+
+  // Copy constructor with vector clone
+  QualifiedPathInType (QualifiedPathInType const &other)
+    : TypeNoBounds (other.mappings, other.locus), path_type (other.path_type)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    // Untested.
+    gcc_unreachable ();
+  }
+
+  // Overloaded assignment operator with vector clone
+  QualifiedPathInType &operator= (QualifiedPathInType const &other)
+  {
+    path_type = other.path_type;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  QualifiedPathInType (QualifiedPathInType &&other) = default;
+  QualifiedPathInType &operator= (QualifiedPathInType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  QualifiedPathType &get_path_type () { return path_type; }
+
+  std::unique_ptr<TypePathSegment> &get_associated_segment ()
+  {
+    return associated_segment;
+  }
+
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+};
+
+class SimplePathSegment
+{
+  Analysis::NodeMapping mappings;
+
+public:
+  SimplePathSegment (Analysis::NodeMapping mappings) : mappings (mappings) {}
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+class SimplePath
+{
+  std::vector<SimplePathSegment> segments;
+  Analysis::NodeMapping mappings;
+  Location locus;
+
+public:
+  SimplePath (std::vector<SimplePathSegment> segments,
+	      Analysis::NodeMapping mappings, Location locus)
+    : segments (std::move (segments)), mappings (mappings), locus (locus)
+  {}
+
+  static HIR::SimplePath create_empty ()
+  {
+    return HIR::SimplePath ({}, Analysis::NodeMapping::get_error (),
+			    Location ());
+  }
+
+  bool is_error () const { return segments.empty (); }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+  const Location &get_locus () const { return locus; }
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-pattern.h b/gcc/rust/hir/tree/rust-hir-pattern.h
new file mode 100644
index 00000000000..7129b5a3684
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-pattern.h
@@ -0,0 +1,1356 @@
+// 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/>.
+
+#ifndef RUST_HIR_PATTERN_H
+#define RUST_HIR_PATTERN_H
+
+#include "rust-common.h"
+#include "rust-hir.h"
+
+namespace Rust {
+namespace HIR {
+
+// Literal pattern HIR node (comparing to a literal)
+class LiteralPattern : public Pattern
+{
+  Literal lit;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for a literal pattern
+  LiteralPattern (Analysis::NodeMapping mappings, Literal lit, Location locus)
+    : lit (std::move (lit)), locus (locus), mappings (mappings)
+  {}
+
+  LiteralPattern (Analysis::NodeMapping mappings, std::string val,
+		  Literal::LitType type, Location locus)
+    : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
+      locus (locus), mappings (mappings)
+  {}
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::LITERAL;
+  }
+
+  Literal &get_literal () { return lit; }
+  const Literal &get_literal () const { return lit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  virtual LiteralPattern *clone_pattern_impl () const override
+  {
+    return new LiteralPattern (*this);
+  }
+};
+
+// Identifier pattern HIR node (bind value matched to a variable)
+class IdentifierPattern : public Pattern
+{
+  Identifier variable_ident;
+  bool is_ref;
+  Mutability mut;
+  std::unique_ptr<Pattern> to_bind;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the IdentifierPattern has a pattern to bind.
+  bool has_pattern_to_bind () const { return to_bind != nullptr; }
+
+  // Constructor
+  IdentifierPattern (Analysis::NodeMapping mappings, Identifier ident,
+		     Location locus, bool is_ref = false,
+		     Mutability mut = Mutability::Imm,
+		     std::unique_ptr<Pattern> to_bind = nullptr)
+    : variable_ident (std::move (ident)), is_ref (is_ref), mut (mut),
+      to_bind (std::move (to_bind)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor with clone
+  IdentifierPattern (IdentifierPattern const &other)
+    : variable_ident (other.variable_ident), is_ref (other.is_ref),
+      mut (other.mut), locus (other.locus), mappings (other.mappings)
+  {
+    // fix to get prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+  }
+
+  // Overload assignment operator to use clone
+  IdentifierPattern &operator= (IdentifierPattern const &other)
+  {
+    variable_ident = other.variable_ident;
+    is_ref = other.is_ref;
+    mut = other.mut;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    // fix to get prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+
+    return *this;
+  }
+
+  // default move semantics
+  IdentifierPattern (IdentifierPattern &&other) = default;
+  IdentifierPattern &operator= (IdentifierPattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  Identifier get_identifier () const { return variable_ident; }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::IDENTIFIER;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IdentifierPattern *clone_pattern_impl () const override
+  {
+    return new IdentifierPattern (*this);
+  }
+};
+
+// HIR node for using the '_' wildcard "match any value" pattern
+class WildcardPattern : public Pattern
+{
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override { return std::string (1, '_'); }
+
+  WildcardPattern (Analysis::NodeMapping mappings, Location locus)
+    : locus (locus), mappings (mappings)
+  {}
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::WILDCARD;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WildcardPattern *clone_pattern_impl () const override
+  {
+    return new WildcardPattern (*this);
+  }
+};
+
+// Base range pattern bound (lower or upper limit) - abstract
+class RangePatternBound
+{
+public:
+  enum RangePatternBoundType
+  {
+    LITERAL,
+    PATH,
+    QUALPATH
+  };
+
+  virtual ~RangePatternBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<RangePatternBound> clone_range_pattern_bound () const
+  {
+    return std::unique_ptr<RangePatternBound> (
+      clone_range_pattern_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual RangePatternBoundType get_bound_type () const = 0;
+
+protected:
+  // pure virtual as RangePatternBound is abstract
+  virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0;
+};
+
+// Literal-based pattern bound
+class RangePatternBoundLiteral : public RangePatternBound
+{
+  Literal literal;
+  /* Can only be a char, byte, int, or float literal - same impl here as
+   * previously */
+
+  // Minus prefixed to literal (if integer or floating-point)
+  bool has_minus;
+
+  Location locus;
+
+public:
+  // Constructor
+  RangePatternBoundLiteral (Literal literal, Location locus,
+			    bool has_minus = false)
+    : literal (literal), has_minus (has_minus), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  Literal get_literal () const { return literal; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::LITERAL;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundLiteral (*this);
+  }
+};
+
+// Path-based pattern bound
+class RangePatternBoundPath : public RangePatternBound
+{
+  PathInExpression path;
+
+  /* TODO: should this be refactored so that PathInExpression is a subclass of
+   * RangePatternBound? */
+
+public:
+  RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  PathInExpression &get_path () { return path; }
+  const PathInExpression &get_path () const { return path; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::PATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundPath (*this);
+  }
+};
+
+// Qualified path-based pattern bound
+class RangePatternBoundQualPath : public RangePatternBound
+{
+  QualifiedPathInExpression path;
+
+  /* TODO: should this be refactored so that QualifiedPathInExpression is a
+   * subclass of RangePatternBound? */
+
+public:
+  RangePatternBoundQualPath (QualifiedPathInExpression path)
+    : path (std::move (path))
+  {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  QualifiedPathInExpression &get_qualified_path () { return path; }
+  const QualifiedPathInExpression &get_qualified_path () const { return path; }
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::QUALPATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundQualPath (*this);
+  }
+};
+
+// HIR node for matching within a certain range (range pattern)
+class RangePattern : public Pattern
+{
+  std::unique_ptr<RangePatternBound> lower;
+  std::unique_ptr<RangePatternBound> upper;
+
+  bool has_ellipsis_syntax;
+
+  /* location only stored to avoid a dereference - lower pattern should give
+   * correct location so maybe change in future */
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  RangePattern (Analysis::NodeMapping mappings,
+		std::unique_ptr<RangePatternBound> lower,
+		std::unique_ptr<RangePatternBound> upper, Location locus,
+		bool has_ellipsis_syntax = false)
+    : lower (std::move (lower)), upper (std::move (upper)),
+      has_ellipsis_syntax (has_ellipsis_syntax), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor with clone
+  RangePattern (RangePattern const &other)
+    : lower (other.lower->clone_range_pattern_bound ()),
+      upper (other.upper->clone_range_pattern_bound ()),
+      has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overloaded assignment operator to clone
+  RangePattern &operator= (RangePattern const &other)
+  {
+    lower = other.lower->clone_range_pattern_bound ();
+    upper = other.upper->clone_range_pattern_bound ();
+    has_ellipsis_syntax = other.has_ellipsis_syntax;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  RangePattern (RangePattern &&other) = default;
+  RangePattern &operator= (RangePattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::RANGE;
+  }
+
+  std::unique_ptr<RangePatternBound> &get_lower_bound ()
+  {
+    rust_assert (lower != nullptr);
+    return lower;
+  }
+
+  std::unique_ptr<RangePatternBound> &get_upper_bound ()
+  {
+    rust_assert (upper != nullptr);
+    return upper;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePattern *clone_pattern_impl () const override
+  {
+    return new RangePattern (*this);
+  }
+};
+
+// HIR node for pattern based on dereferencing the pointers given
+class ReferencePattern : public Pattern
+{
+  bool has_two_amps;
+  Mutability mut;
+  std::unique_ptr<Pattern> pattern;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  ReferencePattern (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Pattern> pattern, Mutability reference_mut,
+		    bool ref_has_two_amps, Location locus)
+    : has_two_amps (ref_has_two_amps), mut (reference_mut),
+      pattern (std::move (pattern)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  ReferencePattern (ReferencePattern const &other)
+    : has_two_amps (other.has_two_amps), mut (other.mut),
+      pattern (other.pattern->clone_pattern ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  ReferencePattern &operator= (ReferencePattern const &other)
+  {
+    pattern = other.pattern->clone_pattern ();
+    mut = other.mut;
+    has_two_amps = other.has_two_amps;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  ReferencePattern (ReferencePattern &&other) = default;
+  ReferencePattern &operator= (ReferencePattern &&other) = default;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::REFERENCE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferencePattern *clone_pattern_impl () const override
+  {
+    return new ReferencePattern (*this);
+  }
+};
+
+// Base class for a single field in a struct pattern - abstract
+class StructPatternField
+{
+  AST::AttrVec outer_attrs;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  enum ItemType
+  {
+    TUPLE_PAT,
+    IDENT_PAT,
+    IDENT
+  };
+
+  virtual ~StructPatternField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructPatternField> clone_struct_pattern_field () const
+  {
+    return std::unique_ptr<StructPatternField> (
+      clone_struct_pattern_field_impl ());
+  }
+
+  virtual std::string as_string () const;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual ItemType get_item_type () const = 0;
+
+  Location get_locus () const { return locus; }
+  Analysis::NodeMapping get_mappings () const { return mappings; };
+
+protected:
+  StructPatternField (Analysis::NodeMapping mappings,
+		      AST::AttrVec outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual StructPatternField *clone_struct_pattern_field_impl () const = 0;
+};
+
+// Tuple pattern single field in a struct pattern
+class StructPatternFieldTuplePat : public StructPatternField
+{
+  TupleIndex index;
+  std::unique_ptr<Pattern> tuple_pattern;
+
+public:
+  StructPatternFieldTuplePat (Analysis::NodeMapping mappings, TupleIndex index,
+			      std::unique_ptr<Pattern> tuple_pattern,
+			      AST::AttrVec outer_attribs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attribs), locus),
+      index (index), tuple_pattern (std::move (tuple_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
+    : StructPatternField (other), index (other.index),
+      tuple_pattern (other.tuple_pattern->clone_pattern ())
+  {}
+
+  // Overload assignment operator to perform clone
+  StructPatternFieldTuplePat &
+  operator= (StructPatternFieldTuplePat const &other)
+  {
+    StructPatternField::operator= (other);
+    tuple_pattern = other.tuple_pattern->clone_pattern ();
+    index = other.index;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default;
+  StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldTuplePat (*this);
+  }
+};
+
+// Identifier pattern single field in a struct pattern
+class StructPatternFieldIdentPat : public StructPatternField
+{
+  Identifier ident;
+  std::unique_ptr<Pattern> ident_pattern;
+
+public:
+  StructPatternFieldIdentPat (Analysis::NodeMapping mappings, Identifier ident,
+			      std::unique_ptr<Pattern> ident_pattern,
+			      AST::AttrVec outer_attrs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attrs), locus),
+      ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
+    : StructPatternField (other), ident (other.ident),
+      ident_pattern (other.ident_pattern->clone_pattern ())
+  {}
+
+  // Overload assignment operator to clone
+  StructPatternFieldIdentPat &
+  operator= (StructPatternFieldIdentPat const &other)
+  {
+    StructPatternField::operator= (other);
+    ident = other.ident;
+    ident_pattern = other.ident_pattern->clone_pattern ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default;
+  StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::IDENT_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdentPat (*this);
+  }
+};
+
+// Identifier only (with no pattern) single field in a struct pattern
+class StructPatternFieldIdent : public StructPatternField
+{
+  bool has_ref;
+  Mutability mut;
+  Identifier ident;
+
+public:
+  StructPatternFieldIdent (Analysis::NodeMapping mappings, Identifier ident,
+			   bool is_ref, Mutability mut,
+			   AST::AttrVec outer_attrs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attrs), locus),
+      has_ref (is_ref), mut (mut), ident (std::move (ident))
+  {}
+
+  std::string as_string () const override;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::IDENT; }
+
+  Identifier get_identifier () const { return ident; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdent *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdent (*this);
+  }
+};
+
+// Elements of a struct pattern
+struct StructPatternElements
+{
+private:
+  std::vector<std::unique_ptr<StructPatternField> > fields;
+
+public:
+  // Returns whether there are any struct pattern fields
+  bool has_struct_pattern_fields () const { return !fields.empty (); }
+
+  /* Returns whether the struct pattern elements is entirely empty (no fields,
+   * no etc). */
+  bool is_empty () const { return !has_struct_pattern_fields (); }
+
+  // Constructor for StructPatternElements with both (potentially)
+  StructPatternElements (
+    std::vector<std::unique_ptr<StructPatternField> > fields)
+    : fields (std::move (fields))
+  {}
+
+  // Copy constructor with vector clone
+  StructPatternElements (StructPatternElements const &other)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  StructPatternElements &operator= (StructPatternElements const &other)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructPatternElements (StructPatternElements &&other) = default;
+  StructPatternElements &operator= (StructPatternElements &&other) = default;
+
+  // Creates an empty StructPatternElements
+  static StructPatternElements create_empty ()
+  {
+    return StructPatternElements (
+      std::vector<std::unique_ptr<StructPatternField> > ());
+  }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<StructPatternField> > &
+  get_struct_pattern_fields ()
+  {
+    return fields;
+  }
+};
+
+// Struct pattern HIR node representation
+class StructPattern : public Pattern
+{
+  PathInExpression path;
+  StructPatternElements elems;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  StructPattern (Analysis::NodeMapping mappings, PathInExpression struct_path,
+		 StructPatternElements elems)
+    : path (std::move (struct_path)), elems (std::move (elems)),
+      mappings (mappings)
+  {}
+
+  bool has_struct_pattern_elems () const { return !elems.is_empty (); }
+
+  Location get_locus () const override { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  PathInExpression &get_path () { return path; }
+  StructPatternElements &get_struct_pattern_elems () { return elems; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::STRUCT;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPattern *clone_pattern_impl () const override
+  {
+    return new StructPattern (*this);
+  }
+};
+
+// Base abstract class for patterns used in TupleStructPattern
+class TupleStructItems
+{
+public:
+  enum ItemType
+  {
+    RANGE,
+    NO_RANGE
+  };
+
+  virtual ~TupleStructItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TupleStructItems> clone_tuple_struct_items () const
+  {
+    return std::unique_ptr<TupleStructItems> (clone_tuple_struct_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual ItemType get_item_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0;
+};
+
+// Class for non-ranged tuple struct pattern patterns
+class TupleStructItemsNoRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TupleStructItemsNoRange (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsNoRange (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default;
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::NO_RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsNoRange (*this);
+  }
+};
+
+// Class for ranged tuple struct pattern patterns
+class TupleStructItemsRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TupleStructItemsRange (std::vector<std::unique_ptr<Pattern> > lower_patterns,
+			 std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsRange (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TupleStructItemsRange &operator= (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsRange (TupleStructItemsRange &&other) = default;
+  TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+  {
+    return lower_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+  {
+    return lower_patterns;
+  }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+  {
+    return upper_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+  {
+    return upper_patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsRange (*this);
+  }
+};
+
+// HIR node representing a tuple struct pattern
+class TupleStructPattern : public Pattern
+{
+  PathInExpression path;
+  std::unique_ptr<TupleStructItems> items;
+  Analysis::NodeMapping mappings;
+
+  /* TOOD: should this store location data? current accessor uses path location
+   * data */
+
+public:
+  std::string as_string () const override;
+
+  TupleStructPattern (Analysis::NodeMapping mappings,
+		      PathInExpression tuple_struct_path,
+		      std::unique_ptr<TupleStructItems> items)
+    : path (std::move (tuple_struct_path)), items (std::move (items)),
+      mappings (mappings)
+  {}
+
+  // Copy constructor required to clone
+  TupleStructPattern (TupleStructPattern const &other)
+    : path (other.path), items (other.items->clone_tuple_struct_items ()),
+      mappings (other.mappings)
+  {}
+
+  // Operator overload assignment operator to clone
+  TupleStructPattern &operator= (TupleStructPattern const &other)
+  {
+    path = other.path;
+    items = other.items->clone_tuple_struct_items ();
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructPattern (TupleStructPattern &&other) = default;
+  TupleStructPattern &operator= (TupleStructPattern &&other) = default;
+
+  Location get_locus () const override { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  PathInExpression &get_path () { return path; }
+
+  std::unique_ptr<TupleStructItems> &get_items () { return items; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::TUPLE_STRUCT;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructPattern *clone_pattern_impl () const override
+  {
+    return new TupleStructPattern (*this);
+  }
+};
+
+// Base abstract class representing TuplePattern patterns
+class TuplePatternItems
+{
+public:
+  enum TuplePatternItemType
+  {
+    MULTIPLE,
+    RANGED,
+  };
+
+  virtual ~TuplePatternItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TuplePatternItems> clone_tuple_pattern_items () const
+  {
+    return std::unique_ptr<TuplePatternItems> (
+      clone_tuple_pattern_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual TuplePatternItemType get_pattern_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0;
+};
+
+// Class representing TuplePattern patterns where there are multiple patterns
+class TuplePatternItemsMultiple : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TuplePatternItemsMultiple (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default;
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::MULTIPLE;
+  }
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsMultiple (*this);
+  }
+};
+
+// Class representing TuplePattern patterns where there are a range of patterns
+class TuplePatternItemsRanged : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TuplePatternItemsRanged (
+    std::vector<std::unique_ptr<Pattern> > lower_patterns,
+    std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsRanged (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default;
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::RANGED;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsRanged (*this);
+  }
+};
+
+// HIR node representing a tuple pattern
+class TuplePattern : public Pattern
+{
+  std::unique_ptr<TuplePatternItems> items;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the tuple pattern has items
+  bool has_tuple_pattern_items () const { return items != nullptr; }
+
+  TuplePattern (Analysis::NodeMapping mappings,
+		std::unique_ptr<TuplePatternItems> items, Location locus)
+    : items (std::move (items)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  TuplePattern (TuplePattern const &other)
+    : items (other.items->clone_tuple_pattern_items ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  TuplePattern &operator= (TuplePattern const &other)
+  {
+    items = other.items->clone_tuple_pattern_items ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::TUPLE;
+  }
+
+  std::unique_ptr<TuplePatternItems> &get_items () { return items; }
+  const std::unique_ptr<TuplePatternItems> &get_items () const { return items; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePattern *clone_pattern_impl () const override
+  {
+    return new TuplePattern (*this);
+  }
+};
+
+// HIR node representing a pattern in parentheses, used to control precedence
+class GroupedPattern : public Pattern
+{
+  std::unique_ptr<Pattern> pattern_in_parens;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override
+  {
+    return "(" + pattern_in_parens->as_string () + ")";
+  }
+
+  GroupedPattern (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Pattern> pattern_in_parens, Location locus)
+    : pattern_in_parens (std::move (pattern_in_parens)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor uses clone
+  GroupedPattern (GroupedPattern const &other)
+    : pattern_in_parens (other.pattern_in_parens->clone_pattern ()),
+      locus (other.locus), mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  GroupedPattern &operator= (GroupedPattern const &other)
+  {
+    pattern_in_parens = other.pattern_in_parens->clone_pattern ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  GroupedPattern (GroupedPattern &&other) = default;
+  GroupedPattern &operator= (GroupedPattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::GROUPED;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedPattern *clone_pattern_impl () const override
+  {
+    return new GroupedPattern (*this);
+  }
+};
+
+// HIR node representing patterns that can match slices and arrays
+class SlicePattern : public Pattern
+{
+  std::vector<std::unique_ptr<Pattern> > items;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  SlicePattern (Analysis::NodeMapping mappings,
+		std::vector<std::unique_ptr<Pattern> > items, Location locus)
+    : items (std::move (items)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor with vector clone
+  SlicePattern (SlicePattern const &other)
+    : locus (other.locus), mappings (other.mappings)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  SlicePattern &operator= (SlicePattern const &other)
+  {
+    locus = other.locus;
+    mappings = other.mappings;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  SlicePattern (SlicePattern &&other) = default;
+  SlicePattern &operator= (SlicePattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::SLICE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SlicePattern *clone_pattern_impl () const override
+  {
+    return new SlicePattern (*this);
+  }
+};
+
+// Moved definition to rust-path.h
+class PathPattern;
+
+// Forward decls for paths (defined in rust-path.h)
+class PathInExpression;
+class QualifiedPathInExpression;
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-stmt.h b/gcc/rust/hir/tree/rust-hir-stmt.h
new file mode 100644
index 00000000000..5247b0aa0f0
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-stmt.h
@@ -0,0 +1,273 @@
+// 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/>.
+
+#ifndef RUST_HIR_STATEMENT_H
+#define RUST_HIR_STATEMENT_H
+
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+#include "rust-hir-expr.h"
+
+namespace Rust {
+namespace HIR {
+// Just a semi-colon, which apparently is a statement.
+class EmptyStmt : public Stmt
+{
+  Location locus;
+
+public:
+  std::string as_string () const override { return std::string (1, ';'); }
+
+  EmptyStmt (Analysis::NodeMapping mappings, Location locus)
+    : Stmt (std::move (mappings)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  EmptyStmt *clone_stmt_impl () const override { return new EmptyStmt (*this); }
+};
+
+/* Variable assignment let statement - type of "declaration statement" as it
+ * introduces new name into scope */
+class LetStmt : public Stmt
+{
+  // bool has_outer_attrs;
+  AST::AttrVec outer_attrs;
+
+  std::unique_ptr<Pattern> variables_pattern;
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  // bool has_init_expr;
+  std::unique_ptr<Expr> init_expr;
+
+  Location locus;
+
+public:
+  // Returns whether let statement has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether let statement has a given return type.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether let statement has an initialisation expression.
+  bool has_init_expr () const { return init_expr != nullptr; }
+
+  std::string as_string () const override;
+
+  LetStmt (Analysis::NodeMapping mappings,
+	   std::unique_ptr<Pattern> variables_pattern,
+	   std::unique_ptr<Expr> init_expr, std::unique_ptr<Type> type,
+	   AST::AttrVec outer_attrs, Location locus)
+    : Stmt (std::move (mappings)), outer_attrs (std::move (outer_attrs)),
+      variables_pattern (std::move (variables_pattern)),
+      type (std::move (type)), init_expr (std::move (init_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  LetStmt (LetStmt const &other)
+    : Stmt (other.mappings), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overloaded assignment operator to clone
+  LetStmt &operator= (LetStmt const &other)
+  {
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+    else
+      variables_pattern = nullptr;
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    else
+      init_expr = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  LetStmt (LetStmt &&other) = default;
+  LetStmt &operator= (LetStmt &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  HIR::Type *get_type () { return type.get (); }
+
+  HIR::Expr *get_init_expr () { return init_expr.get (); }
+
+  HIR::Pattern *get_pattern () { return variables_pattern.get (); }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LetStmt *clone_stmt_impl () const override { return new LetStmt (*this); }
+};
+
+/* Abstract base class for expression statements (statements containing an
+ * expression) */
+class ExprStmt : public Stmt
+{
+  // TODO: add any useful virtual functions
+
+  Location locus;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  ExprStmt (Analysis::NodeMapping mappings, Location locus)
+    : Stmt (std::move (mappings)), locus (locus)
+  {}
+};
+
+/* Statement containing an expression without a block (or, due to technical
+ * difficulties, can only be guaranteed to hold an expression). */
+class ExprStmtWithoutBlock : public ExprStmt
+{
+  std::unique_ptr<Expr> expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprStmtWithoutBlock (Analysis::NodeMapping mappings,
+			std::unique_ptr<Expr> expr, Location locus)
+    : ExprStmt (std::move (mappings), locus), expr (std::move (expr))
+  {}
+
+  // Copy constructor with clone
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other)
+    : ExprStmt (other), expr (other.expr->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock const &other)
+  {
+    ExprStmt::operator= (other);
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock &&other) = default;
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  Expr *get_expr () { return expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithoutBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithoutBlock (*this);
+  }
+};
+
+// Statement containing an expression with a block
+class ExprStmtWithBlock : public ExprStmt
+{
+  std::unique_ptr<ExprWithBlock> expr;
+  bool must_be_unit;
+
+public:
+  std::string as_string () const override;
+
+  ExprStmtWithBlock (Analysis::NodeMapping mappings,
+		     std::unique_ptr<ExprWithBlock> expr, Location locus,
+		     bool must_be_unit)
+    : ExprStmt (std::move (mappings), locus), expr (std::move (expr)),
+      must_be_unit (must_be_unit)
+  {}
+
+  // Copy constructor with clone
+  ExprStmtWithBlock (ExprStmtWithBlock const &other)
+    : ExprStmt (other), expr (other.expr->clone_expr_with_block ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock const &other)
+  {
+    ExprStmt::operator= (other);
+    expr = other.expr->clone_expr_with_block ();
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithBlock (ExprStmtWithBlock &&other) = default;
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  ExprWithBlock *get_expr () { return expr.get (); }
+
+  bool is_unit_check_needed () const override { return must_be_unit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithBlock (*this);
+  }
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-type.h b/gcc/rust/hir/tree/rust-hir-type.h
new file mode 100644
index 00000000000..0d2e7436acc
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-type.h
@@ -0,0 +1,860 @@
+// 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/>.
+
+#ifndef RUST_HIR_TYPE_H
+#define RUST_HIR_TYPE_H
+
+#include "rust-common.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+
+namespace Rust {
+namespace HIR {
+// definitions moved to rust-ast.h
+class TypeParamBound;
+class Lifetime;
+
+// A trait bound
+class TraitBound : public TypeParamBound
+{
+  bool in_parens;
+  bool opening_question_mark;
+  std::vector<LifetimeParam> for_lifetimes;
+  TypePath type_path;
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Returns whether trait bound has "for" lifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  TraitBound (Analysis::NodeMapping mapping, TypePath type_path, Location locus,
+	      bool in_parens = false, bool opening_question_mark = false,
+	      std::vector<LifetimeParam> for_lifetimes
+	      = std::vector<LifetimeParam> ())
+    : in_parens (in_parens), opening_question_mark (opening_question_mark),
+      for_lifetimes (std::move (for_lifetimes)),
+      type_path (std::move (type_path)), locus (locus), mappings (mapping)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  }
+
+  BoundType get_bound_type () const final override { return TRAITBOUND; }
+
+  TypePath &get_path () { return type_path; }
+
+  const TypePath &get_path () const { return type_path; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitBound *clone_type_param_bound_impl () const override
+  {
+    return new TraitBound (*this);
+  }
+};
+
+// definition moved to rust-ast.h
+class TypeNoBounds;
+
+// An impl trait? Poor reference material here.
+class ImplTraitType : public Type
+{
+  // TypeParamBounds type_param_bounds;
+  // inlined form
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitType *clone_type_impl () const override
+  {
+    return new ImplTraitType (*this);
+  }
+
+public:
+  ImplTraitType (Analysis::NodeMapping mappings,
+		 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+		 Location locus)
+    : Type (mappings, locus), type_param_bounds (std::move (type_param_bounds))
+  {}
+
+  // copy constructor with vector clone
+  ImplTraitType (ImplTraitType const &other)
+    : Type (other.mappings, other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  ImplTraitType &operator= (ImplTraitType const &other)
+  {
+    locus = other.locus;
+    mappings = other.mappings;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  ImplTraitType (ImplTraitType &&other) = default;
+  ImplTraitType &operator= (ImplTraitType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// An opaque value of another type that implements a set of traits
+class TraitObjectType : public Type
+{
+  bool has_dyn;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitObjectType *clone_type_impl () const override
+  {
+    return new TraitObjectType (*this);
+  }
+
+public:
+  TraitObjectType (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+    Location locus, bool is_dyn_dispatch)
+    : Type (mappings, locus), has_dyn (is_dyn_dispatch),
+      type_param_bounds (std::move (type_param_bounds))
+  {}
+
+  // copy constructor with vector clone
+  TraitObjectType (TraitObjectType const &other)
+    : Type (other.mappings, other.locus), has_dyn (other.has_dyn)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  TraitObjectType &operator= (TraitObjectType const &other)
+  {
+    mappings = other.mappings;
+    has_dyn = other.has_dyn;
+    locus = other.locus;
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitObjectType (TraitObjectType &&other) = default;
+  TraitObjectType &operator= (TraitObjectType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+};
+
+// A type with parentheses around it, used to avoid ambiguity.
+class ParenthesisedType : public TypeNoBounds
+{
+  std::unique_ptr<Type> type_in_parens;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ParenthesisedType *clone_type_impl () const override
+  {
+    return new ParenthesisedType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ParenthesisedType *clone_type_no_bounds_impl () const override
+  {
+    return new ParenthesisedType (*this);
+  }
+
+public:
+  // Constructor uses Type pointer for polymorphism
+  ParenthesisedType (Analysis::NodeMapping mappings,
+		     std::unique_ptr<Type> type_inside_parens, Location locus)
+    : TypeNoBounds (mappings, locus),
+      type_in_parens (std::move (type_inside_parens))
+  {}
+
+  /* Copy constructor uses custom deep copy method for type to preserve
+   * polymorphism */
+  ParenthesisedType (ParenthesisedType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      type_in_parens (other.type_in_parens->clone_type ())
+  {}
+
+  // overload assignment operator to use custom clone method
+  ParenthesisedType &operator= (ParenthesisedType const &other)
+  {
+    mappings = other.mappings;
+    type_in_parens = other.type_in_parens->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  ParenthesisedType (ParenthesisedType &&other) = default;
+  ParenthesisedType &operator= (ParenthesisedType &&other) = default;
+
+  std::string as_string () const override
+  {
+    return "(" + type_in_parens->as_string () + ")";
+  }
+
+  // Creates a trait bound (clone of this one's trait bound) - HACK
+  TraitBound *to_trait_bound (bool in_parens ATTRIBUTE_UNUSED) const override
+  {
+    /* NOTE: obviously it is unknown whether the internal type is a trait bound
+     * due to polymorphism, so just let the internal type handle it. As
+     * parenthesised type, it must be in parentheses. */
+    return type_in_parens->to_trait_bound (true);
+  }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// Impl trait with a single bound? Poor reference material here.
+class ImplTraitTypeOneBound : public TypeNoBounds
+{
+  TraitBound trait_bound;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitTypeOneBound *clone_type_impl () const override
+  {
+    return new ImplTraitTypeOneBound (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override
+  {
+    return new ImplTraitTypeOneBound (*this);
+  }
+
+public:
+  ImplTraitTypeOneBound (Analysis::NodeMapping mappings, TraitBound trait_bound,
+			 Location locus)
+    : TypeNoBounds (mappings, locus), trait_bound (std::move (trait_bound))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+class TypePath; // definition moved to "rust-path.h"
+
+/* A type consisting of the "product" of others (the tuple's elements) in a
+ * specific order */
+class TupleType : public TypeNoBounds
+{
+  std::vector<std::unique_ptr<Type>> elems;
+
+public:
+  // Returns whether the tuple type is the unit type, i.e. has no elements.
+  bool is_unit_type () const { return elems.empty (); }
+
+  TupleType (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Type>> elems, Location locus)
+    : TypeNoBounds (mappings, locus), elems (std::move (elems))
+  {}
+
+  // copy constructor with vector clone
+  TupleType (TupleType const &other)
+    : TypeNoBounds (other.mappings, other.locus)
+  {
+    mappings = other.mappings;
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+  }
+
+  // overloaded assignment operator to clone
+  TupleType &operator= (TupleType const &other)
+  {
+    locus = other.locus;
+
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleType (TupleType &&other) = default;
+  TupleType &operator= (TupleType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Type>> &get_elems () { return elems; }
+  const std::vector<std::unique_ptr<Type>> &get_elems () const { return elems; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleType *clone_type_impl () const override { return new TupleType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleType *clone_type_no_bounds_impl () const override
+  {
+    return new TupleType (*this);
+  }
+};
+
+/* A type with no values, representing the result of computations that never
+ * complete. Expressions of NeverType can be coerced into any other types.
+ * Represented as "!". */
+class NeverType : public TypeNoBounds
+{
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NeverType *clone_type_impl () const override { return new NeverType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NeverType *clone_type_no_bounds_impl () const override
+  {
+    return new NeverType (*this);
+  }
+
+public:
+  NeverType (Analysis::NodeMapping mappings, Location locus)
+    : TypeNoBounds (mappings, locus)
+  {}
+
+  std::string as_string () const override { return "! (never type)"; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// A type consisting of a pointer without safety or liveness guarantees
+class RawPointerType : public TypeNoBounds
+{
+private:
+  Mutability mut;
+  std::unique_ptr<Type> type;
+
+public:
+  // Constructor requires pointer for polymorphism reasons
+  RawPointerType (Analysis::NodeMapping mappings, Mutability mut,
+		  std::unique_ptr<Type> type, Location locus)
+    : TypeNoBounds (mappings, locus), mut (mut), type (std::move (type))
+  {}
+
+  // Copy constructor calls custom polymorphic clone function
+  RawPointerType (RawPointerType const &other)
+    : TypeNoBounds (other.mappings, other.locus), mut (other.mut),
+      type (other.type->clone_type ())
+  {}
+
+  // overload assignment operator to use custom clone method
+  RawPointerType &operator= (RawPointerType const &other)
+  {
+    mappings = other.mappings;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  RawPointerType (RawPointerType &&other) = default;
+  RawPointerType &operator= (RawPointerType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  Mutability get_mut () const { return mut; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  bool is_const () const { return mut == Mutability::Imm; }
+
+  std::unique_ptr<Type> &get_base_type () { return type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RawPointerType *clone_type_impl () const override
+  {
+    return new RawPointerType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RawPointerType *clone_type_no_bounds_impl () const override
+  {
+    return new RawPointerType (*this);
+  }
+};
+
+// A type pointing to memory owned by another value
+class ReferenceType : public TypeNoBounds
+{
+  // bool has_lifetime; // TODO: handle in lifetime or something?
+  Lifetime lifetime;
+
+  Mutability mut;
+  std::unique_ptr<Type> type;
+
+public:
+  // Returns whether the reference is mutable or immutable.
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  // Returns whether the reference has a lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Constructor
+  ReferenceType (Analysis::NodeMapping mappings, Mutability mut,
+		 std::unique_ptr<Type> type_no_bounds, Location locus,
+		 Lifetime lifetime)
+    : TypeNoBounds (mappings, locus), lifetime (std::move (lifetime)),
+      mut (mut), type (std::move (type_no_bounds))
+  {}
+
+  // Copy constructor with custom clone method
+  ReferenceType (ReferenceType const &other)
+    : TypeNoBounds (other.mappings, other.locus), lifetime (other.lifetime),
+      mut (other.mut), type (other.type->clone_type ())
+  {}
+
+  // Operator overload assignment operator to custom clone the unique pointer
+  ReferenceType &operator= (ReferenceType const &other)
+  {
+    mappings = other.mappings;
+    lifetime = other.lifetime;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ReferenceType (ReferenceType &&other) = default;
+  ReferenceType &operator= (ReferenceType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  Mutability get_mut () const { return mut; }
+
+  std::unique_ptr<Type> &get_base_type () { return type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferenceType *clone_type_impl () const override
+  {
+    return new ReferenceType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferenceType *clone_type_no_bounds_impl () const override
+  {
+    return new ReferenceType (*this);
+  }
+};
+
+// A fixed-size sequence of elements of a specified type
+class ArrayType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+  std::unique_ptr<Expr> size;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     std::unique_ptr<Expr> array_size, Location locus)
+    : TypeNoBounds (mappings, locus), elem_type (std::move (type)),
+      size (std::move (array_size))
+  {}
+
+  // Copy constructor requires deep copies of both unique pointers
+  ArrayType (ArrayType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      elem_type (other.elem_type->clone_type ()),
+      size (other.size->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy pointers
+  ArrayType &operator= (ArrayType const &other)
+  {
+    mappings = other.mappings;
+    elem_type = other.elem_type->clone_type ();
+    size = other.size->clone_expr ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // move constructors
+  ArrayType (ArrayType &&other) = default;
+  ArrayType &operator= (ArrayType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  Type *get_element_type () { return elem_type.get (); }
+
+  Expr *get_size_expr () { return size.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayType *clone_type_impl () const override { return new ArrayType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayType *clone_type_no_bounds_impl () const override
+  {
+    return new ArrayType (*this);
+  }
+};
+
+/* A dynamically-sized type representing a "view" into a sequence of elements of
+ * a type */
+class SliceType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+
+public:
+  // Constructor requires pointer for polymorphism
+  SliceType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     Location locus)
+    : TypeNoBounds (mappings, locus), elem_type (std::move (type))
+  {}
+
+  // Copy constructor requires deep copy of Type smart pointer
+  SliceType (SliceType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      elem_type (other.elem_type->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  SliceType &operator= (SliceType const &other)
+  {
+    mappings = other.mappings;
+    elem_type = other.elem_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  SliceType (SliceType &&other) = default;
+  SliceType &operator= (SliceType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::unique_ptr<Type> &get_element_type () { return elem_type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SliceType *clone_type_impl () const override { return new SliceType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SliceType *clone_type_no_bounds_impl () const override
+  {
+    return new SliceType (*this);
+  }
+};
+
+/* Type used in generic arguments to explicitly request type inference (wildcard
+ * pattern) */
+class InferredType : public TypeNoBounds
+{
+  // e.g. Vec<_> = whatever
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  InferredType *clone_type_impl () const override
+  {
+    return new InferredType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  InferredType *clone_type_no_bounds_impl () const override
+  {
+    return new InferredType (*this);
+  }
+
+public:
+  InferredType (Analysis::NodeMapping mappings, Location locus)
+    : TypeNoBounds (mappings, locus)
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+class QualifiedPathInType; // definition moved to "rust-path.h"
+
+// A possibly named param used in a BaseFunctionType
+struct MaybeNamedParam
+{
+public:
+  enum ParamKind
+  {
+    UNNAMED,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  std::unique_ptr<Type> param_type;
+
+  ParamKind param_kind;
+  Identifier name; // technically, can be an identifier or '_'
+
+  Location locus;
+
+public:
+  MaybeNamedParam (Identifier name, ParamKind param_kind,
+		   std::unique_ptr<Type> param_type, Location locus)
+    : param_type (std::move (param_type)), param_kind (param_kind),
+      name (std::move (name)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MaybeNamedParam (MaybeNamedParam const &other)
+    : param_type (other.param_type->clone_type ()),
+      param_kind (other.param_kind), name (other.name), locus (other.locus)
+  {}
+
+  ~MaybeNamedParam () = default;
+
+  // Overloaded assignment operator with clone
+  MaybeNamedParam &operator= (MaybeNamedParam const &other)
+  {
+    name = other.name;
+    param_kind = other.param_kind;
+    param_type = other.param_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  MaybeNamedParam (MaybeNamedParam &&other) = default;
+  MaybeNamedParam &operator= (MaybeNamedParam &&other) = default;
+
+  std::string as_string () const;
+
+  // Returns whether the param is in an error state.
+  bool is_error () const { return param_type == nullptr; }
+
+  // Creates an error state param.
+  static MaybeNamedParam create_error ()
+  {
+    return MaybeNamedParam ("", UNNAMED, nullptr, Location ());
+  }
+
+  Location get_locus () const { return locus; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (param_type != nullptr);
+    return param_type;
+  }
+
+  ParamKind get_param_kind () const { return param_kind; }
+
+  Identifier get_name () const { return name; }
+};
+
+/* A function pointer type - can be created via coercion from function items and
+ * non- capturing closures. */
+class BareFunctionType : public TypeNoBounds
+{
+  // bool has_for_lifetimes;
+  // ForLifetimes for_lifetimes;
+  std::vector<LifetimeParam> for_lifetimes; // inlined version
+
+  FunctionQualifiers function_qualifiers;
+  std::vector<MaybeNamedParam> params;
+  bool is_variadic;
+
+  std::unique_ptr<Type> return_type; // inlined version
+
+public:
+  // Whether a return type is defined with the function.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Whether the function has ForLifetimes.
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  BareFunctionType (Analysis::NodeMapping mappings,
+		    std::vector<LifetimeParam> lifetime_params,
+		    FunctionQualifiers qualifiers,
+		    std::vector<MaybeNamedParam> named_params, bool is_variadic,
+		    std::unique_ptr<Type> type, Location locus)
+    : TypeNoBounds (mappings, locus),
+      for_lifetimes (std::move (lifetime_params)),
+      function_qualifiers (std::move (qualifiers)),
+      params (std::move (named_params)), is_variadic (is_variadic),
+      return_type (std::move (type))
+  {}
+
+  // Copy constructor with clone
+  BareFunctionType (BareFunctionType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      for_lifetimes (other.for_lifetimes),
+      function_qualifiers (other.function_qualifiers), params (other.params),
+      is_variadic (other.is_variadic),
+      return_type (other.return_type->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  BareFunctionType &operator= (BareFunctionType const &other)
+  {
+    mappings = other.mappings;
+    for_lifetimes = other.for_lifetimes;
+    function_qualifiers = other.function_qualifiers;
+    params = other.params;
+    is_variadic = other.is_variadic;
+    return_type = other.return_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  BareFunctionType (BareFunctionType &&other) = default;
+  BareFunctionType &operator= (BareFunctionType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<MaybeNamedParam> &get_function_params () { return params; }
+  const std::vector<MaybeNamedParam> &get_function_params () const
+  {
+    return params;
+  }
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BareFunctionType *clone_type_impl () const override
+  {
+    return new BareFunctionType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BareFunctionType *clone_type_no_bounds_impl () const override
+  {
+    return new BareFunctionType (*this);
+  }
+};
+
+/* TODO: possible types
+ * struct type?
+ * "enum" (tagged union) type?
+ * C-like union type?
+ * function item type?
+ * closure expression types?
+ * primitive types (bool, int, float, char, str (the slice))
+ * Although supposedly TypePaths are used to reference these types (including
+ * primitives) */
+
+/* FIXME: Incomplete spec references:
+ *  anonymous type parameters, aka "impl Trait in argument position" - impl then
+ * trait bounds abstract return types, aka "impl Trait in return position" -
+ * impl then trait bounds */
+} // namespace HIR
+} // namespace Rust
+
+#endif
