public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Refactor the TypeResolution pass to use fine grained visitors
@ 2022-08-11 19:19 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-08-11 19:19 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2ad0cde0a506d3a8a284a7e1178830f53648f9c3

commit 2ad0cde0a506d3a8a284a7e1178830f53648f9c3
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Wed Aug 10 16:54:16 2022 +0100

    Refactor the TypeResolution pass to use fine grained visitors
    
    This is a huge refactor but no logic has been changed. Most implementation
    has now been moved from the headers and into cc files now. This should
    help alot with compilation times when working within the type system.
    
    This is another incremental step to improve our coercion site logic as also
    support forward declared items.
    
    Addresses #1455

Diff:
---
 gcc/rust/Make-lang.in                              |   3 +
 gcc/rust/hir/tree/rust-hir-expr.h                  |  28 +-
 .../typecheck/rust-hir-inherent-impl-overlap.h     |   6 +-
 gcc/rust/typecheck/rust-hir-path-probe.h           |  10 +-
 gcc/rust/typecheck/rust-hir-trait-resolve.h        |  12 +-
 gcc/rust/typecheck/rust-hir-type-bounds.h          |   2 -
 gcc/rust/typecheck/rust-hir-type-check-base.h      |   6 +-
 gcc/rust/typecheck/rust-hir-type-check-enumitem.cc | 213 +++++
 gcc/rust/typecheck/rust-hir-type-check-enumitem.h  | 177 +---
 gcc/rust/typecheck/rust-hir-type-check-expr.cc     | 966 ++++++++++++++++++++
 gcc/rust/typecheck/rust-hir-type-check-expr.h      | 977 ++-------------------
 gcc/rust/typecheck/rust-hir-type-check-implitem.cc | 583 ++++++++++++
 gcc/rust/typecheck/rust-hir-type-check-implitem.h  | 540 +-----------
 gcc/rust/typecheck/rust-hir-type-check-item.cc     |  19 +-
 gcc/rust/typecheck/rust-hir-type-check-item.h      |  28 +-
 gcc/rust/typecheck/rust-hir-type-check-path.cc     |   2 +
 gcc/rust/typecheck/rust-hir-type-check-pattern.cc  |  46 +
 gcc/rust/typecheck/rust-hir-type-check-pattern.h   |  34 +-
 gcc/rust/typecheck/rust-hir-type-check-stmt.cc     | 498 +++++++++++
 gcc/rust/typecheck/rust-hir-type-check-stmt.h      | 511 +----------
 .../typecheck/rust-hir-type-check-struct-field.h   |  27 +-
 gcc/rust/typecheck/rust-hir-type-check-struct.cc   |  37 +-
 gcc/rust/typecheck/rust-hir-type-check-toplevel.cc |  16 +-
 gcc/rust/typecheck/rust-hir-type-check-toplevel.h  |  23 +-
 gcc/rust/typecheck/rust-hir-type-check-type.cc     |  53 +-
 gcc/rust/typecheck/rust-hir-type-check-type.h      |  41 +-
 gcc/rust/typecheck/rust-hir-type-check.cc          |  46 +-
 gcc/rust/typecheck/rust-tycheck-dump.h             |   4 +-
 28 files changed, 2666 insertions(+), 2242 deletions(-)

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 886bafbe1e0..23dad26e8c4 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -117,6 +117,9 @@ GRS_OBJS = \
     rust/rust-hir-type-check-struct.o \
     rust/rust-hir-type-check-pattern.o \
     rust/rust-hir-type-check-expr.o \
+    rust/rust-hir-type-check-stmt.o \
+    rust/rust-hir-type-check-enumitem.o \
+    rust/rust-hir-type-check-implitem.o \
     rust/rust-hir-dot-operator.o \
     rust/rust-coercion.o \
     rust/rust-hir-type-check-base.o \
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h
index 7cb86a69b2c..83278529646 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.h
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -1409,6 +1409,13 @@ public:
 class StructExprField
 {
 public:
+  enum StructExprFieldKind
+  {
+    IDENTIFIER_VALUE,
+    IDENTIFIER,
+    INDEX_VALUE,
+  };
+
   virtual ~StructExprField () {}
 
   // Unique pointer custom clone function
@@ -1426,6 +1433,8 @@ public:
 
   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;
@@ -1441,11 +1450,11 @@ protected:
 // Identifier-only variant of StructExprField HIR node
 class StructExprFieldIdentifier : public StructExprField
 {
-public:
+private:
   Identifier field_name;
 
   // TODO: should this store location data?
-
+public:
   StructExprFieldIdentifier (Analysis::NodeMapping mapping,
 			     Identifier field_identifier, Location locus)
     : StructExprField (mapping, locus),
@@ -1459,6 +1468,11 @@ public:
 
   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 */
@@ -1527,6 +1541,11 @@ public:
   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 */
@@ -1558,6 +1577,11 @@ public:
   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 */
diff --git a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
index 9abf87280ad..2890b54a00d 100644
--- a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
+++ b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
@@ -25,10 +25,8 @@
 namespace Rust {
 namespace Resolver {
 
-class ImplItemToName : public TypeCheckBase
+class ImplItemToName : private TypeCheckBase, private HIR::HIRImplVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static bool resolve (HIR::ImplItem *item, std::string &name_result)
   {
@@ -66,8 +64,6 @@ private:
 
 class OverlappingImplItemPass : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static void go ()
   {
diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h
index 348b9f775e8..bd4f91e49bf 100644
--- a/gcc/rust/typecheck/rust-hir-path-probe.h
+++ b/gcc/rust/typecheck/rust-hir-path-probe.h
@@ -125,11 +125,8 @@ struct PathProbeCandidate
   bool is_error () const { return type == ERROR; }
 };
 
-class PathProbeType : public TypeCheckBase
+class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor
 {
-protected:
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static std::vector<PathProbeCandidate>
   Probe (const TyTy::BaseType *receiver,
@@ -452,10 +449,9 @@ protected:
   DefId specific_trait_id;
 };
 
-class ReportMultipleCandidateError : private TypeCheckBase
+class ReportMultipleCandidateError : private TypeCheckBase,
+				     private HIR::HIRImplVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static void Report (std::vector<PathProbeCandidate> &candidates,
 		      const HIR::PathIdentSegment &query, Location query_locus)
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h
index a73b67fc142..c4aaf42b141 100644
--- a/gcc/rust/typecheck/rust-hir-trait-resolve.h
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h
@@ -20,19 +20,15 @@
 #define RUST_HIR_TRAIT_RESOLVE_H
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-tyty-visitor.h"
 #include "rust-hir-type-check-type.h"
 #include "rust-hir-trait-ref.h"
-#include "rust-expr.h"
 
 namespace Rust {
 namespace Resolver {
 
-class ResolveTraitItemToRef : public TypeCheckBase
+class ResolveTraitItemToRef : public TypeCheckBase,
+			      private HIR::HIRTraitItemVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static TraitItemReference
   Resolve (HIR::TraitItem &item, TyTy::BaseType *self,
@@ -59,9 +55,9 @@ private:
   std::vector<TyTy::SubstitutionParamMapping> substitutions;
 };
 
-class TraitResolver : public TypeCheckBase
+class TraitResolver : public TypeCheckBase, private HIR::HIRFullVisitorBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
+  using HIR::HIRFullVisitorBase::visit;
 
 public:
   static TraitReference *Resolve (HIR::TypePath &path);
diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h
index a0016ee7f1f..44400efbbf7 100644
--- a/gcc/rust/typecheck/rust-hir-type-bounds.h
+++ b/gcc/rust/typecheck/rust-hir-type-bounds.h
@@ -28,8 +28,6 @@ namespace Resolver {
 
 class TypeBoundsProbe : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
   Probe (const TyTy::BaseType *receiver)
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h
index b6f4b636789..449ff6ab92a 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.h
@@ -30,13 +30,9 @@ namespace Rust {
 namespace Resolver {
 
 class TraitReference;
-
-// base class to allow derivatives to overload as needed
-class TypeCheckBase : public HIR::HIRFullVisitorBase
+class TypeCheckBase
 {
 public:
-  using Rust::HIR::HIRFullVisitorBase::visit;
-
   virtual ~TypeCheckBase () {}
 
   static TyTy::BaseType *coercion_site (HirId id, TyTy::BaseType *lhs,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
new file mode 100644
index 00000000000..e65b2011d36
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
@@ -0,0 +1,213 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-enumitem.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::VariantDef *
+TypeCheckEnumItem::Resolve (HIR::EnumItem *item, int64_t last_discriminant)
+{
+  TypeCheckEnumItem resolver (last_discriminant);
+  switch (item->get_enum_item_kind ())
+    {
+    case HIR::EnumItem::EnumItemKind::Named:
+      resolver.visit (static_cast<HIR::EnumItem &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Tuple:
+      resolver.visit (static_cast<HIR::EnumItemTuple &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Struct:
+      resolver.visit (static_cast<HIR::EnumItemStruct &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Discriminant:
+      resolver.visit (static_cast<HIR::EnumItemDiscriminant &> (*item));
+      break;
+    }
+  return resolver.variant;
+}
+
+TypeCheckEnumItem::TypeCheckEnumItem (int64_t last_discriminant)
+  : TypeCheckBase (), variant (nullptr), last_discriminant (last_discriminant)
+{}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItem &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident, discim_expr);
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemDiscriminant &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  auto &discriminant = item.get_discriminant_expression ();
+  auto capacity_type = TypeCheckExpr::Resolve (discriminant.get ());
+  if (capacity_type->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::ISizeType *expected_ty
+    = new TyTy::ISizeType (discriminant->get_mappings ().get_hirid ());
+  context->insert_type (discriminant->get_mappings (), expected_ty);
+
+  auto unified = expected_ty->unify (capacity_type);
+  if (unified->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					     &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  item.get_discriminant_expression ().get ());
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemTuple &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  std::vector<TyTy::StructFieldType *> fields;
+  size_t idx = 0;
+  for (auto &field : item.get_tuple_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     std::to_string (idx), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+      idx++;
+    }
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  TyTy::VariantDef::VariantType::TUPLE,
+				  discim_expr, fields);
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemStruct &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &field : item.get_struct_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     field.get_field_name (), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+    }
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discrim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  TyTy::VariantDef::VariantType::STRUCT,
+				  discrim_expr, fields);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.h b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
index bc91f458b72..c771ea3782d 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
@@ -21,187 +21,24 @@
 
 #include "rust-hir-type-check-base.h"
 #include "rust-hir-full.h"
-#include "rust-hir-type-check-type.h"
-#include "rust-hir-type-check-expr.h"
-
-extern ::Backend *
-rust_get_backend ();
 
 namespace Rust {
 namespace Resolver {
 
 class TypeCheckEnumItem : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static TyTy::VariantDef *Resolve (HIR::EnumItem *item,
-				    int64_t last_discriminant)
-  {
-    TypeCheckEnumItem resolver (last_discriminant);
-    item->accept_vis (resolver);
-    return resolver.variant;
-  }
-
-  void visit (HIR::EnumItem &item) override
-  {
-    if (last_discriminant == INT64_MAX)
-      rust_error_at (item.get_locus (), "discriminant too big");
-
-    Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
-				   item.get_mappings ().get_nodeid (),
-				   mappings->get_next_hir_id (
-				     item.get_mappings ().get_crate_num ()),
-				   item.get_mappings ().get_local_defid ());
-    HIR::LiteralExpr *discim_expr
-      = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
-			      HIR::Literal::LitType::INT,
-			      PrimitiveCoreType::CORETYPE_I64,
-			      item.get_locus (), {});
-
-    TyTy::BaseType *isize = nullptr;
-    bool ok = context->lookup_builtin ("isize", &isize);
-    rust_assert (ok);
-    context->insert_type (mapping, isize);
-
-    const CanonicalPath *canonical_path = nullptr;
-    ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
-					  &canonical_path);
-    rust_assert (ok);
-
-    RustIdent ident{*canonical_path, item.get_locus ()};
-    variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
-				    item.get_identifier (), ident, discim_expr);
-  }
-
-  void visit (HIR::EnumItemDiscriminant &item) override
-  {
-    if (last_discriminant == INT64_MAX)
-      rust_error_at (item.get_locus (), "discriminant too big");
-
-    auto &discriminant = item.get_discriminant_expression ();
-    auto capacity_type = TypeCheckExpr::Resolve (discriminant.get ());
-    if (capacity_type->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    TyTy::ISizeType *expected_ty
-      = new TyTy::ISizeType (discriminant->get_mappings ().get_hirid ());
-    context->insert_type (discriminant->get_mappings (), expected_ty);
-
-    auto unified = expected_ty->unify (capacity_type);
-    if (unified->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok
-      = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
-					 &canonical_path);
-    rust_assert (ok);
-
-    RustIdent ident{*canonical_path, item.get_locus ()};
-    variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
-				    item.get_identifier (), ident,
-				    item.get_discriminant_expression ().get ());
-  }
-
-  void visit (HIR::EnumItemTuple &item) override
-  {
-    if (last_discriminant == INT64_MAX)
-      rust_error_at (item.get_locus (), "discriminant too big");
-
-    std::vector<TyTy::StructFieldType *> fields;
-    size_t idx = 0;
-    for (auto &field : item.get_tuple_fields ())
-      {
-	TyTy::BaseType *field_type
-	  = TypeCheckType::Resolve (field.get_field_type ().get ());
-	TyTy::StructFieldType *ty_field
-	  = new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
-				       std::to_string (idx), field_type);
-	fields.push_back (ty_field);
-	context->insert_type (field.get_mappings (),
-			      ty_field->get_field_type ());
-	idx++;
-      }
-
-    Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
-				   item.get_mappings ().get_nodeid (),
-				   mappings->get_next_hir_id (
-				     item.get_mappings ().get_crate_num ()),
-				   item.get_mappings ().get_local_defid ());
-    HIR::LiteralExpr *discim_expr
-      = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
-			      HIR::Literal::LitType::INT,
-			      PrimitiveCoreType::CORETYPE_I64,
-			      item.get_locus (), {});
-
-    TyTy::BaseType *isize = nullptr;
-    bool ok = context->lookup_builtin ("isize", &isize);
-    rust_assert (ok);
-    context->insert_type (mapping, isize);
-
-    const CanonicalPath *canonical_path = nullptr;
-    ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
-					  &canonical_path);
-    rust_assert (ok);
-
-    RustIdent ident{*canonical_path, item.get_locus ()};
-    variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
-				    item.get_identifier (), ident,
-				    TyTy::VariantDef::VariantType::TUPLE,
-				    discim_expr, fields);
-  }
-
-  void visit (HIR::EnumItemStruct &item) override
-  {
-    if (last_discriminant == INT64_MAX)
-      rust_error_at (item.get_locus (), "discriminant too big");
-
-    std::vector<TyTy::StructFieldType *> fields;
-    for (auto &field : item.get_struct_fields ())
-      {
-	TyTy::BaseType *field_type
-	  = TypeCheckType::Resolve (field.get_field_type ().get ());
-	TyTy::StructFieldType *ty_field
-	  = new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
-				       field.get_field_name (), field_type);
-	fields.push_back (ty_field);
-	context->insert_type (field.get_mappings (),
-			      ty_field->get_field_type ());
-      }
-
-    Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
-				   item.get_mappings ().get_nodeid (),
-				   mappings->get_next_hir_id (
-				     item.get_mappings ().get_crate_num ()),
-				   item.get_mappings ().get_local_defid ());
-    HIR::LiteralExpr *discrim_expr
-      = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
-			      HIR::Literal::LitType::INT,
-			      PrimitiveCoreType::CORETYPE_I64,
-			      item.get_locus (), {});
-
-    TyTy::BaseType *isize = nullptr;
-    bool ok = context->lookup_builtin ("isize", &isize);
-    rust_assert (ok);
-    context->insert_type (mapping, isize);
-
-    const CanonicalPath *canonical_path = nullptr;
-    ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
-					  &canonical_path);
-    rust_assert (ok);
+				    int64_t last_discriminant);
 
-    RustIdent ident{*canonical_path, item.get_locus ()};
-    variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
-				    item.get_identifier (), ident,
-				    TyTy::VariantDef::VariantType::STRUCT,
-				    discrim_expr, fields);
-  }
+protected:
+  void visit (HIR::EnumItem &item);
+  void visit (HIR::EnumItemDiscriminant &item);
+  void visit (HIR::EnumItemTuple &item);
+  void visit (HIR::EnumItemStruct &item);
 
 private:
-  TypeCheckEnumItem (int64_t last_discriminant)
-    : TypeCheckBase (), variant (nullptr), last_discriminant (last_discriminant)
-  {}
+  TypeCheckEnumItem (int64_t last_discriminant);
 
   TyTy::VariantDef *variant;
   int64_t last_discriminant;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index ff1165d093f..03999baffa4 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -16,11 +16,555 @@
 // along with GCC; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
+#include "rust-hir-full.h"
+#include "rust-tyty-call.h"
+#include "rust-hir-type-check-struct-field.h"
+#include "rust-hir-path-probe.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-hir-type-check-pattern.h"
 #include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-stmt.h"
 
 namespace Rust {
 namespace Resolver {
 
+TypeCheckExpr::TypeCheckExpr () : TypeCheckBase (), infered (nullptr) {}
+
+// Perform type checking on expr. Also runs type unification algorithm.
+// Returns the unified type of expr
+TyTy::BaseType *
+TypeCheckExpr::Resolve (HIR::Expr *expr)
+{
+  TypeCheckExpr resolver;
+  expr->accept_vis (resolver);
+
+  if (resolver.infered == nullptr)
+    {
+      // FIXME
+      // this is an internal error message for debugging and should be removed
+      // at some point
+      rust_error_at (expr->get_locus (), "failed to type resolve expression");
+      return new TyTy::ErrorType (expr->get_mappings ().get_hirid ());
+    }
+
+  auto ref = expr->get_mappings ().get_hirid ();
+  resolver.infered->set_ref (ref);
+  resolver.context->insert_type (expr->get_mappings (), resolver.infered);
+
+  return resolver.infered;
+}
+
+void
+TypeCheckExpr::visit (HIR::TupleIndexExpr &expr)
+{
+  auto resolved = TypeCheckExpr::Resolve (expr.get_tuple_expr ().get ());
+  if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_tuple_expr ()->get_locus (),
+		     "failed to resolve TupleIndexExpr receiver");
+      return;
+    }
+
+  // FIXME does this require autoderef here?
+  if (resolved->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (resolved);
+      resolved = r->get_base ();
+    }
+
+  bool is_valid_type = resolved->get_kind () == TyTy::TypeKind::ADT
+		       || resolved->get_kind () == TyTy::TypeKind::TUPLE;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_tuple_expr ()->get_locus (),
+		     "Expected Tuple or ADT got: %s",
+		     resolved->as_string ().c_str ());
+      return;
+    }
+
+  if (resolved->get_kind () == TyTy::TypeKind::TUPLE)
+    {
+      TyTy::TupleType *tuple = static_cast<TyTy::TupleType *> (resolved);
+      TupleIndex index = expr.get_tuple_index ();
+      if ((size_t) index >= tuple->num_fields ())
+	{
+	  rust_error_at (expr.get_locus (), "unknown field at index %i", index);
+	  return;
+	}
+
+      auto field_tyty = tuple->get_field ((size_t) index);
+      if (field_tyty == nullptr)
+	{
+	  rust_error_at (expr.get_locus (),
+			 "failed to lookup field type at index %i", index);
+	  return;
+	}
+
+      infered = field_tyty;
+      return;
+    }
+
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (resolved);
+  rust_assert (!adt->is_enum ());
+  rust_assert (adt->number_of_variants () == 1);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  TupleIndex index = expr.get_tuple_index ();
+  if ((size_t) index >= variant->num_fields ())
+    {
+      rust_error_at (expr.get_locus (), "unknown field at index %i", index);
+      return;
+    }
+
+  auto field_tyty = variant->get_field_at_index ((size_t) index);
+  if (field_tyty == nullptr)
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup field type at index %i", index);
+      return;
+    }
+
+  infered = field_tyty->get_field_type ();
+}
+
+void
+TypeCheckExpr::visit (HIR::TupleExpr &expr)
+{
+  if (expr.is_unit ())
+    {
+      auto unit_node_id = resolver->get_unit_type_node_id ();
+      if (!context->lookup_builtin (unit_node_id, &infered))
+	{
+	  rust_error_at (expr.get_locus (),
+			 "failed to lookup builtin unit type");
+	}
+      return;
+    }
+
+  std::vector<TyTy::TyVar> fields;
+  for (auto &elem : expr.get_tuple_elems ())
+    {
+      auto field_ty = TypeCheckExpr::Resolve (elem.get ());
+      fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
+    }
+  infered = new TyTy::TupleType (expr.get_mappings ().get_hirid (),
+				 expr.get_locus (), fields);
+}
+
+void
+TypeCheckExpr::visit (HIR::ReturnExpr &expr)
+{
+  auto fn_return_tyty = context->peek_return_type ();
+  rust_assert (fn_return_tyty != nullptr);
+
+  TyTy::BaseType *expr_ty
+    = expr.has_return_expr ()
+	? TypeCheckExpr::Resolve (expr.get_expr ())
+	: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  infered = fn_return_tyty->unify (expr_ty);
+  fn_return_tyty->append_reference (expr_ty->get_ref ());
+  for (auto &ref : infered->get_combined_refs ())
+    fn_return_tyty->append_reference (ref);
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::CallExpr &expr)
+{
+  TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ());
+
+  bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT
+		    || function_tyty->get_kind () == TyTy::TypeKind::FNDEF
+		    || function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
+  if (!valid_tyty)
+    {
+      rust_error_at (expr.get_locus (),
+		     "Failed to resolve expression of function call");
+      return;
+    }
+
+  TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node ();
+  if (function_tyty->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (function_tyty);
+      if (adt->is_enum ())
+	{
+	  // lookup variant id
+	  HirId variant_id;
+	  bool ok = context->lookup_variant_definition (
+	    expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id);
+	  rust_assert (ok);
+
+	  TyTy::VariantDef *lookup_variant = nullptr;
+	  ok = adt->lookup_variant_by_id (variant_id, &lookup_variant);
+	  rust_assert (ok);
+
+	  variant = *lookup_variant;
+	}
+      else
+	{
+	  rust_assert (adt->number_of_variants () == 1);
+	  variant = *adt->get_variants ().at (0);
+	}
+    }
+
+  infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
+}
+
+void
+TypeCheckExpr::visit (HIR::AssignmentExpr &expr)
+{
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs,
+		 expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::CompoundAssignmentExpr &expr)
+{
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  auto lhs = TypeCheckExpr::Resolve (expr.get_left_expr ().get ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_right_expr ().get ());
+
+  // we dont care about the result of the unify from a compound assignment
+  // since this is a unit-type expr
+  auto result = lhs->unify (rhs);
+  if (result->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  auto lang_item_type
+    = Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem (
+      expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
+  if (operator_overloaded)
+    return;
+
+  bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
+  bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
+  bool valid = valid_lhs && valid_rhs;
+  if (!valid)
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot apply this operator to types %s and %s",
+		     lhs->as_string ().c_str (), rhs->as_string ().c_str ());
+      return;
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::IdentifierExpr &expr)
+{
+  NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
+
+  // then lookup the reference_node_id
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
+    {
+      resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
+    }
+
+  if (ref_node_id == UNKNOWN_NODEID)
+    {
+      // FIXME this needs to go away and just return error node
+      rust_error_at (expr.get_locus (), "unresolved node: %s",
+		     expr.as_string ().c_str ());
+      return;
+    }
+
+  // node back to HIR
+  HirId ref;
+  if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+    {
+      // FIXME
+      // this is an internal error
+      rust_error_at (expr.get_locus (), "123 reverse lookup failure");
+      return;
+    }
+
+  // the base reference for this name _must_ have a type set
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (ref, &lookup))
+    {
+      // FIXME
+      // this is an internal error
+      rust_error_at (mappings->lookup_location (ref),
+		     "Failed to resolve IdentifierExpr type: %s",
+		     expr.as_string ().c_str ());
+      return;
+    }
+
+  infered = lookup->clone ();
+
+  // Generic unit structs look like an identifier but they actually need be
+  // handled as a path-in-expression so this gives us a chance to infer the
+  // generic parameters.
+  // see https://github.com/Rust-GCC/gccrs/issues/1447
+  bool is_unit_struct
+    = infered->get_kind () == TyTy::TypeKind::ADT && infered->is_unit ();
+  if (is_unit_struct && infered->needs_generic_substitutions ())
+    {
+      infered = SubstMapper::InferSubst (infered, expr.get_locus ());
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::LiteralExpr &expr)
+{
+  infered = resolve_literal (expr.get_mappings (), expr.get_literal (),
+			     expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  auto lang_item_type
+    = Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
+  if (operator_overloaded)
+    return;
+
+  bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
+  bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
+  bool valid = valid_lhs && valid_rhs;
+  if (!valid)
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot apply this operator to types %s and %s",
+		     lhs->as_string ().c_str (), rhs->as_string ().c_str ());
+      return;
+    }
+
+  switch (expr.get_expr_type ())
+    {
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      infered = rhs->cast (lhs);
+      break;
+
+    default:
+      infered = lhs->unify (rhs);
+      break;
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::ComparisonExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  auto result = lhs->unify (rhs);
+  if (result == nullptr || result->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  bool ok = context->lookup_builtin ("bool", &infered);
+  rust_assert (ok);
+}
+
+void
+TypeCheckExpr::visit (HIR::LazyBooleanExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  // we expect the lhs and rhs must be bools at this point
+  TyTy::BoolType elhs (expr.get_mappings ().get_hirid ());
+  lhs = elhs.unify (lhs);
+  if (lhs->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BoolType rlhs (expr.get_mappings ().get_hirid ());
+  rhs = elhs.unify (rhs);
+  if (lhs->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  infered = lhs->unify (rhs);
+}
+
+void
+TypeCheckExpr::visit (HIR::NegationExpr &expr)
+{
+  auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  // check for operator overload
+  auto lang_item_type = Analysis::RustLangItem::NegationOperatorToLangItem (
+    expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, negated_expr_ty,
+				 nullptr);
+  if (operator_overloaded)
+    return;
+
+  // https://doc.rust-lang.org/reference/expressions/operator-expr.html#negation-operators
+  switch (expr.get_expr_type ())
+    {
+      case NegationOperator::NEGATE: {
+	bool valid
+	  = (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::FLOAT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::ISIZE)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::USIZE)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::INTEGRAL))
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::FLOAT));
+	if (!valid)
+	  {
+	    rust_error_at (expr.get_locus (), "cannot apply unary - to %s",
+			   negated_expr_ty->as_string ().c_str ());
+	    return;
+	  }
+      }
+      break;
+
+      case NegationOperator::NOT: {
+	bool valid
+	  = (negated_expr_ty->get_kind () == TyTy::TypeKind::BOOL)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::INTEGRAL));
+	if (!valid)
+	  {
+	    rust_error_at (expr.get_locus (), "cannot apply unary %<!%> to %s",
+			   negated_expr_ty->as_string ().c_str ());
+	    return;
+	  }
+      }
+      break;
+    }
+
+  infered = negated_expr_ty->clone ();
+  infered->append_reference (negated_expr_ty->get_ref ());
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExpr &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  TypeCheckExpr::Resolve (expr.get_if_block ());
+
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExprConseqElse &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
+  auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block ());
+
+  if (if_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = else_blk_resolved;
+  else if (else_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = if_blk_resolved;
+  else
+    infered = if_blk_resolved->unify (else_blk_resolved);
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExprConseqIf &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
+  auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_conseq_if_expr ());
+
+  if (if_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = else_blk_resolved;
+  else if (else_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = if_blk_resolved;
+  else
+    infered = if_blk_resolved->unify (else_blk_resolved);
+}
+
+void
+TypeCheckExpr::visit (HIR::IfLetExpr &expr)
+{
+  // this needs to perform a least upper bound coercion on the blocks and then
+  // unify the scruintee and arms
+  TyTy::BaseType *scrutinee_tyty
+    = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      TyTy::BaseType *kase_arm_ty
+	= TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
+
+      TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
+      if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+
+  TypeCheckExpr::Resolve (expr.get_if_block ());
+
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::UnsafeBlockExpr &expr)
+{
+  infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BlockExpr &expr)
+{
+  for (auto &s : expr.get_statements ())
+    {
+      if (!s->is_item ())
+	continue;
+
+      TypeCheckStmt::Resolve (s.get ());
+    }
+
+  for (auto &s : expr.get_statements ())
+    {
+      if (s->is_item ())
+	continue;
+
+      auto resolved = TypeCheckStmt::Resolve (s.get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (s->get_locus (), "failure to resolve type");
+	  return;
+	}
+
+      if (s->is_unit_check_needed () && !resolved->is_unit ())
+	{
+	  auto unit
+	    = TyTy::TupleType::get_unit_type (s->get_mappings ().get_hirid ());
+	  resolved = unit->unify (resolved);
+	}
+    }
+
+  if (expr.has_expr ())
+    infered = TypeCheckExpr::Resolve (expr.get_final_expr ().get ())->clone ();
+  else if (expr.is_tail_reachable ())
+    infered
+      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+  else
+    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
 void
 TypeCheckExpr::visit (HIR::RangeFromToExpr &expr)
 {
@@ -304,6 +848,146 @@ TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
 		 index_expr_ty->get_name ().c_str ());
 }
 
+void
+TypeCheckExpr::visit (HIR::ArrayExpr &expr)
+{
+  HIR::ArrayElems &elements = *expr.get_internal_elements ();
+
+  HIR::Expr *capacity_expr = nullptr;
+  TyTy::BaseType *element_type = nullptr;
+  switch (elements.get_array_expr_type ())
+    {
+      case HIR::ArrayElems::ArrayExprType::COPIED: {
+	HIR::ArrayElemsCopied &elems
+	  = static_cast<HIR::ArrayElemsCopied &> (elements);
+	element_type = TypeCheckExpr::Resolve (elems.get_elem_to_copy ());
+
+	auto capacity_type
+	  = TypeCheckExpr::Resolve (elems.get_num_copies_expr ());
+
+	TyTy::BaseType *expected_ty = nullptr;
+	bool ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (elems.get_num_copies_expr ()->get_mappings (),
+			      expected_ty);
+
+	auto unified = expected_ty->unify (capacity_type);
+	if (unified->get_kind () == TyTy::TypeKind::ERROR)
+	  return;
+
+	capacity_expr = elems.get_num_copies_expr ();
+      }
+      break;
+
+      case HIR::ArrayElems::ArrayExprType::VALUES: {
+	HIR::ArrayElemsValues &elems
+	  = static_cast<HIR::ArrayElemsValues &> (elements);
+
+	std::vector<TyTy::BaseType *> types;
+	for (auto &elem : elems.get_values ())
+	  {
+	    types.push_back (TypeCheckExpr::Resolve (elem.get ()));
+	  }
+
+	element_type
+	  = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty ();
+	for (auto &type : types)
+	  {
+	    element_type = element_type->unify (type);
+	  }
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID,
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+	std::string capacity_str = std::to_string (elems.get_num_elements ());
+	capacity_expr = new HIR::LiteralExpr (mapping, capacity_str,
+					      HIR::Literal::LitType::INT,
+					      PrimitiveCoreType::CORETYPE_USIZE,
+					      Location (), {});
+
+	// mark the type for this implicit node
+	TyTy::BaseType *expected_ty = nullptr;
+	bool ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (mapping, expected_ty);
+      }
+      break;
+    }
+
+  infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (),
+				 expr.get_locus (), *capacity_expr,
+				 TyTy::TyVar (element_type->get_ref ()));
+}
+
+// empty struct
+void
+TypeCheckExpr::visit (HIR::StructExprStruct &struct_expr)
+{
+  TyTy::BaseType *struct_path_ty
+    = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
+  if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
+    {
+      rust_error_at (struct_expr.get_struct_name ().get_locus (),
+		     "expected an ADT type for constructor");
+      return;
+    }
+
+  infered = struct_path_ty;
+}
+
+void
+TypeCheckExpr::visit (HIR::StructExprStructFields &struct_expr)
+{
+  infered = TypeCheckStructExpr::Resolve (&struct_expr);
+}
+
+void
+TypeCheckExpr::visit (HIR::GroupedExpr &expr)
+{
+  infered = TypeCheckExpr::Resolve (expr.get_expr_in_parens ().get ());
+}
+
+void
+TypeCheckExpr::visit (HIR::FieldAccessExpr &expr)
+{
+  auto struct_base = TypeCheckExpr::Resolve (expr.get_receiver_expr ().get ());
+
+  // FIXME does this require autoderef here?
+  if (struct_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (struct_base);
+      struct_base = r->get_base ();
+    }
+
+  bool is_valid_type = struct_base->get_kind () == TyTy::TypeKind::ADT;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_locus (),
+		     "expected algebraic data type got: [%s]",
+		     struct_base->as_string ().c_str ());
+      return;
+    }
+
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (struct_base);
+  rust_assert (!adt->is_enum ());
+  rust_assert (adt->number_of_variants () == 1);
+
+  TyTy::VariantDef *vaiant = adt->get_variants ().at (0);
+
+  TyTy::StructFieldType *lookup = nullptr;
+  bool found = vaiant->lookup_field (expr.get_field_name (), &lookup, nullptr);
+  if (!found)
+    {
+      rust_error_at (expr.get_locus (), "unknown field [%s] for type [%s]",
+		     expr.get_field_name ().c_str (),
+		     adt->as_string ().c_str ());
+      return;
+    }
+
+  infered = lookup->get_field_type ();
+}
+
 void
 TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
 {
@@ -459,6 +1143,232 @@ TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
   infered = function_ret_tyty;
 }
 
+void
+TypeCheckExpr::visit (HIR::LoopExpr &expr)
+{
+  context->push_new_loop_context (expr.get_mappings ().get_hirid (),
+				  expr.get_locus ());
+  TyTy::BaseType *block_expr
+    = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
+  if (!block_expr->is_unit ())
+    {
+      rust_error_at (expr.get_loop_block ()->get_locus (),
+		     "expected %<()%> got %s",
+		     block_expr->as_string ().c_str ());
+      return;
+    }
+
+  TyTy::BaseType *loop_context_type = context->pop_loop_context ();
+
+  bool loop_context_type_infered
+    = (loop_context_type->get_kind () != TyTy::TypeKind::INFER)
+      || ((loop_context_type->get_kind () == TyTy::TypeKind::INFER)
+	  && (((TyTy::InferType *) loop_context_type)->get_infer_kind ()
+	      != TyTy::InferType::GENERAL));
+
+  infered
+    = loop_context_type_infered
+	? loop_context_type
+	: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
+{
+  context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
+
+  TypeCheckExpr::Resolve (expr.get_predicate_expr ().get ());
+  TyTy::BaseType *block_expr
+    = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
+
+  if (!block_expr->is_unit ())
+    {
+      rust_error_at (expr.get_loop_block ()->get_locus (),
+		     "expected %<()%> got %s",
+		     block_expr->as_string ().c_str ());
+      return;
+    }
+
+  context->pop_loop_context ();
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BreakExpr &expr)
+{
+  if (!context->have_loop_context ())
+    {
+      rust_error_at (expr.get_locus (), "cannot %<break%> outside of a loop");
+      return;
+    }
+
+  if (expr.has_break_expr ())
+    {
+      TyTy::BaseType *break_expr_tyty
+	= TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+      TyTy::BaseType *loop_context = context->peek_loop_context ();
+      if (loop_context->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (expr.get_locus (),
+			 "can only break with a value inside %<loop%>");
+	  return;
+	}
+
+      TyTy::BaseType *unified_ty = loop_context->unify (break_expr_tyty);
+      context->swap_head_loop_context (unified_ty);
+    }
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::ContinueExpr &expr)
+{
+  if (!context->have_loop_context ())
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot %<continue%> outside of a loop");
+      return;
+    }
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BorrowExpr &expr)
+{
+  TyTy::BaseType *resolved_base
+    = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  // In Rust this is valid because of DST's
+  //
+  // fn test() {
+  //     let a:&str = "TEST 1";
+  //     let b:&str = &"TEST 2";
+  // }
+  if (resolved_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      const TyTy::ReferenceType *ref
+	= static_cast<const TyTy::ReferenceType *> (resolved_base);
+
+      // this might end up being a more generic is_dyn object check but lets
+      // double check dyn traits type-layout first
+      if (ref->is_dyn_str_type ())
+	{
+	  infered = resolved_base;
+	  return;
+	}
+    }
+
+  if (expr.get_is_double_borrow ())
+    {
+      // FIXME double_reference
+      gcc_unreachable ();
+    }
+
+  infered = new TyTy::ReferenceType (expr.get_mappings ().get_hirid (),
+				     TyTy::TyVar (resolved_base->get_ref ()),
+				     expr.get_mut ());
+}
+
+void
+TypeCheckExpr::visit (HIR::DereferenceExpr &expr)
+{
+  TyTy::BaseType *resolved_base
+    = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, resolved_base, nullptr);
+  if (operator_overloaded)
+    {
+      // operator overloaded deref always refurns a reference type lets assert
+      // this
+      rust_assert (infered->get_kind () == TyTy::TypeKind::REF);
+      resolved_base = infered;
+    }
+
+  bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF
+		       || resolved_base->get_kind () == TyTy::TypeKind::POINTER;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_locus (), "expected reference type got %s",
+		     resolved_base->as_string ().c_str ());
+      return;
+    }
+
+  if (resolved_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *ref_base
+	= static_cast<TyTy::ReferenceType *> (resolved_base);
+      infered = ref_base->get_base ()->clone ();
+    }
+  else
+    {
+      TyTy::PointerType *ref_base
+	= static_cast<TyTy::PointerType *> (resolved_base);
+      infered = ref_base->get_base ()->clone ();
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::TypeCastExpr &expr)
+{
+  TyTy::BaseType *expr_to_convert
+    = TypeCheckExpr::Resolve (expr.get_casted_expr ().get ());
+  TyTy::BaseType *tyty_to_convert_to
+    = TypeCheckType::Resolve (expr.get_type_to_convert_to ().get ());
+
+  infered = expr_to_convert->cast (tyty_to_convert_to);
+}
+
+void
+TypeCheckExpr::visit (HIR::MatchExpr &expr)
+{
+  // this needs to perform a least upper bound coercion on the blocks and then
+  // unify the scruintee and arms
+  TyTy::BaseType *scrutinee_tyty
+    = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
+
+  std::vector<TyTy::BaseType *> kase_block_tys;
+  for (auto &kase : expr.get_match_cases ())
+    {
+      // lets check the arms
+      HIR::MatchArm &kase_arm = kase.get_arm ();
+      for (auto &pattern : kase_arm.get_patterns ())
+	{
+	  TyTy::BaseType *kase_arm_ty
+	    = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
+
+	  TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
+	  if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+
+      // check the kase type
+      TyTy::BaseType *kase_block_ty
+	= TypeCheckExpr::Resolve (kase.get_expr ().get ());
+      kase_block_tys.push_back (kase_block_ty);
+    }
+
+  if (kase_block_tys.size () == 0)
+    {
+      infered
+	= TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+      return;
+    }
+
+  infered = kase_block_tys.at (0);
+  for (size_t i = 1; i < kase_block_tys.size (); i++)
+    {
+      TyTy::BaseType *kase_ty = kase_block_tys.at (i);
+      infered = infered->unify (kase_ty);
+      if (infered->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+}
+
 bool
 TypeCheckExpr::resolve_operator_overload (
   Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr,
@@ -644,5 +1554,61 @@ TypeCheckExpr::resolve_operator_overload (
   return true;
 }
 
+bool
+TypeCheckExpr::validate_arithmetic_type (
+  const TyTy::BaseType *tyty, HIR::ArithmeticOrLogicalExpr::ExprType expr_type)
+{
+  const TyTy::BaseType *type = tyty->destructure ();
+
+  // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators
+  // this will change later when traits are added
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+    case ArithmeticOrLogicalOperator::DIVIDE:
+    case ArithmeticOrLogicalOperator::MODULUS:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::FLOAT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL))
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::FLOAT));
+
+      // integers or bools
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::BOOL)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL));
+
+      // integers only
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL));
+    }
+
+  gcc_unreachable ();
+  return false;
+}
+
 } // namespace Resolver
 } // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
index 0c44f28e4ac..01cb213f002 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -20,888 +20,85 @@
 #define RUST_HIR_TYPE_CHECK_EXPR
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-system.h"
 #include "rust-tyty.h"
-#include "rust-tyty-call.h"
-#include "rust-hir-type-check-struct-field.h"
-#include "rust-hir-path-probe.h"
-#include "rust-substitution-mapper.h"
-#include "rust-hir-trait-resolve.h"
-#include "rust-hir-type-bounds.h"
-#include "rust-hir-dot-operator.h"
-#include "rust-hir-type-check-pattern.h"
 
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckExpr : public TypeCheckBase
+class TypeCheckExpr : public TypeCheckBase, private HIR::HIRExpressionVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  // Perform type checking on expr. Also runs type unification algorithm.
-  // Returns the unified type of expr
-  static TyTy::BaseType *Resolve (HIR::Expr *expr)
-  {
-    TypeCheckExpr resolver;
-    expr->accept_vis (resolver);
-
-    if (resolver.infered == nullptr)
-      {
-	// FIXME
-	// this is an internal error message for debugging and should be removed
-	// at some point
-	rust_error_at (expr->get_locus (), "failed to type resolve expression");
-	return new TyTy::ErrorType (expr->get_mappings ().get_hirid ());
-      }
-
-    auto ref = expr->get_mappings ().get_hirid ();
-    resolver.infered->set_ref (ref);
-    resolver.context->insert_type (expr->get_mappings (), resolver.infered);
-
-    return resolver.infered;
-  }
-
-  void visit (HIR::TupleIndexExpr &expr) override
-  {
-    auto resolved = TypeCheckExpr::Resolve (expr.get_tuple_expr ().get ());
-    if (resolved->get_kind () == TyTy::TypeKind::ERROR)
-      {
-	rust_error_at (expr.get_tuple_expr ()->get_locus (),
-		       "failed to resolve TupleIndexExpr receiver");
-	return;
-      }
-
-    // FIXME does this require autoderef here?
-    if (resolved->get_kind () == TyTy::TypeKind::REF)
-      {
-	TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (resolved);
-	resolved = r->get_base ();
-      }
-
-    bool is_valid_type = resolved->get_kind () == TyTy::TypeKind::ADT
-			 || resolved->get_kind () == TyTy::TypeKind::TUPLE;
-    if (!is_valid_type)
-      {
-	rust_error_at (expr.get_tuple_expr ()->get_locus (),
-		       "Expected Tuple or ADT got: %s",
-		       resolved->as_string ().c_str ());
-	return;
-      }
-
-    if (resolved->get_kind () == TyTy::TypeKind::TUPLE)
-      {
-	TyTy::TupleType *tuple = static_cast<TyTy::TupleType *> (resolved);
-	TupleIndex index = expr.get_tuple_index ();
-	if ((size_t) index >= tuple->num_fields ())
-	  {
-	    rust_error_at (expr.get_locus (), "unknown field at index %i",
-			   index);
-	    return;
-	  }
-
-	auto field_tyty = tuple->get_field ((size_t) index);
-	if (field_tyty == nullptr)
-	  {
-	    rust_error_at (expr.get_locus (),
-			   "failed to lookup field type at index %i", index);
-	    return;
-	  }
-
-	infered = field_tyty;
-	return;
-      }
-
-    TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (resolved);
-    rust_assert (!adt->is_enum ());
-    rust_assert (adt->number_of_variants () == 1);
-
-    TyTy::VariantDef *variant = adt->get_variants ().at (0);
-    TupleIndex index = expr.get_tuple_index ();
-    if ((size_t) index >= variant->num_fields ())
-      {
-	rust_error_at (expr.get_locus (), "unknown field at index %i", index);
-	return;
-      }
-
-    auto field_tyty = variant->get_field_at_index ((size_t) index);
-    if (field_tyty == nullptr)
-      {
-	rust_error_at (expr.get_locus (),
-		       "failed to lookup field type at index %i", index);
-	return;
-      }
-
-    infered = field_tyty->get_field_type ();
-  }
-
-  void visit (HIR::TupleExpr &expr) override
-  {
-    if (expr.is_unit ())
-      {
-	auto unit_node_id = resolver->get_unit_type_node_id ();
-	if (!context->lookup_builtin (unit_node_id, &infered))
-	  {
-	    rust_error_at (expr.get_locus (),
-			   "failed to lookup builtin unit type");
-	  }
-	return;
-      }
-
-    std::vector<TyTy::TyVar> fields;
-    for (auto &elem : expr.get_tuple_elems ())
-      {
-	auto field_ty = TypeCheckExpr::Resolve (elem.get ());
-	fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
-      }
-    infered = new TyTy::TupleType (expr.get_mappings ().get_hirid (),
-				   expr.get_locus (), fields);
-  }
-
-  void visit (HIR::ReturnExpr &expr) override
-  {
-    auto fn_return_tyty = context->peek_return_type ();
-    rust_assert (fn_return_tyty != nullptr);
-
-    TyTy::BaseType *expr_ty
-      = expr.has_return_expr ()
-	  ? TypeCheckExpr::Resolve (expr.get_expr ())
-	  : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-
-    infered = fn_return_tyty->unify (expr_ty);
-    fn_return_tyty->append_reference (expr_ty->get_ref ());
-    for (auto &ref : infered->get_combined_refs ())
-      fn_return_tyty->append_reference (ref);
-
-    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::CallExpr &expr) override
-  {
-    TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ());
-
-    bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT
-		      || function_tyty->get_kind () == TyTy::TypeKind::FNDEF
-		      || function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
-    if (!valid_tyty)
-      {
-	rust_error_at (expr.get_locus (),
-		       "Failed to resolve expression of function call");
-	return;
-      }
-
-    TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node ();
-    if (function_tyty->get_kind () == TyTy::TypeKind::ADT)
-      {
-	TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (function_tyty);
-	if (adt->is_enum ())
-	  {
-	    // lookup variant id
-	    HirId variant_id;
-	    bool ok = context->lookup_variant_definition (
-	      expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id);
-	    rust_assert (ok);
-
-	    TyTy::VariantDef *lookup_variant = nullptr;
-	    ok = adt->lookup_variant_by_id (variant_id, &lookup_variant);
-	    rust_assert (ok);
-
-	    variant = *lookup_variant;
-	  }
-	else
-	  {
-	    rust_assert (adt->number_of_variants () == 1);
-	    variant = *adt->get_variants ().at (0);
-	  }
-      }
-
-    infered
-      = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
-  }
+  static TyTy::BaseType *Resolve (HIR::Expr *expr);
 
+  void visit (HIR::TupleIndexExpr &expr) override;
+  void visit (HIR::TupleExpr &expr) override;
+  void visit (HIR::ReturnExpr &expr) override;
+  void visit (HIR::CallExpr &expr) override;
   void visit (HIR::MethodCallExpr &expr) override;
-
-  void visit (HIR::AssignmentExpr &expr) override
-  {
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-
-    auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
-    auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
-
-    coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs,
-		   expr.get_locus ());
-  }
-
-  void visit (HIR::CompoundAssignmentExpr &expr) override
-  {
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-
-    auto lhs = TypeCheckExpr::Resolve (expr.get_left_expr ().get ());
-    auto rhs = TypeCheckExpr::Resolve (expr.get_right_expr ().get ());
-
-    // we dont care about the result of the unify from a compound assignment
-    // since this is a unit-type expr
-    auto result = lhs->unify (rhs);
-    if (result->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    auto lang_item_type
-      = Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem (
-	expr.get_expr_type ());
-    bool operator_overloaded
-      = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
-    if (operator_overloaded)
-      return;
-
-    bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
-    bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
-    bool valid = valid_lhs && valid_rhs;
-    if (!valid)
-      {
-	rust_error_at (expr.get_locus (),
-		       "cannot apply this operator to types %s and %s",
-		       lhs->as_string ().c_str (), rhs->as_string ().c_str ());
-	return;
-      }
-  }
-
-  void visit (HIR::IdentifierExpr &expr) override
-  {
-    NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
-
-    // then lookup the reference_node_id
-    NodeId ref_node_id = UNKNOWN_NODEID;
-    if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
-      {
-	resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
-      }
-
-    if (ref_node_id == UNKNOWN_NODEID)
-      {
-	// FIXME this needs to go away and just return error node
-	rust_error_at (expr.get_locus (), "unresolved node: %s",
-		       expr.as_string ().c_str ());
-	return;
-      }
-
-    // node back to HIR
-    HirId ref;
-    if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
-      {
-	// FIXME
-	// this is an internal error
-	rust_error_at (expr.get_locus (), "123 reverse lookup failure");
-	return;
-      }
-
-    // the base reference for this name _must_ have a type set
-    TyTy::BaseType *lookup;
-    if (!context->lookup_type (ref, &lookup))
-      {
-	// FIXME
-	// this is an internal error
-	rust_error_at (mappings->lookup_location (ref),
-		       "Failed to resolve IdentifierExpr type: %s",
-		       expr.as_string ().c_str ());
-	return;
-      }
-
-    infered = lookup->clone ();
-
-    // Generic unit structs look like an identifier but they actually need be
-    // handled as a path-in-expression so this gives us a chance to infer the
-    // generic parameters.
-    // see https://github.com/Rust-GCC/gccrs/issues/1447
-    bool is_unit_struct
-      = infered->get_kind () == TyTy::TypeKind::ADT && infered->is_unit ();
-    if (is_unit_struct && infered->needs_generic_substitutions ())
-      {
-	infered = SubstMapper::InferSubst (infered, expr.get_locus ());
-      }
-  }
-
-  void visit (HIR::LiteralExpr &expr) override
-  {
-    infered = resolve_literal (expr.get_mappings (), expr.get_literal (),
-			       expr.get_locus ());
-  }
-
-  void visit (HIR::ArithmeticOrLogicalExpr &expr) override
-  {
-    auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
-    auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
-
-    auto lang_item_type
-      = Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
-    bool operator_overloaded
-      = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
-    if (operator_overloaded)
-      return;
-
-    bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
-    bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
-    bool valid = valid_lhs && valid_rhs;
-    if (!valid)
-      {
-	rust_error_at (expr.get_locus (),
-		       "cannot apply this operator to types %s and %s",
-		       lhs->as_string ().c_str (), rhs->as_string ().c_str ());
-	return;
-      }
-
-    switch (expr.get_expr_type ())
-      {
-      case ArithmeticOrLogicalOperator::LEFT_SHIFT:
-      case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
-	infered = rhs->cast (lhs);
-	break;
-
-      default:
-	infered = lhs->unify (rhs);
-	break;
-      }
-  }
-
-  void visit (HIR::ComparisonExpr &expr) override
-  {
-    auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
-    auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
-
-    auto result = lhs->unify (rhs);
-    if (result == nullptr || result->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    bool ok = context->lookup_builtin ("bool", &infered);
-    rust_assert (ok);
-  }
-
-  void visit (HIR::LazyBooleanExpr &expr) override
-  {
-    auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
-    auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
-
-    // we expect the lhs and rhs must be bools at this point
-    TyTy::BoolType elhs (expr.get_mappings ().get_hirid ());
-    lhs = elhs.unify (lhs);
-    if (lhs->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    TyTy::BoolType rlhs (expr.get_mappings ().get_hirid ());
-    rhs = elhs.unify (rhs);
-    if (lhs->get_kind () == TyTy::TypeKind::ERROR)
-      return;
-
-    infered = lhs->unify (rhs);
-  }
-
-  void visit (HIR::NegationExpr &expr) override
-  {
-    auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ());
-
-    // check for operator overload
-    auto lang_item_type = Analysis::RustLangItem::NegationOperatorToLangItem (
-      expr.get_expr_type ());
-    bool operator_overloaded
-      = resolve_operator_overload (lang_item_type, expr, negated_expr_ty,
-				   nullptr);
-    if (operator_overloaded)
-      return;
-
-    // https://doc.rust-lang.org/reference/expressions/operator-expr.html#negation-operators
-    switch (expr.get_expr_type ())
-      {
-	case NegationOperator::NEGATE: {
-	  bool valid
-	    = (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::FLOAT)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::ISIZE)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::USIZE)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
-		  && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
-		      == TyTy::InferType::INTEGRAL))
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
-		  && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
-		      == TyTy::InferType::FLOAT));
-	  if (!valid)
-	    {
-	      rust_error_at (expr.get_locus (), "cannot apply unary - to %s",
-			     negated_expr_ty->as_string ().c_str ());
-	      return;
-	    }
-	}
-	break;
-
-	case NegationOperator::NOT: {
-	  bool valid
-	    = (negated_expr_ty->get_kind () == TyTy::TypeKind::BOOL)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
-	      || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
-		  && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
-		      == TyTy::InferType::INTEGRAL));
-	  if (!valid)
-	    {
-	      rust_error_at (expr.get_locus (),
-			     "cannot apply unary %<!%> to %s",
-			     negated_expr_ty->as_string ().c_str ());
-	      return;
-	    }
-	}
-	break;
-      }
-
-    infered = negated_expr_ty->clone ();
-    infered->append_reference (negated_expr_ty->get_ref ());
-  }
-
-  void visit (HIR::IfExpr &expr) override
-  {
-    TypeCheckExpr::Resolve (expr.get_if_condition ());
-    TypeCheckExpr::Resolve (expr.get_if_block ());
-
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::IfExprConseqElse &expr) override
-  {
-    TypeCheckExpr::Resolve (expr.get_if_condition ());
-    auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
-    auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block ());
-
-    if (if_blk_resolved->get_kind () == TyTy::NEVER)
-      infered = else_blk_resolved;
-    else if (else_blk_resolved->get_kind () == TyTy::NEVER)
-      infered = if_blk_resolved;
-    else
-      infered = if_blk_resolved->unify (else_blk_resolved);
-  }
-
-  void visit (HIR::IfExprConseqIf &expr) override
-  {
-    TypeCheckExpr::Resolve (expr.get_if_condition ());
-    auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
-    auto else_blk_resolved
-      = TypeCheckExpr::Resolve (expr.get_conseq_if_expr ());
-
-    if (if_blk_resolved->get_kind () == TyTy::NEVER)
-      infered = else_blk_resolved;
-    else if (else_blk_resolved->get_kind () == TyTy::NEVER)
-      infered = if_blk_resolved;
-    else
-      infered = if_blk_resolved->unify (else_blk_resolved);
-  }
-
-  void visit (HIR::IfLetExpr &expr) override
-  {
-    // this needs to perform a least upper bound coercion on the blocks and then
-    // unify the scruintee and arms
-    TyTy::BaseType *scrutinee_tyty
-      = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
-
-    for (auto &pattern : expr.get_patterns ())
-      {
-	TyTy::BaseType *kase_arm_ty
-	  = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
-
-	TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
-	if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
-	  return;
-      }
-
-    TypeCheckExpr::Resolve (expr.get_if_block ());
-
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-  }
-
+  void visit (HIR::AssignmentExpr &expr) override;
+  void visit (HIR::CompoundAssignmentExpr &expr) override;
+  void visit (HIR::IdentifierExpr &expr) override;
+  void visit (HIR::LiteralExpr &expr) override;
+  void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
+  void visit (HIR::ComparisonExpr &expr) override;
+  void visit (HIR::LazyBooleanExpr &expr) override;
+  void visit (HIR::NegationExpr &expr) override;
+  void visit (HIR::IfExpr &expr) override;
+  void visit (HIR::IfExprConseqElse &expr) override;
+  void visit (HIR::IfExprConseqIf &expr) override;
+  void visit (HIR::IfLetExpr &expr) override;
   void visit (HIR::BlockExpr &expr) override;
-
-  void visit (HIR::UnsafeBlockExpr &expr) override
-  {
-    infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ());
-  }
-
+  void visit (HIR::UnsafeBlockExpr &expr) override;
   void visit (HIR::ArrayIndexExpr &expr) override;
-
-  void visit (HIR::ArrayExpr &expr) override
-  {
-    HIR::ArrayElems &elements = *expr.get_internal_elements ();
-
-    HIR::Expr *capacity_expr = nullptr;
-    TyTy::BaseType *element_type = nullptr;
-    switch (elements.get_array_expr_type ())
-      {
-	case HIR::ArrayElems::ArrayExprType::COPIED: {
-	  HIR::ArrayElemsCopied &elems
-	    = static_cast<HIR::ArrayElemsCopied &> (elements);
-	  element_type = TypeCheckExpr::Resolve (elems.get_elem_to_copy ());
-
-	  auto capacity_type
-	    = TypeCheckExpr::Resolve (elems.get_num_copies_expr ());
-
-	  TyTy::BaseType *expected_ty = nullptr;
-	  bool ok = context->lookup_builtin ("usize", &expected_ty);
-	  rust_assert (ok);
-	  context->insert_type (elems.get_num_copies_expr ()->get_mappings (),
-				expected_ty);
-
-	  auto unified = expected_ty->unify (capacity_type);
-	  if (unified->get_kind () == TyTy::TypeKind::ERROR)
-	    return;
-
-	  capacity_expr = elems.get_num_copies_expr ();
-	}
-	break;
-
-	case HIR::ArrayElems::ArrayExprType::VALUES: {
-	  HIR::ArrayElemsValues &elems
-	    = static_cast<HIR::ArrayElemsValues &> (elements);
-
-	  std::vector<TyTy::BaseType *> types;
-	  for (auto &elem : elems.get_values ())
-	    {
-	      types.push_back (TypeCheckExpr::Resolve (elem.get ()));
-	    }
-
-	  element_type = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ())
-			   .get_tyty ();
-	  for (auto &type : types)
-	    {
-	      element_type = element_type->unify (type);
-	    }
-
-	  auto crate_num = mappings->get_current_crate ();
-	  Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID,
-					 mappings->get_next_hir_id (crate_num),
-					 UNKNOWN_LOCAL_DEFID);
-	  std::string capacity_str = std::to_string (elems.get_num_elements ());
-	  capacity_expr
-	    = new HIR::LiteralExpr (mapping, capacity_str,
-				    HIR::Literal::LitType::INT,
-				    PrimitiveCoreType::CORETYPE_USIZE,
-				    Location (), {});
-
-	  // mark the type for this implicit node
-	  TyTy::BaseType *expected_ty = nullptr;
-	  bool ok = context->lookup_builtin ("usize", &expected_ty);
-	  rust_assert (ok);
-	  context->insert_type (mapping, expected_ty);
-	}
-	break;
-      }
-
-    infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (),
-				   expr.get_locus (), *capacity_expr,
-				   TyTy::TyVar (element_type->get_ref ()));
-  }
-
-  // empty struct
-  void visit (HIR::StructExprStruct &struct_expr) override
-  {
-    TyTy::BaseType *struct_path_ty
-      = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
-    if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
-      {
-	rust_error_at (struct_expr.get_struct_name ().get_locus (),
-		       "expected an ADT type for constructor");
-	return;
-      }
-
-    infered = struct_path_ty;
-  }
-
-  void visit (HIR::StructExprStructFields &struct_expr) override
-  {
-    infered = TypeCheckStructExpr::Resolve (&struct_expr);
-  }
-
-  void visit (HIR::GroupedExpr &expr) override
-  {
-    infered = TypeCheckExpr::Resolve (expr.get_expr_in_parens ().get ());
-  }
-
-  void visit (HIR::FieldAccessExpr &expr) override
-  {
-    auto struct_base
-      = TypeCheckExpr::Resolve (expr.get_receiver_expr ().get ());
-
-    // FIXME does this require autoderef here?
-    if (struct_base->get_kind () == TyTy::TypeKind::REF)
-      {
-	TyTy::ReferenceType *r
-	  = static_cast<TyTy::ReferenceType *> (struct_base);
-	struct_base = r->get_base ();
-      }
-
-    bool is_valid_type = struct_base->get_kind () == TyTy::TypeKind::ADT;
-    if (!is_valid_type)
-      {
-	rust_error_at (expr.get_locus (),
-		       "expected algebraic data type got: [%s]",
-		       struct_base->as_string ().c_str ());
-	return;
-      }
-
-    TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (struct_base);
-    rust_assert (!adt->is_enum ());
-    rust_assert (adt->number_of_variants () == 1);
-
-    TyTy::VariantDef *vaiant = adt->get_variants ().at (0);
-
-    TyTy::StructFieldType *lookup = nullptr;
-    bool found
-      = vaiant->lookup_field (expr.get_field_name (), &lookup, nullptr);
-    if (!found)
-      {
-	rust_error_at (expr.get_locus (), "unknown field [%s] for type [%s]",
-		       expr.get_field_name ().c_str (),
-		       adt->as_string ().c_str ());
-	return;
-      }
-
-    infered = lookup->get_field_type ();
-  }
-
+  void visit (HIR::ArrayExpr &expr) override;
+  void visit (HIR::StructExprStruct &struct_expr) override;
+  void visit (HIR::StructExprStructFields &struct_expr) override;
+  void visit (HIR::GroupedExpr &expr) override;
+  void visit (HIR::FieldAccessExpr &expr) override;
   void visit (HIR::QualifiedPathInExpression &expr) override;
-
   void visit (HIR::PathInExpression &expr) override;
-
-  void visit (HIR::LoopExpr &expr) override
-  {
-    context->push_new_loop_context (expr.get_mappings ().get_hirid (),
-				    expr.get_locus ());
-    TyTy::BaseType *block_expr
-      = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
-    if (!block_expr->is_unit ())
-      {
-	rust_error_at (expr.get_loop_block ()->get_locus (),
-		       "expected %<()%> got %s",
-		       block_expr->as_string ().c_str ());
-	return;
-      }
-
-    TyTy::BaseType *loop_context_type = context->pop_loop_context ();
-
-    bool loop_context_type_infered
-      = (loop_context_type->get_kind () != TyTy::TypeKind::INFER)
-	|| ((loop_context_type->get_kind () == TyTy::TypeKind::INFER)
-	    && (((TyTy::InferType *) loop_context_type)->get_infer_kind ()
-		!= TyTy::InferType::GENERAL));
-
-    infered
-      = loop_context_type_infered
-	  ? loop_context_type
-	  : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::WhileLoopExpr &expr) override
-  {
-    context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
-
-    TypeCheckExpr::Resolve (expr.get_predicate_expr ().get ());
-    TyTy::BaseType *block_expr
-      = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
-
-    if (!block_expr->is_unit ())
-      {
-	rust_error_at (expr.get_loop_block ()->get_locus (),
-		       "expected %<()%> got %s",
-		       block_expr->as_string ().c_str ());
-	return;
-      }
-
-    context->pop_loop_context ();
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::BreakExpr &expr) override
-  {
-    if (!context->have_loop_context ())
-      {
-	rust_error_at (expr.get_locus (), "cannot %<break%> outside of a loop");
-	return;
-      }
-
-    if (expr.has_break_expr ())
-      {
-	TyTy::BaseType *break_expr_tyty
-	  = TypeCheckExpr::Resolve (expr.get_expr ().get ());
-
-	TyTy::BaseType *loop_context = context->peek_loop_context ();
-	if (loop_context->get_kind () == TyTy::TypeKind::ERROR)
-	  {
-	    rust_error_at (expr.get_locus (),
-			   "can only break with a value inside %<loop%>");
-	    return;
-	  }
-
-	TyTy::BaseType *unified_ty = loop_context->unify (break_expr_tyty);
-	context->swap_head_loop_context (unified_ty);
-      }
-
-    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::ContinueExpr &expr) override
-  {
-    if (!context->have_loop_context ())
-      {
-	rust_error_at (expr.get_locus (),
-		       "cannot %<continue%> outside of a loop");
-	return;
-      }
-
-    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
-  }
-
-  void visit (HIR::BorrowExpr &expr) override
-  {
-    TyTy::BaseType *resolved_base
-      = TypeCheckExpr::Resolve (expr.get_expr ().get ());
-
-    // In Rust this is valid because of DST's
-    //
-    // fn test() {
-    //     let a:&str = "TEST 1";
-    //     let b:&str = &"TEST 2";
-    // }
-    if (resolved_base->get_kind () == TyTy::TypeKind::REF)
-      {
-	const TyTy::ReferenceType *ref
-	  = static_cast<const TyTy::ReferenceType *> (resolved_base);
-
-	// this might end up being a more generic is_dyn object check but lets
-	// double check dyn traits type-layout first
-	if (ref->is_dyn_str_type ())
-	  {
-	    infered = resolved_base;
-	    return;
-	  }
-      }
-
-    if (expr.get_is_double_borrow ())
-      {
-	// FIXME double_reference
-	gcc_unreachable ();
-      }
-
-    infered = new TyTy::ReferenceType (expr.get_mappings ().get_hirid (),
-				       TyTy::TyVar (resolved_base->get_ref ()),
-				       expr.get_mut ());
-  }
-
-  void visit (HIR::DereferenceExpr &expr) override
+  void visit (HIR::LoopExpr &expr) override;
+  void visit (HIR::BreakExpr &expr) override;
+  void visit (HIR::ContinueExpr &expr) override;
+  void visit (HIR::BorrowExpr &expr) override;
+  void visit (HIR::DereferenceExpr &expr) override;
+  void visit (HIR::TypeCastExpr &expr) override;
+  void visit (HIR::MatchExpr &expr) override;
+  void visit (HIR::RangeFromToExpr &expr) override;
+  void visit (HIR::RangeFromExpr &expr) override;
+  void visit (HIR::RangeToExpr &expr) override;
+  void visit (HIR::RangeFullExpr &expr) override;
+  void visit (HIR::RangeFromToInclExpr &expr) override;
+  void visit (HIR::WhileLoopExpr &expr) override;
+
+  // TODO
+  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::ClosureExprInner &expr) override {}
+  void visit (HIR::ErrorPropagationExpr &expr) override {}
+  void visit (HIR::RangeToInclExpr &expr) override {}
+  void visit (HIR::WhileLetLoopExpr &expr) override {}
+  void visit (HIR::ForLoopExpr &expr) override {}
+  void visit (HIR::IfExprConseqIfLet &expr) override {}
+  void visit (HIR::IfLetExprConseqElse &expr) override {}
+  void visit (HIR::IfLetExprConseqIf &expr) override {}
+  void visit (HIR::IfLetExprConseqIfLet &expr) override {}
+  void visit (HIR::AwaitExpr &expr) override {}
+  void visit (HIR::AsyncBlockExpr &expr) override {}
+
+  // don't need to implement these see rust-hir-type-check-struct-field.h
+  void visit (HIR::StructExprFieldIdentifier &field) override
   {
-    TyTy::BaseType *resolved_base
-      = TypeCheckExpr::Resolve (expr.get_expr ().get ());
-
-    auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
-    bool operator_overloaded
-      = resolve_operator_overload (lang_item_type, expr, resolved_base,
-				   nullptr);
-    if (operator_overloaded)
-      {
-	// operator overloaded deref always refurns a reference type lets assert
-	// this
-	rust_assert (infered->get_kind () == TyTy::TypeKind::REF);
-	resolved_base = infered;
-      }
-
-    bool is_valid_type
-      = resolved_base->get_kind () == TyTy::TypeKind::REF
-	|| resolved_base->get_kind () == TyTy::TypeKind::POINTER;
-    if (!is_valid_type)
-      {
-	rust_error_at (expr.get_locus (), "expected reference type got %s",
-		       resolved_base->as_string ().c_str ());
-	return;
-      }
-
-    if (resolved_base->get_kind () == TyTy::TypeKind::REF)
-      {
-	TyTy::ReferenceType *ref_base
-	  = static_cast<TyTy::ReferenceType *> (resolved_base);
-	infered = ref_base->get_base ()->clone ();
-      }
-    else
-      {
-	TyTy::PointerType *ref_base
-	  = static_cast<TyTy::PointerType *> (resolved_base);
-	infered = ref_base->get_base ()->clone ();
-      }
+    gcc_unreachable ();
   }
-
-  void visit (HIR::TypeCastExpr &expr) override
+  void visit (HIR::StructExprFieldIdentifierValue &field) override
   {
-    TyTy::BaseType *expr_to_convert
-      = TypeCheckExpr::Resolve (expr.get_casted_expr ().get ());
-    TyTy::BaseType *tyty_to_convert_to
-      = TypeCheckType::Resolve (expr.get_type_to_convert_to ().get ());
-
-    infered = expr_to_convert->cast (tyty_to_convert_to);
+    gcc_unreachable ();
   }
-
-  void visit (HIR::MatchExpr &expr) override
+  void visit (HIR::StructExprFieldIndexValue &field) override
   {
-    // this needs to perform a least upper bound coercion on the blocks and then
-    // unify the scruintee and arms
-    TyTy::BaseType *scrutinee_tyty
-      = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
-
-    std::vector<TyTy::BaseType *> kase_block_tys;
-    for (auto &kase : expr.get_match_cases ())
-      {
-	// lets check the arms
-	HIR::MatchArm &kase_arm = kase.get_arm ();
-	for (auto &pattern : kase_arm.get_patterns ())
-	  {
-	    TyTy::BaseType *kase_arm_ty
-	      = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
-
-	    TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
-	    if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
-	      return;
-	  }
-
-	// check the kase type
-	TyTy::BaseType *kase_block_ty
-	  = TypeCheckExpr::Resolve (kase.get_expr ().get ());
-	kase_block_tys.push_back (kase_block_ty);
-      }
-
-    if (kase_block_tys.size () == 0)
-      {
-	infered
-	  = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-	return;
-      }
-
-    infered = kase_block_tys.at (0);
-    for (size_t i = 1; i < kase_block_tys.size (); i++)
-      {
-	TyTy::BaseType *kase_ty = kase_block_tys.at (i);
-	infered = infered->unify (kase_ty);
-	if (infered->get_kind () == TyTy::TypeKind::ERROR)
-	  return;
-      }
+    gcc_unreachable ();
   }
 
-  void visit (HIR::RangeFromToExpr &expr) override;
-
-  void visit (HIR::RangeFromExpr &expr) override;
-
-  void visit (HIR::RangeToExpr &expr) override;
-
-  void visit (HIR::RangeFullExpr &expr) override;
-
-  void visit (HIR::RangeFromToInclExpr &expr) override;
-
 protected:
   bool
   resolve_operator_overload (Analysis::RustLangItem::ItemType lang_item_type,
@@ -909,7 +106,7 @@ protected:
 			     TyTy::BaseType *rhs);
 
 private:
-  TypeCheckExpr () : TypeCheckBase (), infered (nullptr) {}
+  TypeCheckExpr ();
 
   TyTy::BaseType *resolve_root_path (HIR::PathInExpression &expr,
 				     size_t *offset,
@@ -923,59 +120,7 @@ private:
 
   bool
   validate_arithmetic_type (const TyTy::BaseType *tyty,
-			    HIR::ArithmeticOrLogicalExpr::ExprType expr_type)
-  {
-    const TyTy::BaseType *type = tyty->destructure ();
-
-    // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators
-    // this will change later when traits are added
-    switch (expr_type)
-      {
-      case ArithmeticOrLogicalOperator::ADD:
-      case ArithmeticOrLogicalOperator::SUBTRACT:
-      case ArithmeticOrLogicalOperator::MULTIPLY:
-      case ArithmeticOrLogicalOperator::DIVIDE:
-      case ArithmeticOrLogicalOperator::MODULUS:
-	return (type->get_kind () == TyTy::TypeKind::INT)
-	       || (type->get_kind () == TyTy::TypeKind::UINT)
-	       || (type->get_kind () == TyTy::TypeKind::FLOAT)
-	       || (type->get_kind () == TyTy::TypeKind::USIZE)
-	       || (type->get_kind () == TyTy::TypeKind::ISIZE)
-	       || (type->get_kind () == TyTy::TypeKind::INFER
-		   && (((const TyTy::InferType *) type)->get_infer_kind ()
-		       == TyTy::InferType::INTEGRAL))
-	       || (type->get_kind () == TyTy::TypeKind::INFER
-		   && (((const TyTy::InferType *) type)->get_infer_kind ()
-		       == TyTy::InferType::FLOAT));
-
-	// integers or bools
-      case ArithmeticOrLogicalOperator::BITWISE_AND:
-      case ArithmeticOrLogicalOperator::BITWISE_OR:
-      case ArithmeticOrLogicalOperator::BITWISE_XOR:
-	return (type->get_kind () == TyTy::TypeKind::INT)
-	       || (type->get_kind () == TyTy::TypeKind::UINT)
-	       || (type->get_kind () == TyTy::TypeKind::USIZE)
-	       || (type->get_kind () == TyTy::TypeKind::ISIZE)
-	       || (type->get_kind () == TyTy::TypeKind::BOOL)
-	       || (type->get_kind () == TyTy::TypeKind::INFER
-		   && (((const TyTy::InferType *) type)->get_infer_kind ()
-		       == TyTy::InferType::INTEGRAL));
-
-	// integers only
-      case ArithmeticOrLogicalOperator::LEFT_SHIFT:
-      case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
-	return (type->get_kind () == TyTy::TypeKind::INT)
-	       || (type->get_kind () == TyTy::TypeKind::UINT)
-	       || (type->get_kind () == TyTy::TypeKind::USIZE)
-	       || (type->get_kind () == TyTy::TypeKind::ISIZE)
-	       || (type->get_kind () == TyTy::TypeKind::INFER
-		   && (((const TyTy::InferType *) type)->get_infer_kind ()
-		       == TyTy::InferType::INTEGRAL));
-      }
-
-    gcc_unreachable ();
-    return false;
-  }
+			    HIR::ArithmeticOrLogicalExpr::ExprType expr_type);
 
   /* The return value of TypeCheckExpr::Resolve */
   TyTy::BaseType *infered;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.cc b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
new file mode 100644
index 00000000000..784e4990409
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
@@ -0,0 +1,583 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckTopLevelExternItem::TypeCheckTopLevelExternItem (
+  const HIR::ExternBlock &parent)
+  : TypeCheckBase (), parent (parent)
+{}
+
+void
+TypeCheckTopLevelExternItem::Resolve (HIR::ExternalItem *item,
+				      const HIR::ExternBlock &parent)
+{
+  TypeCheckTopLevelExternItem resolver (parent);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevelExternItem::visit (HIR::ExternalStaticItem &item)
+{
+  TyTy::BaseType *actual_type
+    = TypeCheckType::Resolve (item.get_item_type ().get ());
+
+  context->insert_type (item.get_mappings (), actual_type);
+}
+
+void
+TypeCheckTopLevelExternItem::visit (HIR::ExternalFunctionItem &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ().get ());
+
+      // these are implicit mappings and not used
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      HIR::IdentifierPattern *param_pattern
+	= new HIR::IdentifierPattern (mapping, param.get_param_name (),
+				      Location (), false, Mutability::Imm,
+				      std::unique_ptr<HIR::Pattern> (nullptr));
+
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param_pattern,
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+
+      // FIXME do we need error checking for patterns here?
+      // see https://github.com/Rust-GCC/gccrs/issues/995
+    }
+
+  uint8_t flags = TyTy::FnType::FNTYPE_IS_EXTERN_FLAG;
+  if (function.is_variadic ())
+    flags |= TyTy::FnType::FNTYPE_IS_VARADIC_FLAG;
+
+  RustIdent ident{
+    CanonicalPath::new_seg (function.get_mappings ().get_nodeid (),
+			    function.get_item_name ()),
+    function.get_locus ()};
+
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_item_name (), ident, flags,
+				  parent.get_abi (), std::move (params),
+				  ret_type, std::move (substitutions));
+
+  context->insert_type (function.get_mappings (), fnType);
+}
+
+TypeCheckTopLevelImplItem::TypeCheckTopLevelImplItem (
+  TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  : TypeCheckBase (), self (self), substitutions (substitutions)
+{}
+
+void
+TypeCheckTopLevelImplItem::Resolve (
+  HIR::ImplItem *item, TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+{
+  TypeCheckTopLevelImplItem resolver (self, substitutions);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::TypeAlias &alias)
+{
+  TyTy::BaseType *actual_type
+    = TypeCheckType::Resolve (alias.get_type_aliased ().get ());
+
+  context->insert_type (alias.get_mappings (), actual_type);
+
+  for (auto &where_clause_item : alias.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::ConstantItem &constant)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
+
+  context->insert_type (constant.get_mappings (), type->unify (expr_type));
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::Function &function)
+{
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  for (auto &where_clause_item : function.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_function_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  if (function.is_method ())
+    {
+      // these are implicit mappings and not used
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      // add the synthetic self param at the front, this is a placeholder for
+      // compilation to know parameter names. The types are ignored but we
+      // reuse the HIR identifier pattern which requires it
+      HIR::SelfParam &self_param = function.get_self_param ();
+      HIR::IdentifierPattern *self_pattern
+	= new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (),
+				      self_param.is_ref (),
+				      self_param.get_mut (),
+				      std::unique_ptr<HIR::Pattern> (nullptr));
+
+      // might have a specified type
+      TyTy::BaseType *self_type = nullptr;
+      if (self_param.has_type ())
+	{
+	  std::unique_ptr<HIR::Type> &specified_type = self_param.get_type ();
+	  self_type = TypeCheckType::Resolve (specified_type.get ());
+	}
+      else
+	{
+	  switch (self_param.get_self_kind ())
+	    {
+	    case HIR::SelfParam::IMM:
+	    case HIR::SelfParam::MUT:
+	      self_type = self->clone ();
+	      break;
+
+	    case HIR::SelfParam::IMM_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Imm);
+	      break;
+
+	    case HIR::SelfParam::MUT_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Mut);
+	      break;
+
+	    default:
+	      gcc_unreachable ();
+	      return;
+	    }
+	}
+
+      context->insert_type (self_param.get_mappings (), self_type);
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (self_pattern, self_type));
+    }
+
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, function.get_locus ()};
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_function_name (), ident,
+				  function.is_method ()
+				    ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG
+				    : TyTy::FnType::FNTYPE_DEFAULT_FLAGS,
+				  ABI::RUST, std::move (params), ret_type,
+				  std::move (substitutions));
+
+  context->insert_type (function.get_mappings (), fnType);
+}
+
+TypeCheckImplItem::TypeCheckImplItem (HIR::ImplBlock *parent,
+				      TyTy::BaseType *self)
+  : TypeCheckBase (), parent (parent), self (self)
+{}
+
+void
+TypeCheckImplItem::Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item,
+			    TyTy::BaseType *self)
+{
+  TypeCheckImplItem resolver (parent, self);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckImplItem::visit (HIR::Function &function)
+{
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
+    {
+      rust_error_at (function.get_locus (), "failed to lookup function type");
+      return;
+    }
+
+  if (lookup->get_kind () != TyTy::TypeKind::FNDEF)
+    {
+      rust_error_at (function.get_locus (),
+		     "found invalid type for function [%s]",
+		     lookup->as_string ().c_str ());
+      return;
+    }
+
+  // need to get the return type from this
+  TyTy::FnType *resolve_fn_type = static_cast<TyTy::FnType *> (lookup);
+  auto expected_ret_tyty = resolve_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (parent, &function),
+			     expected_ret_tyty);
+
+  auto block_expr_ty
+    = TypeCheckExpr::Resolve (function.get_definition ().get ());
+
+  context->pop_return_type ();
+  expected_ret_tyty->unify (block_expr_ty);
+}
+
+void
+TypeCheckImplItem::visit (HIR::ConstantItem &const_item)
+{}
+
+void
+TypeCheckImplItem::visit (HIR::TypeAlias &type_alias)
+{}
+
+TypeCheckImplItemWithTrait::TypeCheckImplItemWithTrait (
+  HIR::ImplBlock *parent, TyTy::BaseType *self,
+  TyTy::TypeBoundPredicate &trait_reference,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  : TypeCheckImplItem (parent, self), trait_reference (trait_reference),
+    resolved_trait_item (TyTy::TypeBoundPredicateItem::error ()),
+    substitutions (substitutions)
+{
+  rust_assert (is_trait_impl_block ());
+}
+
+TyTy::TypeBoundPredicateItem
+TypeCheckImplItemWithTrait::Resolve (
+  HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self,
+  TyTy::TypeBoundPredicate &trait_reference,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+{
+  TypeCheckImplItemWithTrait resolver (parent, self, trait_reference,
+				       substitutions);
+  item->accept_vis (resolver);
+  return resolver.resolved_trait_item;
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::ConstantItem &constant)
+{
+  // normal resolution of the item
+  TypeCheckImplItem::visit (constant);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (constant.get_identifier (),
+				       TraitItemReference::TraitItemType::CONST,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (constant.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "constant %<%s%> is not a member of trait %<%s%>",
+		     constant.get_identifier ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (constant.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (constant.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (
+	r, "constant %<%s%> has an incompatible type for trait %<%s%>",
+	constant.get_identifier ().c_str (),
+	trait_reference.get_name ().c_str ());
+    }
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::TypeAlias &type)
+{
+  // normal resolution of the item
+  TypeCheckImplItem::visit (type);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (type.get_new_type_name (),
+				       TraitItemReference::TraitItemType::TYPE,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (type.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "type alias %<%s%> is not a member of trait %<%s%>",
+		     type.get_new_type_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (type.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (type.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (
+	r, "type alias %<%s%> has an incompatible type for trait %<%s%>",
+	type.get_new_type_name ().c_str (),
+	trait_reference.get_name ().c_str ());
+    }
+
+  // its actually a projection, since we need a way to actually bind the
+  // generic substitutions to the type itself
+  TyTy::ProjectionType *projection
+    = new TyTy::ProjectionType (type.get_mappings ().get_hirid (), lookup, tref,
+				raw_trait_item->get_mappings ().get_defid (),
+				substitutions);
+
+  context->insert_type (type.get_mappings (), projection);
+  raw_trait_item->associated_type_set (projection);
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::Function &function)
+{
+  // we get the error checking from the base method here
+  TypeCheckImplItem::visit (function);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (function.get_function_name (),
+				       TraitItemReference::TraitItemType::FN,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (function.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>",
+		     function.get_function_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (function.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (function.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (r,
+		     "method %<%s%> has an incompatible type for trait %<%s%>",
+		     function.get_function_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+    }
+}
+
+void
+TypeCheckImplItemWithTrait::merge_attributes (AST::AttrVec &impl_item_attrs,
+					      const HIR::TraitItem &trait_item)
+{
+  for (const auto &attr : trait_item.get_outer_attrs ())
+    {
+      impl_item_attrs.push_back (attr);
+    }
+}
+
+bool
+TypeCheckImplItemWithTrait::is_trait_impl_block () const
+{
+  return !trait_reference.is_error ();
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
index 294dffdead2..f2f3faab9e0 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-implitem.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
@@ -20,368 +20,58 @@
 #define RUST_HIR_TYPE_CHECK_IMPLITEM_H
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-hir-type-check-type.h"
-#include "rust-hir-type-check-expr.h"
-#include "rust-tyty.h"
 
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckTopLevelExternItem : public TypeCheckBase
+class TypeCheckTopLevelExternItem : public TypeCheckBase,
+				    public HIR::HIRExternalItemVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static void Resolve (HIR::ExternalItem *item, const HIR::ExternBlock &parent)
-  {
-    TypeCheckTopLevelExternItem resolver (parent);
-    item->accept_vis (resolver);
-  }
-
-  void visit (HIR::ExternalStaticItem &item) override
-  {
-    TyTy::BaseType *actual_type
-      = TypeCheckType::Resolve (item.get_item_type ().get ());
-
-    context->insert_type (item.get_mappings (), actual_type);
-  }
-
-  void visit (HIR::ExternalFunctionItem &function) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (function.has_generics ())
-      {
-	for (auto &generic_param : function.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    TyTy::BaseType *ret_type = nullptr;
-    if (!function.has_return_type ())
-      ret_type = TyTy::TupleType::get_unit_type (
-	function.get_mappings ().get_hirid ());
-    else
-      {
-	auto resolved
-	  = TypeCheckType::Resolve (function.get_return_type ().get ());
-	if (resolved == nullptr)
-	  {
-	    rust_error_at (function.get_locus (),
-			   "failed to resolve return type");
-	    return;
-	  }
-
-	ret_type = resolved->clone ();
-	ret_type->set_ref (
-	  function.get_return_type ()->get_mappings ().get_hirid ());
-      }
-
-    std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
-    for (auto &param : function.get_function_params ())
-      {
-	// get the name as well required for later on
-	auto param_tyty = TypeCheckType::Resolve (param.get_type ().get ());
-
-	// these are implicit mappings and not used
-	auto crate_num = mappings->get_current_crate ();
-	Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
-				       mappings->get_next_hir_id (crate_num),
-				       UNKNOWN_LOCAL_DEFID);
-
-	HIR::IdentifierPattern *param_pattern = new HIR::IdentifierPattern (
-	  mapping, param.get_param_name (), Location (), false, Mutability::Imm,
-	  std::unique_ptr<HIR::Pattern> (nullptr));
-
-	params.push_back (
-	  std::pair<HIR::Pattern *, TyTy::BaseType *> (param_pattern,
-						       param_tyty));
-
-	context->insert_type (param.get_mappings (), param_tyty);
-
-	// FIXME do we need error checking for patterns here?
-	// see https://github.com/Rust-GCC/gccrs/issues/995
-      }
-
-    uint8_t flags = TyTy::FnType::FNTYPE_IS_EXTERN_FLAG;
-    if (function.is_variadic ())
-      flags |= TyTy::FnType::FNTYPE_IS_VARADIC_FLAG;
-
-    RustIdent ident{
-      CanonicalPath::new_seg (function.get_mappings ().get_nodeid (),
-			      function.get_item_name ()),
-      function.get_locus ()};
-
-    auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
-				    function.get_mappings ().get_defid (),
-				    function.get_item_name (), ident, flags,
-				    parent.get_abi (), std::move (params),
-				    ret_type, std::move (substitutions));
+  static void Resolve (HIR::ExternalItem *item, const HIR::ExternBlock &parent);
 
-    context->insert_type (function.get_mappings (), fnType);
-  }
+  void visit (HIR::ExternalStaticItem &item) override;
+  void visit (HIR::ExternalFunctionItem &function) override;
 
 private:
-  TypeCheckTopLevelExternItem (const HIR::ExternBlock &parent)
-    : TypeCheckBase (), parent (parent)
-  {}
+  TypeCheckTopLevelExternItem (const HIR::ExternBlock &parent);
 
   const HIR::ExternBlock &parent;
 };
 
-class TypeCheckTopLevelImplItem : public TypeCheckBase
+class TypeCheckTopLevelImplItem : public TypeCheckBase,
+				  public HIR::HIRImplVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static void
   Resolve (HIR::ImplItem *item, TyTy::BaseType *self,
-	   std::vector<TyTy::SubstitutionParamMapping> substitutions)
-  {
-    TypeCheckTopLevelImplItem resolver (self, substitutions);
-    item->accept_vis (resolver);
-  }
-
-  void visit (HIR::TypeAlias &alias) override
-  {
-    TyTy::BaseType *actual_type
-      = TypeCheckType::Resolve (alias.get_type_aliased ().get ());
-
-    context->insert_type (alias.get_mappings (), actual_type);
-
-    for (auto &where_clause_item : alias.get_where_clause ().get_items ())
-      {
-	ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
-      }
-  }
-
-  void visit (HIR::ConstantItem &constant) override
-  {
-    TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
-    TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
-
-    context->insert_type (constant.get_mappings (), type->unify (expr_type));
-  }
-
-  void visit (HIR::Function &function) override
-  {
-    if (function.has_generics ())
-      {
-	for (auto &generic_param : function.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    for (auto &where_clause_item : function.get_where_clause ().get_items ())
-      {
-	ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
-      }
-
-    TyTy::BaseType *ret_type = nullptr;
-    if (!function.has_function_return_type ())
-      ret_type = TyTy::TupleType::get_unit_type (
-	function.get_mappings ().get_hirid ());
-    else
-      {
-	auto resolved
-	  = TypeCheckType::Resolve (function.get_return_type ().get ());
-	if (resolved == nullptr)
-	  {
-	    rust_error_at (function.get_locus (),
-			   "failed to resolve return type");
-	    return;
-	  }
-
-	ret_type = resolved->clone ();
-	ret_type->set_ref (
-	  function.get_return_type ()->get_mappings ().get_hirid ());
-      }
-
-    std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
-    if (function.is_method ())
-      {
-	// these are implicit mappings and not used
-	auto crate_num = mappings->get_current_crate ();
-	Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
-				       mappings->get_next_hir_id (crate_num),
-				       UNKNOWN_LOCAL_DEFID);
-
-	// add the synthetic self param at the front, this is a placeholder for
-	// compilation to know parameter names. The types are ignored but we
-	// reuse the HIR identifier pattern which requires it
-	HIR::SelfParam &self_param = function.get_self_param ();
-	HIR::IdentifierPattern *self_pattern = new HIR::IdentifierPattern (
-	  mapping, "self", self_param.get_locus (), self_param.is_ref (),
-	  self_param.get_mut (), std::unique_ptr<HIR::Pattern> (nullptr));
-
-	// might have a specified type
-	TyTy::BaseType *self_type = nullptr;
-	if (self_param.has_type ())
-	  {
-	    std::unique_ptr<HIR::Type> &specified_type = self_param.get_type ();
-	    self_type = TypeCheckType::Resolve (specified_type.get ());
-	  }
-	else
-	  {
-	    switch (self_param.get_self_kind ())
-	      {
-	      case HIR::SelfParam::IMM:
-	      case HIR::SelfParam::MUT:
-		self_type = self->clone ();
-		break;
-
-	      case HIR::SelfParam::IMM_REF:
-		self_type = new TyTy::ReferenceType (
-		  self_param.get_mappings ().get_hirid (),
-		  TyTy::TyVar (self->get_ref ()), Mutability::Imm);
-		break;
-
-	      case HIR::SelfParam::MUT_REF:
-		self_type = new TyTy::ReferenceType (
-		  self_param.get_mappings ().get_hirid (),
-		  TyTy::TyVar (self->get_ref ()), Mutability::Mut);
-		break;
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
 
-	      default:
-		gcc_unreachable ();
-		return;
-	      }
-	  }
-
-	context->insert_type (self_param.get_mappings (), self_type);
-	params.push_back (
-	  std::pair<HIR::Pattern *, TyTy::BaseType *> (self_pattern,
-						       self_type));
-      }
-
-    for (auto &param : function.get_function_params ())
-      {
-	// get the name as well required for later on
-	auto param_tyty = TypeCheckType::Resolve (param.get_type ());
-	params.push_back (
-	  std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
-						       param_tyty));
-
-	context->insert_type (param.get_mappings (), param_tyty);
-	TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
-      }
-
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok
-      = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
-					 &canonical_path);
-    rust_assert (ok);
-
-    RustIdent ident{*canonical_path, function.get_locus ()};
-    auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
-				    function.get_mappings ().get_defid (),
-				    function.get_function_name (), ident,
-				    function.is_method ()
-				      ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG
-				      : TyTy::FnType::FNTYPE_DEFAULT_FLAGS,
-				    ABI::RUST, std::move (params), ret_type,
-				    std::move (substitutions));
-
-    context->insert_type (function.get_mappings (), fnType);
-  }
+  void visit (HIR::TypeAlias &alias) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::Function &function) override;
 
 private:
   TypeCheckTopLevelImplItem (
     TyTy::BaseType *self,
-    std::vector<TyTy::SubstitutionParamMapping> substitutions)
-    : TypeCheckBase (), self (self), substitutions (substitutions)
-  {}
+    std::vector<TyTy::SubstitutionParamMapping> substitutions);
 
   TyTy::BaseType *self;
   std::vector<TyTy::SubstitutionParamMapping> substitutions;
 };
 
-class TypeCheckImplItem : public TypeCheckBase
+class TypeCheckImplItem : public TypeCheckBase, public HIR::HIRImplVisitor
 {
 public:
-  using Rust::Resolver::TypeCheckBase::visit;
-
   static void Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item,
-		       TyTy::BaseType *self)
-  {
-    TypeCheckImplItem resolver (parent, self);
-    item->accept_vis (resolver);
-  }
-
-  void visit (HIR::Function &function) override
-  {
-    TyTy::BaseType *lookup;
-    if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
-      {
-	rust_error_at (function.get_locus (), "failed to lookup function type");
-	return;
-      }
-
-    if (lookup->get_kind () != TyTy::TypeKind::FNDEF)
-      {
-	rust_error_at (function.get_locus (),
-		       "found invalid type for function [%s]",
-		       lookup->as_string ().c_str ());
-	return;
-      }
+		       TyTy::BaseType *self);
 
-    // need to get the return type from this
-    TyTy::FnType *resolve_fn_type = static_cast<TyTy::FnType *> (lookup);
-    auto expected_ret_tyty = resolve_fn_type->get_return_type ();
-    context->push_return_type (TypeCheckContextItem (parent, &function),
-			       expected_ret_tyty);
-
-    auto block_expr_ty
-      = TypeCheckExpr::Resolve (function.get_definition ().get ());
-
-    context->pop_return_type ();
-    expected_ret_tyty->unify (block_expr_ty);
-  }
+  void visit (HIR::Function &function) override;
+  void visit (HIR::ConstantItem &const_item) override;
+  void visit (HIR::TypeAlias &type_alias) override;
 
 protected:
-  TypeCheckImplItem (HIR::ImplBlock *parent, TyTy::BaseType *self)
-    : TypeCheckBase (), parent (parent), self (self)
-  {}
+  TypeCheckImplItem (HIR::ImplBlock *parent, TyTy::BaseType *self);
 
   HIR::ImplBlock *parent;
   TyTy::BaseType *self;
@@ -389,207 +79,29 @@ protected:
 
 class TypeCheckImplItemWithTrait : public TypeCheckImplItem
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static TyTy::TypeBoundPredicateItem
   Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self,
 	   TyTy::TypeBoundPredicate &trait_reference,
-	   std::vector<TyTy::SubstitutionParamMapping> substitutions)
-  {
-    TypeCheckImplItemWithTrait resolver (parent, self, trait_reference,
-					 substitutions);
-    item->accept_vis (resolver);
-    return resolver.resolved_trait_item;
-  }
-
-  void visit (HIR::ConstantItem &constant) override
-  {
-    // normal resolution of the item
-    TypeCheckImplItem::visit (constant);
-    TyTy::BaseType *lookup;
-    if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup))
-      return;
-
-    // map the impl item to the associated trait item
-    const auto tref = trait_reference.get ();
-    const TraitItemReference *raw_trait_item = nullptr;
-    bool found = tref->lookup_trait_item_by_type (
-      constant.get_identifier (), TraitItemReference::TraitItemType::CONST,
-      &raw_trait_item);
-
-    // unknown trait item
-    if (!found || raw_trait_item->is_error ())
-      {
-	RichLocation r (constant.get_locus ());
-	r.add_range (trait_reference.get_locus ());
-	rust_error_at (r, "constant %<%s%> is not a member of trait %<%s%>",
-		       constant.get_identifier ().c_str (),
-		       trait_reference.get_name ().c_str ());
-	return;
-      }
-
-    // get the item from the predicate
-    resolved_trait_item
-      = trait_reference.lookup_associated_item (raw_trait_item);
-    rust_assert (!resolved_trait_item.is_error ());
-
-    // merge the attributes
-    const HIR::TraitItem *hir_trait_item
-      = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
-    merge_attributes (constant.get_outer_attrs (), *hir_trait_item);
-
-    // check the types are compatible
-    auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
-    if (!trait_item_type->can_eq (lookup, true))
-      {
-	RichLocation r (constant.get_locus ());
-	r.add_range (resolved_trait_item.get_locus ());
-
-	rust_error_at (
-	  r, "constant %<%s%> has an incompatible type for trait %<%s%>",
-	  constant.get_identifier ().c_str (),
-	  trait_reference.get_name ().c_str ());
-      }
-  }
-
-  void visit (HIR::TypeAlias &type) override
-  {
-    // normal resolution of the item
-    TypeCheckImplItem::visit (type);
-    TyTy::BaseType *lookup;
-    if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup))
-      return;
-
-    // map the impl item to the associated trait item
-    const auto tref = trait_reference.get ();
-    const TraitItemReference *raw_trait_item = nullptr;
-    bool found = tref->lookup_trait_item_by_type (
-      type.get_new_type_name (), TraitItemReference::TraitItemType::TYPE,
-      &raw_trait_item);
-
-    // unknown trait item
-    if (!found || raw_trait_item->is_error ())
-      {
-	RichLocation r (type.get_locus ());
-	r.add_range (trait_reference.get_locus ());
-	rust_error_at (r, "type alias %<%s%> is not a member of trait %<%s%>",
-		       type.get_new_type_name ().c_str (),
-		       trait_reference.get_name ().c_str ());
-	return;
-      }
-
-    // get the item from the predicate
-    resolved_trait_item
-      = trait_reference.lookup_associated_item (raw_trait_item);
-    rust_assert (!resolved_trait_item.is_error ());
-
-    // merge the attributes
-    const HIR::TraitItem *hir_trait_item
-      = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
-    merge_attributes (type.get_outer_attrs (), *hir_trait_item);
-
-    // check the types are compatible
-    auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
-    if (!trait_item_type->can_eq (lookup, true))
-      {
-	RichLocation r (type.get_locus ());
-	r.add_range (resolved_trait_item.get_locus ());
-
-	rust_error_at (
-	  r, "type alias %<%s%> has an incompatible type for trait %<%s%>",
-	  type.get_new_type_name ().c_str (),
-	  trait_reference.get_name ().c_str ());
-      }
-
-    // its actually a projection, since we need a way to actually bind the
-    // generic substitutions to the type itself
-    TyTy::ProjectionType *projection
-      = new TyTy::ProjectionType (type.get_mappings ().get_hirid (), lookup,
-				  tref,
-				  raw_trait_item->get_mappings ().get_defid (),
-				  substitutions);
-
-    context->insert_type (type.get_mappings (), projection);
-    raw_trait_item->associated_type_set (projection);
-  }
-
-  void visit (HIR::Function &function) override
-  {
-    // we get the error checking from the base method here
-    TypeCheckImplItem::visit (function);
-    TyTy::BaseType *lookup;
-    if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
-      return;
-
-    // map the impl item to the associated trait item
-    const auto tref = trait_reference.get ();
-    const TraitItemReference *raw_trait_item = nullptr;
-    bool found
-      = tref->lookup_trait_item_by_type (function.get_function_name (),
-					 TraitItemReference::TraitItemType::FN,
-					 &raw_trait_item);
-
-    // unknown trait item
-    if (!found || raw_trait_item->is_error ())
-      {
-	RichLocation r (function.get_locus ());
-	r.add_range (trait_reference.get_locus ());
-	rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>",
-		       function.get_function_name ().c_str (),
-		       trait_reference.get_name ().c_str ());
-	return;
-      }
-
-    // get the item from the predicate
-    resolved_trait_item
-      = trait_reference.lookup_associated_item (raw_trait_item);
-    rust_assert (!resolved_trait_item.is_error ());
-
-    // merge the attributes
-    const HIR::TraitItem *hir_trait_item
-      = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
-    merge_attributes (function.get_outer_attrs (), *hir_trait_item);
-
-    // check the types are compatible
-    auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
-    if (!trait_item_type->can_eq (lookup, true))
-      {
-	RichLocation r (function.get_locus ());
-	r.add_range (resolved_trait_item.get_locus ());
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
 
-	rust_error_at (
-	  r, "method %<%s%> has an incompatible type for trait %<%s%>",
-	  function.get_function_name ().c_str (),
-	  trait_reference.get_name ().c_str ());
-      }
-  }
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::TypeAlias &type) override;
+  void visit (HIR::Function &function) override;
 
 protected:
   // this allows us to inherit the must_use specified on a trait definition onto
   // its implementation
   void merge_attributes (AST::AttrVec &impl_item_attrs,
-			 const HIR::TraitItem &trait_item)
-  {
-    for (const auto &attr : trait_item.get_outer_attrs ())
-      {
-	impl_item_attrs.push_back (attr);
-      }
-  }
+			 const HIR::TraitItem &trait_item);
 
 private:
   TypeCheckImplItemWithTrait (
     HIR::ImplBlock *parent, TyTy::BaseType *self,
     TyTy::TypeBoundPredicate &trait_reference,
-    std::vector<TyTy::SubstitutionParamMapping> substitutions)
-    : TypeCheckImplItem (parent, self), trait_reference (trait_reference),
-      resolved_trait_item (TyTy::TypeBoundPredicateItem::error ()),
-      substitutions (substitutions)
-  {
-    rust_assert (is_trait_impl_block ());
-  }
+    std::vector<TyTy::SubstitutionParamMapping> substitutions);
 
-  bool is_trait_impl_block () const { return !trait_reference.is_error (); }
+  bool is_trait_impl_block () const;
 
   TyTy::TypeBoundPredicate &trait_reference;
   TyTy::TypeBoundPredicateItem resolved_trait_item;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc
index 28cac51f793..d31a6df4777 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -17,15 +17,26 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-hir-type-check-item.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-stmt.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-trait-resolve.h"
 
 namespace Rust {
-
 namespace Resolver {
+
+TypeCheckItem::TypeCheckItem () : TypeCheckBase () {}
+
 void
-TypeCheckItem::Resolve (HIR::Item *item)
+TypeCheckItem::Resolve (HIR::Item &item)
 {
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
   TypeCheckItem resolver;
-  item->accept_vis (resolver);
+  vis_item.accept_vis (resolver);
 }
 
 void
@@ -213,7 +224,7 @@ void
 TypeCheckItem::visit (HIR::Module &module)
 {
   for (auto &item : module.get_items ())
-    TypeCheckItem::Resolve (item.get ());
+    TypeCheckItem::Resolve (*item.get ());
 }
 
 void
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h
index b9a0d9b2e90..ba4de19c9c7 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.h
@@ -20,30 +20,36 @@
 #define RUST_HIR_TYPE_CHECK_ITEM
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-hir-type-check-implitem.h"
-#include "rust-hir-type-check-type.h"
-#include "rust-hir-type-check-stmt.h"
-#include "rust-hir-trait-resolve.h"
-#include "rust-tyty-visitor.h"
 
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckItem : public TypeCheckBase
+class TypeCheckItem : private TypeCheckBase, private HIR::HIRVisItemVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static void Resolve (HIR::Item *item);
+  static void Resolve (HIR::Item &item);
 
   void visit (HIR::ImplBlock &impl_block) override;
   void visit (HIR::Function &function) override;
   void visit (HIR::Module &module) override;
   void visit (HIR::Trait &trait) override;
 
+  // FIXME - get rid of toplevel pass
+  void visit (HIR::TypeAlias &alias) override{};
+  void visit (HIR::TupleStruct &struct_decl) override{};
+  void visit (HIR::StructStruct &struct_decl) override{};
+  void visit (HIR::Enum &enum_decl) override{};
+  void visit (HIR::Union &union_decl) override{};
+  void visit (HIR::StaticItem &var) override{};
+  void visit (HIR::ConstantItem &constant) override{};
+  void visit (HIR::ExternBlock &extern_block) override{};
+
+  // nothing to do
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+
 private:
-  TypeCheckItem () : TypeCheckBase () {}
+  TypeCheckItem ();
 };
 
 } // namespace Resolver
diff --git a/gcc/rust/typecheck/rust-hir-type-check-path.cc b/gcc/rust/typecheck/rust-hir-type-check-path.cc
index ac1ebfd6c95..fd0ca3ea16b 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-path.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-path.cc
@@ -17,6 +17,8 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-resolve.h"
 
 namespace Rust {
 namespace Resolver {
diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
index df312af6dab..a62fbc96a08 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
@@ -22,6 +22,24 @@
 namespace Rust {
 namespace Resolver {
 
+TypeCheckPattern::TypeCheckPattern (TyTy::BaseType *parent)
+  : TypeCheckBase (), parent (parent), infered (nullptr)
+{}
+
+TyTy::BaseType *
+TypeCheckPattern::Resolve (HIR::Pattern *pattern, TyTy::BaseType *parent)
+{
+  TypeCheckPattern resolver (parent);
+  pattern->accept_vis (resolver);
+
+  if (resolver.infered == nullptr)
+    return new TyTy::ErrorType (pattern->get_pattern_mappings ().get_hirid ());
+
+  resolver.context->insert_type (pattern->get_pattern_mappings (),
+				 resolver.infered);
+  return resolver.infered;
+}
+
 void
 TypeCheckPattern::visit (HIR::PathInExpression &pattern)
 {
@@ -357,5 +375,33 @@ TypeCheckPattern::visit (HIR::IdentifierPattern &pattern)
   infered = parent;
 }
 
+void
+TypeCheckPattern::visit (HIR::GroupedPattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::QualifiedPathInExpression &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::ReferencePattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::SlicePattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
 } // namespace Resolver
 } // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.h b/gcc/rust/typecheck/rust-hir-type-check-pattern.h
index 860aca9207f..8af106033b7 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-pattern.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.h
@@ -25,45 +25,27 @@
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckPattern : public TypeCheckBase
+class TypeCheckPattern : public TypeCheckBase, public HIR::HIRPatternVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static TyTy::BaseType *Resolve (HIR::Pattern *pattern, TyTy::BaseType *parent)
-  {
-    TypeCheckPattern resolver (parent);
-    pattern->accept_vis (resolver);
-
-    if (resolver.infered == nullptr)
-      return new TyTy::ErrorType (
-	pattern->get_pattern_mappings ().get_hirid ());
-
-    resolver.context->insert_type (pattern->get_pattern_mappings (),
-				   resolver.infered);
-    return resolver.infered;
-  }
+  static TyTy::BaseType *Resolve (HIR::Pattern *pattern,
+				  TyTy::BaseType *parent);
 
   void visit (HIR::PathInExpression &pattern) override;
-
   void visit (HIR::StructPattern &pattern) override;
-
   void visit (HIR::TupleStructPattern &pattern) override;
-
   void visit (HIR::WildcardPattern &pattern) override;
-
   void visit (HIR::TuplePattern &pattern) override;
-
   void visit (HIR::LiteralPattern &pattern) override;
-
   void visit (HIR::RangePattern &pattern) override;
-
   void visit (HIR::IdentifierPattern &pattern) override;
+  void visit (HIR::GroupedPattern &pattern) override;
+  void visit (HIR::QualifiedPathInExpression &pattern) override;
+  void visit (HIR::ReferencePattern &pattern) override;
+  void visit (HIR::SlicePattern &pattern) override;
 
 private:
-  TypeCheckPattern (TyTy::BaseType *parent)
-    : TypeCheckBase (), parent (parent), infered (nullptr)
-  {}
+  TypeCheckPattern (TyTy::BaseType *parent);
 
   static TyTy::BaseType *
   typecheck_range_pattern_bound (HIR::RangePatternBound *bound,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.cc b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc
new file mode 100644
index 00000000000..9f34ed49165
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc
@@ -0,0 +1,498 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-hir-type-check-stmt.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-enumitem.h"
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-pattern.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::BaseType *
+TypeCheckStmt::Resolve (HIR::Stmt *stmt)
+{
+  TypeCheckStmt resolver;
+  stmt->accept_vis (resolver);
+  return resolver.infered;
+}
+
+void
+TypeCheckStmt::visit (HIR::ExprStmtWithBlock &stmt)
+{
+  infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+}
+
+void
+TypeCheckStmt::visit (HIR::ExprStmtWithoutBlock &stmt)
+{
+  infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+}
+
+void
+TypeCheckStmt::visit (HIR::EmptyStmt &stmt)
+{
+  infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckStmt::visit (HIR::ExternBlock &extern_block)
+{
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block);
+    }
+}
+
+void
+TypeCheckStmt::visit (HIR::ConstantItem &constant)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
+
+  infered = type->unify (expr_type);
+  context->insert_type (constant.get_mappings (), infered);
+}
+
+void
+TypeCheckStmt::visit (HIR::LetStmt &stmt)
+{
+  infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
+
+  const HIR::Pattern &stmt_pattern = *stmt.get_pattern ();
+  TyTy::BaseType *init_expr_ty = nullptr;
+  if (stmt.has_init_expr ())
+    {
+      init_expr_ty = TypeCheckExpr::Resolve (stmt.get_init_expr ());
+      if (init_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      init_expr_ty->append_reference (
+	stmt_pattern.get_pattern_mappings ().get_hirid ());
+    }
+
+  TyTy::BaseType *specified_ty = nullptr;
+  if (stmt.has_type ())
+    specified_ty = TypeCheckType::Resolve (stmt.get_type ());
+
+  // let x:i32 = 123;
+  if (specified_ty != nullptr && init_expr_ty != nullptr)
+    {
+      // FIXME use this result and look at the regressions
+      coercion_site (stmt.get_mappings ().get_hirid (), specified_ty,
+		     init_expr_ty, stmt.get_locus ());
+      context->insert_type (stmt_pattern.get_pattern_mappings (), specified_ty);
+    }
+  else
+    {
+      // let x:i32;
+      if (specified_ty != nullptr)
+	{
+	  context->insert_type (stmt_pattern.get_pattern_mappings (),
+				specified_ty);
+	}
+      // let x = 123;
+      else if (init_expr_ty != nullptr)
+	{
+	  context->insert_type (stmt_pattern.get_pattern_mappings (),
+				init_expr_ty);
+	}
+      // let x;
+      else
+	{
+	  context->insert_type (
+	    stmt_pattern.get_pattern_mappings (),
+	    new TyTy::InferType (
+	      stmt_pattern.get_pattern_mappings ().get_hirid (),
+	      TyTy::InferType::InferTypeKind::GENERAL, stmt.get_locus ()));
+	}
+    }
+}
+
+void
+TypeCheckStmt::visit (HIR::TupleStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    {
+      for (auto &generic_param : struct_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  size_t idx = 0;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     std::to_string (idx), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+      idx++;
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::TUPLE, nullptr, std::move (fields)));
+
+  // Process #[repr(...)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::TUPLE_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Enum &enum_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (enum_decl.has_generics ())
+    {
+      for (auto &generic_param : enum_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::VariantDef *> variants;
+  int64_t discriminant_value = 0;
+  for (auto &variant : enum_decl.get_variants ())
+    {
+      TyTy::VariantDef *field_type
+	= TypeCheckEnumItem::Resolve (variant.get (), discriminant_value);
+
+      discriminant_value++;
+      variants.push_back (field_type);
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (enum_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, enum_decl.get_locus ()};
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 enum_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::ENUM, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (enum_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::StructStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    {
+      for (auto &generic_param : struct_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     field.get_field_name (), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  // Process #[repr(...)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::STRUCT_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Union &union_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (union_decl.has_generics ())
+    {
+      for (auto &generic_param : union_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &variant : union_decl.get_variants ())
+    {
+      TyTy::BaseType *variant_type
+	= TypeCheckType::Resolve (variant.get_field_type ().get ());
+      TyTy::StructFieldType *ty_variant
+	= new TyTy::StructFieldType (variant.get_mappings ().get_hirid (),
+				     variant.get_field_name (), variant_type);
+      fields.push_back (ty_variant);
+      context->insert_type (variant.get_mappings (),
+			    ty_variant->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (union_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, union_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    union_decl.get_mappings ().get_hirid (), union_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 union_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::UNION, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (union_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Function &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_function_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, function.get_locus ()};
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_function_name (), ident,
+				  TyTy::FnType::FNTYPE_DEFAULT_FLAGS, ABI::RUST,
+				  std::move (params), ret_type,
+				  std::move (substitutions));
+  context->insert_type (function.get_mappings (), fnType);
+
+  TyTy::FnType *resolved_fn_type = fnType;
+  auto expected_ret_tyty = resolved_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (&function),
+			     expected_ret_tyty);
+
+  auto block_expr_ty
+    = TypeCheckExpr::Resolve (function.get_definition ().get ());
+
+  context->pop_return_type ();
+
+  if (block_expr_ty->get_kind () != TyTy::NEVER)
+    expected_ret_tyty->unify (block_expr_ty);
+
+  infered = fnType;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.h b/gcc/rust/typecheck/rust-hir-type-check-stmt.h
index f8bf6e083dd..a79f17a59ce 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-stmt.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.h
@@ -20,489 +20,68 @@
 #define RUST_HIR_TYPE_CHECK_STMT
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-hir-type-check-type.h"
-#include "rust-hir-type-check-expr.h"
-#include "rust-hir-type-check-enumitem.h"
-#include "rust-hir-type-check-implitem.h"
 
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckStmt : public TypeCheckBase
+class TypeCheckStmt : private TypeCheckBase, private HIR::HIRStmtVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static TyTy::BaseType *Resolve (HIR::Stmt *stmt)
-  {
-    TypeCheckStmt resolver;
-    stmt->accept_vis (resolver);
-    return resolver.infered;
+  static TyTy::BaseType *Resolve (HIR::Stmt *stmt);
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override;
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override;
+  void visit (HIR::EmptyStmt &stmt) override;
+  void visit (HIR::ExternBlock &extern_block) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::LetStmt &stmt) override;
+  void visit (HIR::TupleStruct &struct_decl) override;
+  void visit (HIR::Enum &enum_decl) override;
+  void visit (HIR::StructStruct &struct_decl) override;
+  void visit (HIR::Union &union_decl) override;
+  void visit (HIR::Function &function) override;
+
+  void visit (HIR::EnumItemTuple &) override
+  { /* TODO? */
   }
-
-  void visit (HIR::ExprStmtWithBlock &stmt) override
-  {
-    infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+  void visit (HIR::EnumItemStruct &) override
+  { /* TODO? */
   }
-
-  void visit (HIR::ExprStmtWithoutBlock &stmt) override
-  {
-    infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+  void visit (HIR::EnumItem &item) override
+  { /* TODO? */
   }
-
-  void visit (HIR::EmptyStmt &stmt) override
-  {
-    infered
-      = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
+  void visit (HIR::EnumItemDiscriminant &) override
+  { /* TODO? */
   }
-
-  void visit (HIR::ExternBlock &extern_block) override
-  {
-    for (auto &item : extern_block.get_extern_items ())
-      {
-	TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block);
-      }
+  void visit (HIR::TypePathSegmentFunction &segment) override
+  { /* TODO? */
   }
-
-  void visit (HIR::ConstantItem &constant) override
-  {
-    TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
-    TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
-
-    infered = type->unify (expr_type);
-    context->insert_type (constant.get_mappings (), infered);
+  void visit (HIR::TypePath &path) override
+  { /* TODO? */
   }
-
-  void visit (HIR::LetStmt &stmt) override
-  {
-    infered
-      = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
-
-    const HIR::Pattern &stmt_pattern = *stmt.get_pattern ();
-    TyTy::BaseType *init_expr_ty = nullptr;
-    if (stmt.has_init_expr ())
-      {
-	init_expr_ty = TypeCheckExpr::Resolve (stmt.get_init_expr ());
-	if (init_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
-	  return;
-
-	init_expr_ty->append_reference (
-	  stmt_pattern.get_pattern_mappings ().get_hirid ());
-      }
-
-    TyTy::BaseType *specified_ty = nullptr;
-    if (stmt.has_type ())
-      specified_ty = TypeCheckType::Resolve (stmt.get_type ());
-
-    // let x:i32 = 123;
-    if (specified_ty != nullptr && init_expr_ty != nullptr)
-      {
-	// FIXME use this result and look at the regressions
-	coercion_site (stmt.get_mappings ().get_hirid (), specified_ty,
-		       init_expr_ty, stmt.get_locus ());
-	context->insert_type (stmt_pattern.get_pattern_mappings (),
-			      specified_ty);
-      }
-    else
-      {
-	// let x:i32;
-	if (specified_ty != nullptr)
-	  {
-	    context->insert_type (stmt_pattern.get_pattern_mappings (),
-				  specified_ty);
-	  }
-	// let x = 123;
-	else if (init_expr_ty != nullptr)
-	  {
-	    context->insert_type (stmt_pattern.get_pattern_mappings (),
-				  init_expr_ty);
-	  }
-	// let x;
-	else
-	  {
-	    context->insert_type (
-	      stmt_pattern.get_pattern_mappings (),
-	      new TyTy::InferType (
-		stmt_pattern.get_pattern_mappings ().get_hirid (),
-		TyTy::InferType::InferTypeKind::GENERAL, stmt.get_locus ()));
-	  }
-      }
+  void visit (HIR::QualifiedPathInType &path) override
+  { /* TODO? */
   }
-
-  void visit (HIR::TupleStruct &struct_decl) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (struct_decl.has_generics ())
-      {
-	for (auto &generic_param : struct_decl.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    std::vector<TyTy::StructFieldType *> fields;
-    size_t idx = 0;
-    for (auto &field : struct_decl.get_fields ())
-      {
-	TyTy::BaseType *field_type
-	  = TypeCheckType::Resolve (field.get_field_type ().get ());
-	TyTy::StructFieldType *ty_field
-	  = new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
-				       std::to_string (idx), field_type);
-	fields.push_back (ty_field);
-	context->insert_type (field.get_mappings (),
-			      ty_field->get_field_type ());
-	idx++;
-      }
-
-    // get the path
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok = mappings->lookup_canonical_path (
-      struct_decl.get_mappings ().get_nodeid (), &canonical_path);
-    rust_assert (ok);
-    RustIdent ident{*canonical_path, struct_decl.get_locus ()};
-
-    // there is only a single variant
-    std::vector<TyTy::VariantDef *> variants;
-    variants.push_back (
-      new TyTy::VariantDef (struct_decl.get_mappings ().get_hirid (),
-			    struct_decl.get_identifier (), ident,
-			    TyTy::VariantDef::VariantType::TUPLE, nullptr,
-			    std::move (fields)));
-
-    // Process #[repr(...)] attribute, if any
-    const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
-    TyTy::ADTType::ReprOptions repr
-      = parse_repr_options (attrs, struct_decl.get_locus ());
-
-    TyTy::BaseType *type
-      = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
-			   mappings->get_next_hir_id (),
-			   struct_decl.get_identifier (), ident,
-			   TyTy::ADTType::ADTKind::TUPLE_STRUCT,
-			   std::move (variants), std::move (substitutions),
-			   repr);
-
-    context->insert_type (struct_decl.get_mappings (), type);
-    infered = type;
+  void visit (HIR::Module &module) override
+  { /* TODO? */
   }
-
-  void visit (HIR::Enum &enum_decl) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (enum_decl.has_generics ())
-      {
-	for (auto &generic_param : enum_decl.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    std::vector<TyTy::VariantDef *> variants;
-    int64_t discriminant_value = 0;
-    for (auto &variant : enum_decl.get_variants ())
-      {
-	TyTy::VariantDef *field_type
-	  = TypeCheckEnumItem::Resolve (variant.get (), discriminant_value);
-
-	discriminant_value++;
-	variants.push_back (field_type);
-      }
-
-    // get the path
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok = mappings->lookup_canonical_path (
-      enum_decl.get_mappings ().get_nodeid (), &canonical_path);
-    rust_assert (ok);
-    RustIdent ident{*canonical_path, enum_decl.get_locus ()};
-
-    TyTy::BaseType *type
-      = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (),
-			   mappings->get_next_hir_id (),
-			   enum_decl.get_identifier (), ident,
-			   TyTy::ADTType::ADTKind::ENUM, std::move (variants),
-			   std::move (substitutions));
-
-    context->insert_type (enum_decl.get_mappings (), type);
-    infered = type;
+  void visit (HIR::ExternCrate &crate) override
+  { /* TODO? */
   }
-
-  void visit (HIR::StructStruct &struct_decl) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (struct_decl.has_generics ())
-      {
-	for (auto &generic_param : struct_decl.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    std::vector<TyTy::StructFieldType *> fields;
-    for (auto &field : struct_decl.get_fields ())
-      {
-	TyTy::BaseType *field_type
-	  = TypeCheckType::Resolve (field.get_field_type ().get ());
-	TyTy::StructFieldType *ty_field
-	  = new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
-				       field.get_field_name (), field_type);
-	fields.push_back (ty_field);
-	context->insert_type (field.get_mappings (),
-			      ty_field->get_field_type ());
-      }
-
-    // get the path
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok = mappings->lookup_canonical_path (
-      struct_decl.get_mappings ().get_nodeid (), &canonical_path);
-    rust_assert (ok);
-    RustIdent ident{*canonical_path, struct_decl.get_locus ()};
-
-    // there is only a single variant
-    std::vector<TyTy::VariantDef *> variants;
-    variants.push_back (
-      new TyTy::VariantDef (struct_decl.get_mappings ().get_hirid (),
-			    struct_decl.get_identifier (), ident,
-			    TyTy::VariantDef::VariantType::STRUCT, nullptr,
-			    std::move (fields)));
-
-    // Process #[repr(...)] attribute, if any
-    const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
-    TyTy::ADTType::ReprOptions repr
-      = parse_repr_options (attrs, struct_decl.get_locus ());
-
-    TyTy::BaseType *type
-      = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
-			   mappings->get_next_hir_id (),
-			   struct_decl.get_identifier (), ident,
-			   TyTy::ADTType::ADTKind::STRUCT_STRUCT,
-			   std::move (variants), std::move (substitutions),
-			   repr);
-
-    context->insert_type (struct_decl.get_mappings (), type);
-    infered = type;
+  void visit (HIR::UseDeclaration &use_decl) override
+  { /* TODO? */
   }
-
-  void visit (HIR::Union &union_decl) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (union_decl.has_generics ())
-      {
-	for (auto &generic_param : union_decl.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    std::vector<TyTy::StructFieldType *> fields;
-    for (auto &variant : union_decl.get_variants ())
-      {
-	TyTy::BaseType *variant_type
-	  = TypeCheckType::Resolve (variant.get_field_type ().get ());
-	TyTy::StructFieldType *ty_variant
-	  = new TyTy::StructFieldType (variant.get_mappings ().get_hirid (),
-				       variant.get_field_name (), variant_type);
-	fields.push_back (ty_variant);
-	context->insert_type (variant.get_mappings (),
-			      ty_variant->get_field_type ());
-      }
-
-    // get the path
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok = mappings->lookup_canonical_path (
-      union_decl.get_mappings ().get_nodeid (), &canonical_path);
-    rust_assert (ok);
-    RustIdent ident{*canonical_path, union_decl.get_locus ()};
-
-    // there is only a single variant
-    std::vector<TyTy::VariantDef *> variants;
-    variants.push_back (
-      new TyTy::VariantDef (union_decl.get_mappings ().get_hirid (),
-			    union_decl.get_identifier (), ident,
-			    TyTy::VariantDef::VariantType::STRUCT, nullptr,
-			    std::move (fields)));
-
-    TyTy::BaseType *type
-      = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (),
-			   mappings->get_next_hir_id (),
-			   union_decl.get_identifier (), ident,
-			   TyTy::ADTType::ADTKind::UNION, std::move (variants),
-			   std::move (substitutions));
-
-    context->insert_type (union_decl.get_mappings (), type);
-    infered = type;
+  void visit (HIR::TypeAlias &type_alias) override
+  { /* TODO? */
   }
-
-  void visit (HIR::Function &function) override
-  {
-    std::vector<TyTy::SubstitutionParamMapping> substitutions;
-    if (function.has_generics ())
-      {
-	for (auto &generic_param : function.get_generic_params ())
-	  {
-	    switch (generic_param.get ()->get_kind ())
-	      {
-	      case HIR::GenericParam::GenericKind::LIFETIME:
-	      case HIR::GenericParam::GenericKind::CONST:
-		// FIXME: Skipping Lifetime and Const completely until better
-		// handling.
-		break;
-
-		case HIR::GenericParam::GenericKind::TYPE: {
-		  auto param_type
-		    = TypeResolveGenericParam::Resolve (generic_param.get ());
-		  context->insert_type (generic_param->get_mappings (),
-					param_type);
-
-		  substitutions.push_back (TyTy::SubstitutionParamMapping (
-		    static_cast<HIR::TypeParam &> (*generic_param),
-		    param_type));
-		}
-		break;
-	      }
-	  }
-      }
-
-    TyTy::BaseType *ret_type = nullptr;
-    if (!function.has_function_return_type ())
-      ret_type = TyTy::TupleType::get_unit_type (
-	function.get_mappings ().get_hirid ());
-    else
-      {
-	auto resolved
-	  = TypeCheckType::Resolve (function.get_return_type ().get ());
-	if (resolved == nullptr)
-	  {
-	    rust_error_at (function.get_locus (),
-			   "failed to resolve return type");
-	    return;
-	  }
-
-	ret_type = resolved->clone ();
-	ret_type->set_ref (
-	  function.get_return_type ()->get_mappings ().get_hirid ());
-      }
-
-    std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
-    for (auto &param : function.get_function_params ())
-      {
-	// get the name as well required for later on
-	auto param_tyty = TypeCheckType::Resolve (param.get_type ());
-	params.push_back (
-	  std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
-						       param_tyty));
-
-	context->insert_type (param.get_mappings (), param_tyty);
-	TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
-      }
-
-    // get the path
-    const CanonicalPath *canonical_path = nullptr;
-    bool ok
-      = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
-					 &canonical_path);
-    rust_assert (ok);
-
-    RustIdent ident{*canonical_path, function.get_locus ()};
-    auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
-				    function.get_mappings ().get_defid (),
-				    function.get_function_name (), ident,
-				    TyTy::FnType::FNTYPE_DEFAULT_FLAGS,
-				    ABI::RUST, std::move (params), ret_type,
-				    std::move (substitutions));
-    context->insert_type (function.get_mappings (), fnType);
-
-    TyTy::FnType *resolved_fn_type = fnType;
-    auto expected_ret_tyty = resolved_fn_type->get_return_type ();
-    context->push_return_type (TypeCheckContextItem (&function),
-			       expected_ret_tyty);
-
-    auto block_expr_ty
-      = TypeCheckExpr::Resolve (function.get_definition ().get ());
-
-    context->pop_return_type ();
-
-    if (block_expr_ty->get_kind () != TyTy::NEVER)
-      expected_ret_tyty->unify (block_expr_ty);
-
-    infered = fnType;
+  void visit (HIR::StaticItem &static_item) override
+  { /* TODO? */
+  }
+  void visit (HIR::Trait &trait) override
+  { /* TODO? */
+  }
+  void visit (HIR::ImplBlock &impl) override
+  { /* TODO? */
   }
 
 private:
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct-field.h b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
index 014489d90ce..22af1aad4c3 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
@@ -29,31 +29,18 @@ namespace Resolver {
 
 class TypeCheckStructExpr : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static TyTy::BaseType *Resolve (HIR::StructExprStructFields *expr)
-  {
-    TypeCheckStructExpr resolver (expr);
-    expr->accept_vis (resolver);
-    return resolver.resolved;
-  }
-
-  void visit (HIR::StructExprStructFields &struct_expr) override;
-
-  void visit (HIR::StructExprFieldIdentifierValue &field) override;
+  static TyTy::BaseType *Resolve (HIR::StructExprStructFields *expr);
 
-  void visit (HIR::StructExprFieldIndexValue &field) override;
+protected:
+  void resolve (HIR::StructExprStructFields &struct_expr);
 
-  void visit (HIR::StructExprFieldIdentifier &field) override;
+  void visit (HIR::StructExprFieldIdentifierValue &field);
+  void visit (HIR::StructExprFieldIndexValue &field);
+  void visit (HIR::StructExprFieldIdentifier &field);
 
 private:
-  TypeCheckStructExpr (HIR::Expr *e)
-    : TypeCheckBase (),
-      resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())),
-      struct_path_resolved (nullptr),
-      variant (&TyTy::VariantDef::get_error_node ())
-  {}
+  TypeCheckStructExpr (HIR::Expr *e);
 
   // result
   TyTy::BaseType *resolved;
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct.cc b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
index 5b522772a3e..ec82442beb2 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-struct.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
@@ -24,8 +24,23 @@
 namespace Rust {
 namespace Resolver {
 
+TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr *e)
+  : TypeCheckBase (),
+    resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())),
+    struct_path_resolved (nullptr),
+    variant (&TyTy::VariantDef::get_error_node ())
+{}
+
+TyTy::BaseType *
+TypeCheckStructExpr::Resolve (HIR::StructExprStructFields *expr)
+{
+  TypeCheckStructExpr resolver (expr);
+  resolver.resolve (*expr);
+  return resolver.resolved;
+}
+
 void
-TypeCheckStructExpr::visit (HIR::StructExprStructFields &struct_expr)
+TypeCheckStructExpr::resolve (HIR::StructExprStructFields &struct_expr)
 {
   TyTy::BaseType *struct_path_ty
     = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
@@ -77,7 +92,23 @@ TypeCheckStructExpr::visit (HIR::StructExprStructFields &struct_expr)
   for (auto &field : struct_expr.get_fields ())
     {
       resolved_field_value_expr = nullptr;
-      field->accept_vis (*this);
+
+      switch (field->get_kind ())
+	{
+	case HIR::StructExprField::StructExprFieldKind::IDENTIFIER:
+	  visit (static_cast<HIR::StructExprFieldIdentifier &> (*field.get ()));
+	  break;
+
+	case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE:
+	  visit (
+	    static_cast<HIR::StructExprFieldIdentifierValue &> (*field.get ()));
+	  break;
+
+	case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE:
+	  visit (static_cast<HIR::StructExprFieldIndexValue &> (*field.get ()));
+	  break;
+	}
+
       if (resolved_field_value_expr == nullptr)
 	{
 	  rust_fatal_error (field->get_locus (),
@@ -294,7 +325,7 @@ TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
   if (resolved_field_value_expr != nullptr)
 
     {
-      fields_assigned.insert (field.field_name);
+      fields_assigned.insert (field.get_field_name ());
       adtFieldIndexToField[field_index] = &field;
     }
 }
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
index d5270c9acbb..27f36b642fc 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
@@ -17,15 +17,25 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-hir-type-check-toplevel.h"
+#include "rust-hir-type-check-enumitem.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-hir-type-check-implitem.h"
 
 namespace Rust {
 namespace Resolver {
 
+TypeCheckTopLevel::TypeCheckTopLevel () : TypeCheckBase () {}
+
 void
-TypeCheckTopLevel::Resolve (HIR::Item *item)
+TypeCheckTopLevel::Resolve (HIR::Item &item)
 {
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
   TypeCheckTopLevel resolver;
-  item->accept_vis (resolver);
+  vis_item.accept_vis (resolver);
 }
 
 void
@@ -100,7 +110,7 @@ void
 TypeCheckTopLevel::visit (HIR::Module &module)
 {
   for (auto &item : module.get_items ())
-    TypeCheckTopLevel::Resolve (item.get ());
+    TypeCheckTopLevel::Resolve (*item.get ());
 }
 
 void
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
index d2785b15e54..d0db07d7281 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
@@ -20,37 +20,34 @@
 #define RUST_HIR_TYPE_CHECK_TOPLEVEL
 
 #include "rust-hir-type-check-base.h"
-#include "rust-hir-full.h"
-#include "rust-hir-type-check-implitem.h"
-#include "rust-hir-type-check-type.h"
-#include "rust-hir-type-check-expr.h"
-#include "rust-hir-type-check-enumitem.h"
-#include "rust-tyty.h"
 
 namespace Rust {
 namespace Resolver {
 
-class TypeCheckTopLevel : public TypeCheckBase
+class TypeCheckTopLevel : private TypeCheckBase, public HIR::HIRVisItemVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
-  static void Resolve (HIR::Item *item);
+  static void Resolve (HIR::Item &item);
 
+  void visit (HIR::Module &module) override;
+  void visit (HIR::Function &function) override;
   void visit (HIR::TypeAlias &alias) override;
   void visit (HIR::TupleStruct &struct_decl) override;
-  void visit (HIR::Module &module) override;
   void visit (HIR::StructStruct &struct_decl) override;
   void visit (HIR::Enum &enum_decl) override;
   void visit (HIR::Union &union_decl) override;
   void visit (HIR::StaticItem &var) override;
   void visit (HIR::ConstantItem &constant) override;
-  void visit (HIR::Function &function) override;
   void visit (HIR::ImplBlock &impl_block) override;
   void visit (HIR::ExternBlock &extern_block) override;
 
+  // nothing to do
+  void visit (HIR::Trait &trait_block) override {}
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+
 private:
-  TypeCheckTopLevel () : TypeCheckBase () {}
+  TypeCheckTopLevel ();
 };
 
 } // namespace Resolver
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
index 3c2a7c5c75b..3538d77b220 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -27,7 +27,15 @@ HIR::GenericArgs
 TypeCheckResolveGenericArguments::resolve (HIR::TypePathSegment *segment)
 {
   TypeCheckResolveGenericArguments resolver (segment->get_locus ());
-  segment->accept_vis (resolver);
+  switch (segment->get_type ())
+    {
+    case HIR::TypePathSegment::SegmentType::GENERIC:
+      resolver.visit (static_cast<HIR::TypePathSegmentGeneric &> (*segment));
+      break;
+
+    default:
+      break;
+    }
   return resolver.args;
 }
 
@@ -674,17 +682,35 @@ TyTy::ParamType *
 TypeResolveGenericParam::Resolve (HIR::GenericParam *param)
 {
   TypeResolveGenericParam resolver;
-  param->accept_vis (resolver);
-
-  if (resolver.resolved == nullptr)
+  switch (param->get_kind ())
     {
-      rust_error_at (param->get_locus (), "failed to setup generic parameter");
-      return nullptr;
-    }
+    case HIR::GenericParam::GenericKind::TYPE:
+      resolver.visit (static_cast<HIR::TypeParam &> (*param));
+      break;
 
+    case HIR::GenericParam::GenericKind::CONST:
+      resolver.visit (static_cast<HIR::ConstGenericParam &> (*param));
+      break;
+
+    case HIR::GenericParam::GenericKind::LIFETIME:
+      resolver.visit (static_cast<HIR::LifetimeParam &> (*param));
+      break;
+    }
   return resolver.resolved;
 }
 
+void
+TypeResolveGenericParam::visit (HIR::LifetimeParam &param)
+{
+  // nothing to do
+}
+
+void
+TypeResolveGenericParam::visit (HIR::ConstGenericParam &param)
+{
+  // TODO
+}
+
 void
 TypeResolveGenericParam::visit (HIR::TypeParam &param)
 {
@@ -725,11 +751,20 @@ void
 ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item)
 {
   ResolveWhereClauseItem resolver;
-  item.accept_vis (resolver);
+  switch (item.get_item_type ())
+    {
+    case HIR::WhereClauseItem::LIFETIME:
+      resolver.visit (static_cast<HIR::LifetimeWhereClauseItem &> (item));
+      break;
+
+    case HIR::WhereClauseItem::TYPE_BOUND:
+      resolver.visit (static_cast<HIR::TypeBoundWhereClauseItem &> (item));
+      break;
+    }
 }
 
 void
-ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &)
+ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &item)
 {}
 
 void
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h
index 151e87de601..90d5ddbb411 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.h
@@ -27,14 +27,15 @@
 namespace Rust {
 namespace Resolver {
 
+// FIXME
+// This simply fetches the HIR:::GenericArgs from the base class. Check to see
+// if we can get rid of this class
 class TypeCheckResolveGenericArguments : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static HIR::GenericArgs resolve (HIR::TypePathSegment *segment);
 
-  void visit (HIR::TypePathSegmentGeneric &generic) override;
+  void visit (HIR::TypePathSegmentGeneric &generic);
 
 private:
   TypeCheckResolveGenericArguments (Location locus)
@@ -44,10 +45,8 @@ private:
   HIR::GenericArgs args;
 };
 
-class TypeCheckType : public TypeCheckBase
+class TypeCheckType : public TypeCheckBase, public HIR::HIRTypeVisitor
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static TyTy::BaseType *Resolve (HIR::Type *type);
 
@@ -63,6 +62,22 @@ public:
   void visit (HIR::NeverType &type) override;
   void visit (HIR::TraitObjectType &type) override;
 
+  void visit (HIR::TypePathSegmentFunction &segment) override
+  { /* TODO */
+  }
+  void visit (HIR::TraitBound &bound) override
+  { /* TODO */
+  }
+  void visit (HIR::ImplTraitType &type) override
+  { /* TODO */
+  }
+  void visit (HIR::ParenthesisedType &type) override
+  { /* TODO */
+  }
+  void visit (HIR::ImplTraitTypeOneBound &type) override
+  { /* TODO */
+  }
+
 private:
   TypeCheckType (HirId id)
     : TypeCheckBase (), translated (new TyTy::ErrorType (id))
@@ -82,12 +97,13 @@ private:
 
 class TypeResolveGenericParam : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static TyTy::ParamType *Resolve (HIR::GenericParam *param);
 
-  void visit (HIR::TypeParam &param) override;
+protected:
+  void visit (HIR::TypeParam &param);
+  void visit (HIR::LifetimeParam &param);
+  void visit (HIR::ConstGenericParam &param);
 
 private:
   TypeResolveGenericParam () : TypeCheckBase (), resolved (nullptr) {}
@@ -97,13 +113,12 @@ private:
 
 class ResolveWhereClauseItem : public TypeCheckBase
 {
-  using Rust::Resolver::TypeCheckBase::visit;
-
 public:
   static void Resolve (HIR::WhereClauseItem &item);
 
-  void visit (HIR::LifetimeWhereClauseItem &) override;
-  void visit (HIR::TypeBoundWhereClauseItem &item) override;
+protected:
+  void visit (HIR::LifetimeWhereClauseItem &item);
+  void visit (HIR::TypeBoundWhereClauseItem &item);
 
 private:
   ResolveWhereClauseItem () : TypeCheckBase () {}
diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc
index 37a085f521b..c314585cd3d 100644
--- a/gcc/rust/typecheck/rust-hir-type-check.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check.cc
@@ -21,6 +21,7 @@
 #include "rust-hir-type-check-toplevel.h"
 #include "rust-hir-type-check-item.h"
 #include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
 #include "rust-hir-type-check-struct-field.h"
 #include "rust-hir-inherent-impl-overlap.h"
 
@@ -34,7 +35,7 @@ void
 TypeResolution::Resolve (HIR::Crate &crate)
 {
   for (auto it = crate.items.begin (); it != crate.items.end (); it++)
-    TypeCheckTopLevel::Resolve (it->get ());
+    TypeCheckTopLevel::Resolve (*it->get ());
 
   if (saw_errors ())
     return;
@@ -44,7 +45,7 @@ TypeResolution::Resolve (HIR::Crate &crate)
     return;
 
   for (auto it = crate.items.begin (); it != crate.items.end (); it++)
-    TypeCheckItem::Resolve (it->get ());
+    TypeCheckItem::Resolve (*it->get ());
 
   if (saw_errors ())
     return;
@@ -81,47 +82,6 @@ TypeResolution::Resolve (HIR::Crate &crate)
   });
 }
 
-// RUST_HIR_TYPE_CHECK_EXPR
-void
-TypeCheckExpr::visit (HIR::BlockExpr &expr)
-{
-  for (auto &s : expr.get_statements ())
-    {
-      if (!s->is_item ())
-	continue;
-
-      TypeCheckStmt::Resolve (s.get ());
-    }
-
-  for (auto &s : expr.get_statements ())
-    {
-      if (s->is_item ())
-	continue;
-
-      auto resolved = TypeCheckStmt::Resolve (s.get ());
-      if (resolved == nullptr)
-	{
-	  rust_error_at (s->get_locus (), "failure to resolve type");
-	  return;
-	}
-
-      if (s->is_unit_check_needed () && !resolved->is_unit ())
-	{
-	  auto unit
-	    = TyTy::TupleType::get_unit_type (s->get_mappings ().get_hirid ());
-	  resolved = unit->unify (resolved);
-	}
-    }
-
-  if (expr.has_expr ())
-    infered = TypeCheckExpr::Resolve (expr.get_final_expr ().get ())->clone ();
-  else if (expr.is_tail_reachable ())
-    infered
-      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
-  else
-    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
-}
-
 // rust-hir-trait-ref.h
 
 TraitItemReference::TraitItemReference (
diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h
index 36963e5efa3..f66d7eb4b8c 100644
--- a/gcc/rust/typecheck/rust-tycheck-dump.h
+++ b/gcc/rust/typecheck/rust-tycheck-dump.h
@@ -25,9 +25,9 @@
 namespace Rust {
 namespace Resolver {
 
-class TypeResolverDump : public TypeCheckBase
+class TypeResolverDump : private TypeCheckBase, private HIR::HIRFullVisitorBase
 {
-  using Rust::HIR::HIRFullVisitorBase::visit;
+  using HIR::HIRFullVisitorBase::visit;
 
 public:
   static void go (HIR::Crate &crate, std::ofstream &out)


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-11 19:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-11 19:19 [gcc/devel/rust/master] Refactor the TypeResolution pass to use fine grained visitors Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).