public inbox for gcc-rust@gcc.gnu.org
 help / color / mirror / Atom feed
From: herron.philip@googlemail.com
To: gcc-patches@gcc.gnu.org
Cc: gcc-rust@gcc.gnu.org, Philip Herron <philip.herron@embecosm.com>
Subject: [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass
Date: Wed, 24 Aug 2022 12:59:41 +0100	[thread overview]
Message-ID: <20220824115956.737931-23-philip.herron@embecosm.com> (raw)
In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com>

From: Philip Herron <philip.herron@embecosm.com>

TODO
---
 gcc/rust/typecheck/rust-autoderef.cc          |  398 +++
 gcc/rust/typecheck/rust-autoderef.h           |  178 +
 gcc/rust/typecheck/rust-casts.cc              |  292 ++
 gcc/rust/typecheck/rust-casts.h               |   53 +
 gcc/rust/typecheck/rust-coercion.cc           |  357 ++
 gcc/rust/typecheck/rust-coercion.h            |   93 +
 gcc/rust/typecheck/rust-hir-dot-operator.cc   |  263 ++
 gcc/rust/typecheck/rust-hir-dot-operator.h    |   81 +
 .../rust-hir-inherent-impl-overlap.h          |  186 ++
 gcc/rust/typecheck/rust-hir-path-probe.h      |  540 +++
 gcc/rust/typecheck/rust-hir-trait-ref.h       |  472 +++
 gcc/rust/typecheck/rust-hir-trait-resolve.cc  |  599 ++++
 gcc/rust/typecheck/rust-hir-trait-resolve.h   |   87 +
 gcc/rust/typecheck/rust-hir-type-bounds.h     |   77 +
 .../typecheck/rust-hir-type-check-base.cc     |  439 +++
 gcc/rust/typecheck/rust-hir-type-check-base.h |   80 +
 .../typecheck/rust-hir-type-check-enumitem.cc |  213 ++
 .../typecheck/rust-hir-type-check-enumitem.h  |   50 +
 .../typecheck/rust-hir-type-check-expr.cc     | 1567 +++++++++
 gcc/rust/typecheck/rust-hir-type-check-expr.h |  131 +
 .../typecheck/rust-hir-type-check-implitem.cc |  583 ++++
 .../typecheck/rust-hir-type-check-implitem.h  |  114 +
 .../typecheck/rust-hir-type-check-item.cc     |  237 ++
 gcc/rust/typecheck/rust-hir-type-check-item.h |   58 +
 .../typecheck/rust-hir-type-check-path.cc     |  467 +++
 .../typecheck/rust-hir-type-check-pattern.cc  |  416 +++
 .../typecheck/rust-hir-type-check-pattern.h   |   62 +
 .../typecheck/rust-hir-type-check-stmt.cc     |  498 +++
 gcc/rust/typecheck/rust-hir-type-check-stmt.h |   96 +
 .../rust-hir-type-check-struct-field.h        |   59 +
 .../typecheck/rust-hir-type-check-struct.cc   |  340 ++
 .../typecheck/rust-hir-type-check-toplevel.cc |  364 +++
 .../typecheck/rust-hir-type-check-toplevel.h  |   56 +
 .../typecheck/rust-hir-type-check-type.cc     |  838 +++++
 gcc/rust/typecheck/rust-hir-type-check-type.h |  130 +
 .../typecheck/rust-hir-type-check-util.cc     |   41 +
 gcc/rust/typecheck/rust-hir-type-check-util.h |   50 +
 gcc/rust/typecheck/rust-hir-type-check.cc     |  295 ++
 gcc/rust/typecheck/rust-hir-type-check.h      |  379 +++
 .../typecheck/rust-substitution-mapper.cc     |   77 +
 gcc/rust/typecheck/rust-substitution-mapper.h |  394 +++
 gcc/rust/typecheck/rust-tycheck-dump.h        |  239 ++
 gcc/rust/typecheck/rust-tyctx.cc              |  155 +
 gcc/rust/typecheck/rust-tyty-bounds.cc        |  462 +++
 gcc/rust/typecheck/rust-tyty-call.cc          |  263 ++
 gcc/rust/typecheck/rust-tyty-call.h           |  147 +
 gcc/rust/typecheck/rust-tyty-cmp.h            | 1554 +++++++++
 gcc/rust/typecheck/rust-tyty-rules.h          | 1584 +++++++++
 gcc/rust/typecheck/rust-tyty-visitor.h        |   88 +
 gcc/rust/typecheck/rust-tyty.cc               | 2885 +++++++++++++++++
 gcc/rust/typecheck/rust-tyty.h                | 2533 +++++++++++++++
 51 files changed, 21620 insertions(+)
 create mode 100644 gcc/rust/typecheck/rust-autoderef.cc
 create mode 100644 gcc/rust/typecheck/rust-autoderef.h
 create mode 100644 gcc/rust/typecheck/rust-casts.cc
 create mode 100644 gcc/rust/typecheck/rust-casts.h
 create mode 100644 gcc/rust/typecheck/rust-coercion.cc
 create mode 100644 gcc/rust/typecheck/rust-coercion.h
 create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.h
 create mode 100644 gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
 create mode 100644 gcc/rust/typecheck/rust-hir-path-probe.h
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-ref.h
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-bounds.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-path.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct-field.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check.h
 create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.cc
 create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.h
 create mode 100644 gcc/rust/typecheck/rust-tycheck-dump.h
 create mode 100644 gcc/rust/typecheck/rust-tyctx.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-bounds.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-call.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-call.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-cmp.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-rules.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-visitor.h
 create mode 100644 gcc/rust/typecheck/rust-tyty.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty.h

diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc
new file mode 100644
index 00000000000..423f8e4709b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.cc
@@ -0,0 +1,398 @@
+// 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-autoderef.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+static bool
+resolve_operator_overload_fn (
+  Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
+  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  Adjustment::AdjustmentType *requires_ref_adjustment);
+
+TyTy::BaseType *
+Adjuster::adjust_type (const std::vector<Adjustment> &adjustments)
+{
+  if (adjustments.size () == 0)
+    return base->clone ();
+
+  return adjustments.back ().get_expected ()->clone ();
+}
+
+Adjustment
+Adjuster::try_deref_type (const TyTy::BaseType *ty,
+			  Analysis::RustLangItem::ItemType deref_lang_item)
+{
+  HIR::ImplItem *impl_item = nullptr;
+  TyTy::FnType *fn = nullptr;
+  Adjustment::AdjustmentType requires_ref_adjustment
+    = Adjustment::AdjustmentType::ERROR;
+  bool operator_overloaded
+    = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item,
+				    &requires_ref_adjustment);
+  if (!operator_overloaded)
+    {
+      return Adjustment::get_error ();
+    }
+
+  auto resolved_base = fn->get_return_type ()->clone ();
+  bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  TyTy::ReferenceType *ref_base
+    = static_cast<TyTy::ReferenceType *> (resolved_base);
+
+  Adjustment::AdjustmentType adjustment_type
+    = Adjustment::AdjustmentType::ERROR;
+  switch (deref_lang_item)
+    {
+    case Analysis::RustLangItem::ItemType::DEREF:
+      adjustment_type = Adjustment::AdjustmentType::DEREF;
+      break;
+
+    case Analysis::RustLangItem::ItemType::DEREF_MUT:
+      adjustment_type = Adjustment::AdjustmentType::DEREF_MUT;
+      break;
+
+    default:
+      break;
+    }
+
+  return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty,
+						       ref_base, fn, impl_item,
+						       requires_ref_adjustment);
+}
+
+Adjustment
+Adjuster::try_raw_deref_type (const TyTy::BaseType *ty)
+{
+  bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  const TyTy::ReferenceType *ref_base
+    = static_cast<const TyTy::ReferenceType *> (ty);
+  auto infered = ref_base->get_base ()->clone ();
+
+  return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
+}
+
+Adjustment
+Adjuster::try_unsize_type (const TyTy::BaseType *ty)
+{
+  bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = TypeCheckContext::get ();
+
+  const auto ref_base = static_cast<const TyTy::ArrayType *> (ty);
+  auto slice_elem = ref_base->get_element_type ();
+
+  auto slice
+    = new TyTy::SliceType (mappings->get_next_hir_id (), ty->get_ident ().locus,
+			   TyTy::TyVar (slice_elem->get_ref ()));
+  context->insert_implicit_type (slice);
+
+  return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice);
+}
+
+static bool
+resolve_operator_overload_fn (
+  Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
+  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  Adjustment::AdjustmentType *requires_ref_adjustment)
+{
+  auto context = TypeCheckContext::get ();
+  auto mappings = Analysis::Mappings::get ();
+
+  // look up lang item for arithmetic type
+  std::string associated_item_name
+    = Analysis::RustLangItem::ToString (lang_item_type);
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  if (!lang_item_defined)
+    return false;
+
+  auto segment = HIR::PathIdentSegment (associated_item_name);
+  auto candidate
+    = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name),
+			     true);
+
+  bool have_implementation_for_lang_item = !candidate.is_error ();
+  if (!have_implementation_for_lang_item)
+    return false;
+
+  // Get the adjusted self
+  Adjuster adj (ty);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // is this the case we are recursive
+  // handle the case where we are within the impl block for this
+  // lang_item otherwise we end up with a recursive operator overload
+  // such as the i32 operator overload trait
+  TypeCheckContextItem &fn_context = context->peek_context ();
+  if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+    {
+      auto &impl_item = fn_context.get_impl_item ();
+      HIR::ImplBlock *parent = impl_item.first;
+      HIR::Function *fn = impl_item.second;
+
+      if (parent->has_trait_ref ()
+	  && fn->get_function_name ().compare (associated_item_name) == 0)
+	{
+	  TraitReference *trait_reference
+	    = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
+	  if (!trait_reference->is_error ())
+	    {
+	      TyTy::BaseType *lookup = nullptr;
+	      bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
+					      &lookup);
+	      rust_assert (ok);
+	      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+
+	      TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+	      rust_assert (fntype->is_method ());
+
+	      bool is_lang_item_impl
+		= trait_reference->get_mappings ().get_defid ()
+		  == respective_lang_item_id;
+	      bool self_is_lang_item_self
+		= fntype->get_self_type ()->is_equal (*adjusted_self);
+	      bool recursive_operator_overload
+		= is_lang_item_impl && self_is_lang_item_self;
+
+	      if (recursive_operator_overload)
+		return false;
+	    }
+	}
+    }
+
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+
+  // rust only support impl item deref operator overloading ie you must have an
+  // impl block for it
+  rust_assert (candidate.candidate.type
+	       == PathProbeCandidate::CandidateType::IMPL_FUNC);
+  *impl_item = candidate.candidate.item.impl.impl_item;
+
+  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (fn->is_method ());
+
+  if (fn->needs_substitution ())
+    {
+      if (ty->get_kind () == TyTy::TypeKind::ADT)
+	{
+	  const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+      else
+	{
+	  rust_assert (candidate.adjustments.size () < 2);
+
+	  // lets infer the params for this we could probably fix this up by
+	  // actually just performing a substitution of a single param but this
+	  // seems more generic i think.
+	  //
+	  // this is the case where we had say Foo<&Bar>> and we have derefed to
+	  // the &Bar and we are trying to match a method self of Bar which
+	  // requires another deref which is matched to the deref trait impl of
+	  // &&T so this requires another reference and deref call
+
+	  lookup = fn->infer_substitions (Location ());
+	  rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+	  fn = static_cast<TyTy::FnType *> (lookup);
+	  fn->get_self_type ()->unify (adjusted_self);
+	  lookup = fn;
+	}
+    }
+
+  if (candidate.adjustments.size () > 0)
+    *requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
+
+  *resolved_fn = static_cast<TyTy::FnType *> (lookup);
+
+  return true;
+}
+
+AutoderefCycle::AutoderefCycle (bool autoderef_flag)
+  : autoderef_flag (autoderef_flag)
+{}
+
+AutoderefCycle::~AutoderefCycle () {}
+
+void
+AutoderefCycle::try_hook (const TyTy::BaseType &)
+{}
+
+bool
+AutoderefCycle::cycle (const TyTy::BaseType *receiver)
+{
+  const TyTy::BaseType *r = receiver;
+  while (true)
+    {
+      if (try_autoderefed (r))
+	return true;
+
+      // 4. deref to to 1, if cannot deref then quit
+      if (autoderef_flag)
+	return false;
+
+      // try unsize
+      Adjustment unsize = Adjuster::try_unsize_type (r);
+      if (!unsize.is_error ())
+	{
+	  adjustments.push_back (unsize);
+	  auto unsize_r = unsize.get_expected ();
+
+	  if (try_autoderefed (unsize_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      Adjustment deref
+	= Adjuster::try_deref_type (r, Analysis::RustLangItem::ItemType::DEREF);
+      if (!deref.is_error ())
+	{
+	  auto deref_r = deref.get_expected ();
+	  adjustments.push_back (deref);
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      Adjustment deref_mut = Adjuster::try_deref_type (
+	r, Analysis::RustLangItem::ItemType::DEREF_MUT);
+      if (!deref_mut.is_error ())
+	{
+	  auto deref_r = deref_mut.get_expected ();
+	  adjustments.push_back (deref_mut);
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      if (!deref_mut.is_error ())
+	{
+	  auto deref_r = deref_mut.get_expected ();
+	  adjustments.push_back (deref_mut);
+	  Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
+	  adjustments.push_back (raw_deref);
+	  deref_r = raw_deref.get_expected ();
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	  adjustments.pop_back ();
+	}
+
+      if (!deref.is_error ())
+	{
+	  r = deref.get_expected ();
+	  adjustments.push_back (deref);
+	}
+      Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
+      if (raw_deref.is_error ())
+	return false;
+
+      r = raw_deref.get_expected ();
+      adjustments.push_back (raw_deref);
+    }
+  return false;
+}
+
+bool
+AutoderefCycle::try_autoderefed (const TyTy::BaseType *r)
+{
+  try_hook (*r);
+
+  // 1. try raw
+  if (select (*r))
+    return true;
+
+  // 2. try ref
+  TyTy::ReferenceType *r1
+    = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
+			       Mutability::Imm);
+  adjustments.push_back (
+    Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1));
+  if (select (*r1))
+    return true;
+
+  adjustments.pop_back ();
+
+  // 3. try mut ref
+  TyTy::ReferenceType *r2
+    = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
+			       Mutability::Mut);
+  adjustments.push_back (
+    Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2));
+  if (select (*r2))
+    return true;
+
+  adjustments.pop_back ();
+
+  return false;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-autoderef.h b/gcc/rust/typecheck/rust-autoderef.h
new file mode 100644
index 00000000000..2f8d64b97e6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.h
@@ -0,0 +1,178 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AUTODEREF
+#define RUST_AUTODEREF
+
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class Adjustment
+{
+public:
+  enum AdjustmentType
+  {
+    ERROR,
+
+    IMM_REF,
+    MUT_REF,
+    DEREF,
+    DEREF_MUT,
+    INDIRECTION,
+    UNSIZE,
+  };
+
+  // ctor for all adjustments except derefs
+  Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
+	      const TyTy::BaseType *expected)
+    : Adjustment (type, actual, expected, nullptr, nullptr,
+		  AdjustmentType::ERROR)
+  {}
+
+  static Adjustment get_op_overload_deref_adjustment (
+    AdjustmentType type, const TyTy::BaseType *actual,
+    const TyTy::BaseType *expected, TyTy::FnType *fn, HIR::ImplItem *deref_item,
+    Adjustment::AdjustmentType requires_ref_adjustment)
+  {
+    rust_assert (type == DEREF || type == DEREF_MUT);
+    return Adjustment (type, actual, expected, fn, deref_item,
+		       requires_ref_adjustment);
+  }
+
+  AdjustmentType get_type () const { return type; }
+
+  const TyTy::BaseType *get_actual () const { return actual; }
+  const TyTy::BaseType *get_expected () const { return expected; }
+
+  std::string as_string () const
+  {
+    return Adjustment::type_string (get_type ()) + "->"
+	   + get_expected ()->debug_str ();
+  }
+
+  static std::string type_string (AdjustmentType type)
+  {
+    switch (type)
+      {
+      case AdjustmentType::ERROR:
+	return "ERROR";
+      case AdjustmentType::IMM_REF:
+	return "IMM_REF";
+      case AdjustmentType::MUT_REF:
+	return "MUT_REF";
+      case AdjustmentType::DEREF:
+	return "DEREF";
+      case AdjustmentType::DEREF_MUT:
+	return "DEREF_MUT";
+      case AdjustmentType::INDIRECTION:
+	return "INDIRECTION";
+      case AdjustmentType::UNSIZE:
+	return "UNSIZE";
+      }
+    gcc_unreachable ();
+    return "";
+  }
+
+  static Adjustment get_error () { return Adjustment{ERROR, nullptr, nullptr}; }
+
+  bool is_error () const { return type == ERROR; }
+
+  bool is_deref_adjustment () const { return type == DEREF; }
+
+  bool is_deref_mut_adjustment () const { return type == DEREF_MUT; }
+
+  bool has_operator_overload () const { return deref_operator_fn != nullptr; }
+
+  TyTy::FnType *get_deref_operator_fn () const { return deref_operator_fn; }
+
+  AdjustmentType get_deref_adjustment_type () const
+  {
+    return requires_ref_adjustment;
+  }
+
+  HIR::ImplItem *get_deref_hir_item () const { return deref_item; }
+
+private:
+  Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
+	      const TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn,
+	      HIR::ImplItem *deref_item,
+	      Adjustment::AdjustmentType requires_ref_adjustment)
+    : type (type), actual (actual), expected (expected),
+      deref_operator_fn (deref_operator_fn), deref_item (deref_item),
+      requires_ref_adjustment (requires_ref_adjustment)
+  {}
+
+  AdjustmentType type;
+  const TyTy::BaseType *actual;
+  const TyTy::BaseType *expected;
+
+  // - only used for deref operator_overloads
+  //
+  // the fn that we are calling
+  TyTy::FnType *deref_operator_fn;
+  HIR::ImplItem *deref_item;
+  // operator overloads can requre a reference
+  Adjustment::AdjustmentType requires_ref_adjustment;
+};
+
+class Adjuster
+{
+public:
+  Adjuster (const TyTy::BaseType *ty) : base (ty) {}
+
+  TyTy::BaseType *adjust_type (const std::vector<Adjustment> &adjustments);
+
+  static Adjustment
+  try_deref_type (const TyTy::BaseType *ty,
+		  Analysis::RustLangItem::ItemType deref_lang_item);
+
+  static Adjustment try_raw_deref_type (const TyTy::BaseType *ty);
+
+  static Adjustment try_unsize_type (const TyTy::BaseType *ty);
+
+private:
+  const TyTy::BaseType *base;
+};
+
+class AutoderefCycle
+{
+protected:
+  AutoderefCycle (bool autoderef_flag);
+
+  virtual ~AutoderefCycle ();
+
+  virtual bool select (const TyTy::BaseType &autoderefed) = 0;
+
+  // optional: this is a chance to hook in to grab predicate items on the raw
+  // type
+  virtual void try_hook (const TyTy::BaseType &);
+
+  virtual bool cycle (const TyTy::BaseType *receiver);
+
+  bool try_autoderefed (const TyTy::BaseType *r);
+
+  bool autoderef_flag;
+  std::vector<Adjustment> adjustments;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AUTODEREF
diff --git a/gcc/rust/typecheck/rust-casts.cc b/gcc/rust/typecheck/rust-casts.cc
new file mode 100644
index 00000000000..61004dfabc3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.cc
@@ -0,0 +1,292 @@
+// 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-casts.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCastRules::TypeCastRules (Location locus, TyTy::TyWithLocation from,
+			      TyTy::TyWithLocation to)
+  : locus (locus), from (from), to (to)
+{}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::resolve (Location locus, TyTy::TyWithLocation from,
+			TyTy::TyWithLocation to)
+{
+  TypeCastRules cast_rules (locus, from, to);
+  return cast_rules.check ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::check ()
+{
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582
+  auto possible_coercion
+    = TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus);
+  if (!possible_coercion.is_error ())
+    return possible_coercion;
+
+  // try the simple cast rules
+  auto simple_cast = cast_rules ();
+  if (!simple_cast.is_error ())
+    return simple_cast;
+
+  // failed to cast
+  emit_cast_error ();
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::cast_rules ()
+{
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654
+
+  rust_debug ("cast_rules from={%s} to={%s}",
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  switch (from.get_ty ()->get_kind ())
+    {
+      case TyTy::TypeKind::INFER: {
+	TyTy::InferType *from_infer
+	  = static_cast<TyTy::InferType *> (from.get_ty ());
+	switch (from_infer->get_infer_kind ())
+	  {
+	  case TyTy::InferType::InferTypeKind::GENERAL:
+	    return TypeCoercionRules::CoercionResult{{},
+						     to.get_ty ()->clone ()};
+
+	  case TyTy::InferType::InferTypeKind::INTEGRAL:
+	    switch (to.get_ty ()->get_kind ())
+	      {
+	      case TyTy::TypeKind::CHAR:
+	      case TyTy::TypeKind::BOOL:
+	      case TyTy::TypeKind::USIZE:
+	      case TyTy::TypeKind::ISIZE:
+	      case TyTy::TypeKind::UINT:
+	      case TyTy::TypeKind::INT:
+	      case TyTy::TypeKind::POINTER:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+		case TyTy::TypeKind::INFER: {
+		  TyTy::InferType *to_infer
+		    = static_cast<TyTy::InferType *> (to.get_ty ());
+
+		  switch (to_infer->get_infer_kind ())
+		    {
+		    case TyTy::InferType::InferTypeKind::GENERAL:
+		    case TyTy::InferType::InferTypeKind::INTEGRAL:
+		      return TypeCoercionRules::CoercionResult{
+			{}, to.get_ty ()->clone ()};
+
+		    default:
+		      return TypeCoercionRules::CoercionResult::get_error ();
+		    }
+		}
+		break;
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	    break;
+
+	  case TyTy::InferType::InferTypeKind::FLOAT:
+	    switch (to.get_ty ()->get_kind ())
+	      {
+	      case TyTy::TypeKind::USIZE:
+	      case TyTy::TypeKind::ISIZE:
+	      case TyTy::TypeKind::UINT:
+	      case TyTy::TypeKind::INT:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+		case TyTy::TypeKind::INFER: {
+		  TyTy::InferType *to_infer
+		    = static_cast<TyTy::InferType *> (to.get_ty ());
+
+		  switch (to_infer->get_infer_kind ())
+		    {
+		    case TyTy::InferType::InferTypeKind::GENERAL:
+		    case TyTy::InferType::InferTypeKind::FLOAT:
+		      return TypeCoercionRules::CoercionResult{
+			{}, to.get_ty ()->clone ()};
+
+		    default:
+		      return TypeCoercionRules::CoercionResult::get_error ();
+		    }
+		}
+		break;
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	    break;
+	  }
+      }
+      break;
+
+    case TyTy::TypeKind::BOOL:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::INFER:
+	case TyTy::TypeKind::USIZE:
+	case TyTy::TypeKind::ISIZE:
+	case TyTy::TypeKind::UINT:
+	case TyTy::TypeKind::INT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::CHAR:
+    case TyTy::TypeKind::USIZE:
+    case TyTy::TypeKind::ISIZE:
+    case TyTy::TypeKind::UINT:
+    case TyTy::TypeKind::INT:
+      switch (to.get_ty ()->get_kind ())
+	{
+	  case TyTy::TypeKind::CHAR: {
+	    // only u8 and char
+	    bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT;
+	    bool was_u8 = was_uint
+			  && (static_cast<TyTy::UintType *> (from.get_ty ())
+				->get_uint_kind ()
+			      == TyTy::UintType::UintKind::U8);
+	    if (was_u8)
+	      return TypeCoercionRules::CoercionResult{{},
+						       to.get_ty ()->clone ()};
+	  }
+	  break;
+
+	case TyTy::TypeKind::INFER:
+	case TyTy::TypeKind::USIZE:
+	case TyTy::TypeKind::ISIZE:
+	case TyTy::TypeKind::UINT:
+	case TyTy::TypeKind::INT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::FLOAT:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::FLOAT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	  case TyTy::TypeKind::INFER: {
+	    TyTy::InferType *to_infer
+	      = static_cast<TyTy::InferType *> (to.get_ty ());
+
+	    switch (to_infer->get_infer_kind ())
+	      {
+	      case TyTy::InferType::InferTypeKind::GENERAL:
+	      case TyTy::InferType::InferTypeKind::FLOAT:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	  }
+	  break;
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::REF:
+    case TyTy::TypeKind::POINTER:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::REF:
+	case TyTy::TypeKind::POINTER:
+	  return check_ptr_ptr_cast ();
+
+	  // FIXME can you cast a pointer to a integral type?
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    default:
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::check_ptr_ptr_cast ()
+{
+  rust_debug ("check_ptr_ptr_cast from={%s} to={%s}",
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF;
+  bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF;
+  bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
+  bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
+
+  if (from_is_ptr && to_is_ptr)
+    {
+      // mutability is ignored here as all pointer usage requires unsafe
+      return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+    }
+  else if (from_is_ref && to_is_ref)
+    {
+      // mutability must be coercedable
+      TyTy::ReferenceType &f
+	= static_cast<TyTy::ReferenceType &> (*from.get_ty ());
+      TyTy::ReferenceType &t
+	= static_cast<TyTy::ReferenceType &> (*to.get_ty ());
+
+      if (TypeCoercionRules::coerceable_mutability (f.mutability (),
+						    t.mutability ()))
+	{
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+	}
+    }
+
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+void
+TypeCastRules::emit_cast_error () const
+{
+  // error[E0604]
+  RichLocation r (locus);
+  r.add_range (from.get_locus ());
+  r.add_range (to.get_locus ());
+  rust_error_at (r, "invalid cast %<%s%> to %<%s%>",
+		 from.get_ty ()->get_name ().c_str (),
+		 to.get_ty ()->get_name ().c_str ());
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-casts.h b/gcc/rust/typecheck/rust-casts.h
new file mode 100644
index 00000000000..e908f49b656
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_CASTS
+#define RUST_CASTS
+
+#include "rust-tyty.h"
+#include "rust-coercion.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCastRules
+{
+public:
+  static TypeCoercionRules::CoercionResult
+  resolve (Location locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to);
+
+protected:
+  TypeCoercionRules::CoercionResult check ();
+  TypeCoercionRules::CoercionResult cast_rules ();
+  TypeCoercionRules::CoercionResult check_ptr_ptr_cast ();
+
+  void emit_cast_error () const;
+
+protected:
+  TypeCastRules (Location locus, TyTy::TyWithLocation from,
+		 TyTy::TyWithLocation to);
+
+  Location locus;
+  TyTy::TyWithLocation from;
+  TyTy::TyWithLocation to;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_CASTS
diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc
new file mode 100644
index 00000000000..2ad2b8007ff
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.cc
@@ -0,0 +1,357 @@
+// 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-coercion.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
+			   Location locus)
+{
+  TypeCoercionRules resolver (expected, locus, true);
+  bool ok = resolver.do_coercion (receiver);
+  return ok ? resolver.try_result : CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
+			      TyTy::BaseType *expected, Location locus)
+{
+  TypeCoercionRules resolver (expected, locus, false);
+  bool ok = resolver.do_coercion (receiver);
+  return ok ? resolver.try_result : CoercionResult::get_error ();
+}
+
+TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected, Location locus,
+				      bool emit_errors)
+  : AutoderefCycle (false), mappings (Analysis::Mappings::get ()),
+    context (TypeCheckContext::get ()), expected (expected), locus (locus),
+    try_result (CoercionResult::get_error ()), emit_errors (emit_errors)
+{}
+
+bool
+TypeCoercionRules::do_coercion (TyTy::BaseType *receiver)
+{
+  // FIXME this is not finished and might be super simplified
+  // see:
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
+
+  // unsize
+  bool unsafe_error = false;
+  CoercionResult unsize_coercion
+    = coerce_unsized (receiver, expected, unsafe_error);
+  bool valid_unsize_coercion = !unsize_coercion.is_error ();
+  if (valid_unsize_coercion)
+    {
+      try_result = unsize_coercion;
+      return true;
+    }
+  else if (unsafe_error)
+    {
+      // Location lhs = mappings->lookup_location (receiver->get_ref ());
+      // Location rhs = mappings->lookup_location (expected->get_ref ());
+      // object_unsafe_error (locus, lhs, rhs);
+      return false;
+    }
+
+  // pointers
+  switch (expected->get_kind ())
+    {
+      case TyTy::TypeKind::POINTER: {
+	TyTy::PointerType *ptr = static_cast<TyTy::PointerType *> (expected);
+	try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
+	return !try_result.is_error ();
+      }
+
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ptr
+	  = static_cast<TyTy::ReferenceType *> (expected);
+	try_result
+	  = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
+	return !try_result.is_error ();
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return !try_result.is_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
+				      TyTy::PointerType *expected,
+				      Mutability to_mutbl)
+{
+  rust_debug ("coerce_unsafe_ptr(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+
+  Mutability from_mutbl = Mutability::Imm;
+  TyTy::BaseType *element = nullptr;
+  switch (receiver->get_kind ())
+    {
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ref
+	  = static_cast<TyTy::ReferenceType *> (receiver);
+	from_mutbl = ref->mutability ();
+	element = ref->get_base ();
+      }
+      break;
+
+      case TyTy::TypeKind::POINTER: {
+	TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
+	from_mutbl = ref->mutability ();
+	element = ref->get_base ();
+      }
+      break;
+
+      default: {
+	if (receiver->can_eq (expected, false))
+	  return CoercionResult{{}, expected->clone ()};
+
+	return CoercionResult::get_error ();
+      }
+    }
+
+  if (!coerceable_mutability (from_mutbl, to_mutbl))
+    {
+      Location lhs = mappings->lookup_location (receiver->get_ref ());
+      Location rhs = mappings->lookup_location (expected->get_ref ());
+      mismatched_mutability_error (locus, lhs, rhs);
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  TyTy::PointerType *result
+    = new TyTy::PointerType (receiver->get_ref (),
+			     TyTy::TyVar (element->get_ref ()), to_mutbl);
+  if (!result->can_eq (expected, false))
+    return CoercionResult::get_error ();
+
+  return CoercionResult{{}, result};
+}
+
+/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
+/// To match `A` with `B`, autoderef will be performed,
+/// calling `deref`/`deref_mut` where necessary.
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
+					    TyTy::ReferenceType *expected,
+					    Mutability to_mutbl)
+{
+  rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+
+  Mutability from_mutbl = Mutability::Imm;
+  switch (receiver->get_kind ())
+    {
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ref
+	  = static_cast<TyTy::ReferenceType *> (receiver);
+	from_mutbl = ref->mutability ();
+      }
+      break;
+
+      default: {
+	TyTy::BaseType *result = receiver->unify (expected);
+	return CoercionResult{{}, result};
+      }
+    }
+
+  if (!coerceable_mutability (from_mutbl, to_mutbl))
+    {
+      Location lhs = mappings->lookup_location (receiver->get_ref ());
+      Location rhs = mappings->lookup_location (expected->get_ref ());
+      mismatched_mutability_error (locus, lhs, rhs);
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  AutoderefCycle::cycle (receiver);
+  return try_result;
+}
+
+// &[T; n] or &mut [T; n] -> &[T]
+// or &mut [T; n] -> &mut [T]
+// or &Concrete -> &Trait, etc.
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
+				   TyTy::BaseType *target, bool &unsafe_error)
+{
+  rust_debug ("coerce_unsized(source={%s}, target={%s})",
+	      source->debug_str ().c_str (), target->debug_str ().c_str ());
+
+  bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
+  bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
+  bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
+
+  bool needs_reborrow = false;
+  TyTy::BaseType *ty_a = source;
+  TyTy::BaseType *ty_b = target;
+  Mutability expected_mutability = Mutability::Imm;
+  if (source_is_ref && target_is_ref)
+    {
+      TyTy::ReferenceType *source_ref
+	= static_cast<TyTy::ReferenceType *> (source);
+      TyTy::ReferenceType *target_ref
+	= static_cast<TyTy::ReferenceType *> (target);
+
+      Mutability from_mutbl = source_ref->mutability ();
+      Mutability to_mutbl = target_ref->mutability ();
+      if (!coerceable_mutability (from_mutbl, to_mutbl))
+	{
+	  unsafe_error = true;
+	  Location lhs = mappings->lookup_location (source->get_ref ());
+	  Location rhs = mappings->lookup_location (target->get_ref ());
+	  mismatched_mutability_error (locus, lhs, rhs);
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      ty_a = source_ref->get_base ();
+      ty_b = target_ref->get_base ();
+      needs_reborrow = true;
+      expected_mutability = to_mutbl;
+
+      adjustments.push_back (
+	Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
+    }
+  else if (source_is_ref && target_is_ptr)
+    {
+      TyTy::ReferenceType *source_ref
+	= static_cast<TyTy::ReferenceType *> (source);
+      TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
+
+      Mutability from_mutbl = source_ref->mutability ();
+      Mutability to_mutbl = target_ref->mutability ();
+      if (!coerceable_mutability (from_mutbl, to_mutbl))
+	{
+	  unsafe_error = true;
+	  Location lhs = mappings->lookup_location (source->get_ref ());
+	  Location rhs = mappings->lookup_location (target->get_ref ());
+	  mismatched_mutability_error (locus, lhs, rhs);
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      ty_a = source_ref->get_base ();
+      ty_b = target_ref->get_base ();
+      needs_reborrow = true;
+      expected_mutability = to_mutbl;
+
+      adjustments.push_back (
+	Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
+    }
+
+  // FIXME
+  // there is a bunch of code to ensure something is coerce able to a dyn trait
+  // we need to support but we need to support a few more lang items for that
+  // see:
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
+
+  const auto a = ty_a;
+  const auto b = ty_b;
+
+  bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
+  bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
+
+  if (expect_dyn && need_unsize)
+    {
+      bool bounds_compatible = b->bounds_compatible (*a, locus, true);
+      if (!bounds_compatible)
+	{
+	  unsafe_error = true;
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      // return the unsize coercion
+      TyTy::BaseType *result = b->clone ();
+      // result->set_ref (a->get_ref ());
+
+      // append a dyn coercion adjustment
+      adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result));
+
+      // reborrow if needed
+      if (needs_reborrow)
+	{
+	  TyTy::ReferenceType *reborrow
+	    = new TyTy::ReferenceType (source->get_ref (),
+				       TyTy::TyVar (result->get_ref ()),
+				       expected_mutability);
+
+	  Adjustment::AdjustmentType borrow_type
+	    = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
+						     : Adjustment::MUT_REF;
+	  adjustments.push_back (Adjustment (borrow_type, result, reborrow));
+	  result = reborrow;
+	}
+
+      return CoercionResult{adjustments, result};
+    }
+
+  adjustments.clear ();
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+bool
+TypeCoercionRules::select (const TyTy::BaseType &autoderefed)
+{
+  if (autoderefed.can_eq (expected, false))
+    {
+      try_result = CoercionResult{adjustments, autoderefed.clone ()};
+      return true;
+    }
+  return false;
+}
+
+/// Coercing a mutable reference to an immutable works, while
+/// coercing `&T` to `&mut T` should be forbidden.
+bool
+TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
+					  Mutability to_mutbl)
+{
+  return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl);
+}
+
+void
+TypeCoercionRules::mismatched_mutability_error (Location expr_locus,
+						Location lhs, Location rhs)
+{
+  if (!emit_errors)
+    return;
+
+  RichLocation r (expr_locus);
+  r.add_range (lhs);
+  r.add_range (rhs);
+  rust_error_at (r, "mismatched mutability");
+}
+
+void
+TypeCoercionRules::object_unsafe_error (Location expr_locus, Location lhs,
+					Location rhs)
+{
+  if (!emit_errors)
+    return;
+
+  RichLocation r (expr_locus);
+  r.add_range (lhs);
+  r.add_range (rhs);
+  rust_error_at (r, "unsafe unsize coercion");
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-coercion.h b/gcc/rust/typecheck/rust-coercion.h
new file mode 100644
index 00000000000..da28c7c5e1b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.h
@@ -0,0 +1,93 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_COERCION
+#define RUST_COERCION
+
+#include "rust-autoderef.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCoercionRules : protected AutoderefCycle
+{
+public:
+  struct CoercionResult
+  {
+    std::vector<Adjustment> adjustments;
+    TyTy::BaseType *tyty;
+
+    bool is_error ()
+    {
+      return tyty == nullptr || tyty->get_kind () == TyTy::TypeKind::ERROR;
+    }
+
+    static CoercionResult get_error () { return CoercionResult{{}, nullptr}; }
+  };
+
+  static CoercionResult Coerce (TyTy::BaseType *receiver,
+				TyTy::BaseType *expected, Location locus);
+
+  static CoercionResult TryCoerce (TyTy::BaseType *receiver,
+				   TyTy::BaseType *expected, Location locus);
+
+  CoercionResult coerce_unsafe_ptr (TyTy::BaseType *receiver,
+				    TyTy::PointerType *expected,
+				    Mutability mutability);
+
+  CoercionResult coerce_borrowed_pointer (TyTy::BaseType *receiver,
+					  TyTy::ReferenceType *expected,
+					  Mutability mutability);
+
+  CoercionResult coerce_unsized (TyTy::BaseType *receiver,
+				 TyTy::BaseType *expected, bool &unsafe_error);
+
+  static bool coerceable_mutability (Mutability from_mutbl,
+				     Mutability to_mutbl);
+
+  void mismatched_mutability_error (Location expr_locus, Location lhs,
+				    Location rhs);
+  void object_unsafe_error (Location expr_locus, Location lhs, Location rhs);
+
+protected:
+  TypeCoercionRules (TyTy::BaseType *expected, Location locus,
+		     bool emit_errors);
+
+  bool select (const TyTy::BaseType &autoderefed) override;
+
+  bool do_coercion (TyTy::BaseType *receiver);
+
+private:
+  // context info
+  Analysis::Mappings *mappings;
+  TypeCheckContext *context;
+
+  // search
+  TyTy::BaseType *expected;
+  Location locus;
+
+  // mutable fields
+  CoercionResult try_result;
+  bool emit_errors;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_COERCION
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc
new file mode 100644
index 00000000000..d45f0903478
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc
@@ -0,0 +1,263 @@
+// 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-dot-operator.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+MethodResolver::MethodResolver (bool autoderef_flag,
+				const HIR::PathIdentSegment &segment_name)
+  : AutoderefCycle (autoderef_flag), mappings (Analysis::Mappings::get ()),
+    context (TypeCheckContext::get ()), segment_name (segment_name),
+    try_result (MethodCandidate::get_error ())
+{}
+
+MethodCandidate
+MethodResolver::Probe (const TyTy::BaseType *receiver,
+		       const HIR::PathIdentSegment &segment_name,
+		       bool autoderef_flag)
+{
+  MethodResolver resolver (autoderef_flag, segment_name);
+  bool ok = resolver.cycle (receiver);
+  return ok ? resolver.try_result : MethodCandidate::get_error ();
+}
+
+void
+MethodResolver::try_hook (const TyTy::BaseType &r)
+{
+  const auto &specified_bounds = r.get_specified_bounds ();
+  predicate_items = get_predicate_items (segment_name, r, specified_bounds);
+}
+
+bool
+MethodResolver::select (const TyTy::BaseType &receiver)
+{
+  struct impl_item_candidate
+  {
+    HIR::Function *item;
+    HIR::ImplBlock *impl_block;
+    TyTy::FnType *ty;
+  };
+
+  // assemble inherent impl items
+  std::vector<impl_item_candidate> inherent_impl_fns;
+  mappings->iterate_impl_items (
+    [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
+      bool is_trait_impl = impl->has_trait_ref ();
+      if (is_trait_impl)
+	return true;
+
+      bool is_fn
+	= item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
+      if (!is_fn)
+	return true;
+
+      HIR::Function *func = static_cast<HIR::Function *> (item);
+      if (!func->is_method ())
+	return true;
+
+      bool name_matches
+	= func->get_function_name ().compare (segment_name.as_string ()) == 0;
+      if (!name_matches)
+	return true;
+
+      TyTy::BaseType *ty = nullptr;
+      if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
+	return true;
+      if (ty->get_kind () == TyTy::TypeKind::ERROR)
+	return true;
+
+      rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+      TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+      inherent_impl_fns.push_back ({func, impl, fnty});
+
+      return true;
+    });
+
+  struct trait_item_candidate
+  {
+    const HIR::TraitItemFunc *item;
+    const HIR::Trait *trait;
+    TyTy::FnType *ty;
+    const TraitReference *reference;
+    const TraitItemReference *item_ref;
+  };
+
+  std::vector<trait_item_candidate> trait_fns;
+  mappings->iterate_impl_blocks ([&] (HirId id,
+				      HIR::ImplBlock *impl) mutable -> bool {
+    bool is_trait_impl = impl->has_trait_ref ();
+    if (!is_trait_impl)
+      return true;
+
+    // look for impl implementation else lookup the associated trait item
+    for (auto &impl_item : impl->get_impl_items ())
+      {
+	bool is_fn = impl_item->get_impl_item_type ()
+		     == HIR::ImplItem::ImplItemType::FUNCTION;
+	if (!is_fn)
+	  continue;
+
+	HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
+	if (!func->is_method ())
+	  continue;
+
+	bool name_matches
+	  = func->get_function_name ().compare (segment_name.as_string ()) == 0;
+	if (!name_matches)
+	  continue;
+
+	TyTy::BaseType *ty = nullptr;
+	if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
+	  continue;
+	if (ty->get_kind () == TyTy::TypeKind::ERROR)
+	  continue;
+
+	rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+	TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+	inherent_impl_fns.push_back ({func, impl, fnty});
+	return true;
+      }
+
+    TraitReference *trait_ref
+      = TraitResolver::Resolve (*impl->get_trait_ref ().get ());
+    rust_assert (!trait_ref->is_error ());
+
+    auto item_ref
+      = trait_ref->lookup_trait_item (segment_name.as_string (),
+				      TraitItemReference::TraitItemType::FN);
+    if (item_ref->is_error ())
+      return true;
+
+    const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
+    HIR::TraitItem *item = item_ref->get_hir_trait_item ();
+    rust_assert (item->get_item_kind () == HIR::TraitItem::TraitItemKind::FUNC);
+    HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
+
+    TyTy::BaseType *ty = item_ref->get_tyty ();
+    rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+    TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+    trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
+    trait_fns.push_back (candidate);
+
+    return true;
+  });
+
+  // lookup specified bounds for an associated item
+  struct precdicate_candidate
+  {
+    TyTy::TypeBoundPredicateItem lookup;
+    TyTy::FnType *fntype;
+  };
+
+  for (auto impl_item : inherent_impl_fns)
+    {
+      TyTy::FnType *fn = impl_item.ty;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  PathProbeCandidate::ImplItemCandidate c{impl_item.item,
+						  impl_item.impl_block};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
+				fn, impl_item.item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  for (auto trait_item : trait_fns)
+    {
+      TyTy::FnType *fn = trait_item.ty;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
+						   trait_item.item_ref,
+						   nullptr};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
+				fn, trait_item.item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  for (const auto &predicate : predicate_items)
+    {
+      const TyTy::FnType *fn = predicate.fntype;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  const TraitReference *trait_ref
+	    = predicate.lookup.get_parent ()->get ();
+	  const TraitItemReference *trait_item
+	    = predicate.lookup.get_raw_item ();
+
+	  PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
+						   nullptr};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
+				fn->clone (), trait_item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+std::vector<MethodResolver::predicate_candidate>
+MethodResolver::get_predicate_items (
+  const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
+  const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
+{
+  std::vector<predicate_candidate> predicate_items;
+  for (auto &bound : specified_bounds)
+    {
+      TyTy::TypeBoundPredicateItem lookup
+	= bound.lookup_associated_item (segment_name.as_string ());
+      if (lookup.is_error ())
+	continue;
+
+      TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
+      if (ty->get_kind () == TyTy::TypeKind::FNDEF)
+	{
+	  TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+	  predicate_candidate candidate{lookup, fnty};
+	  predicate_items.push_back (candidate);
+	}
+    }
+
+  return predicate_items;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.h b/gcc/rust/typecheck/rust-hir-dot-operator.h
new file mode 100644
index 00000000000..750601a2d9e
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_DOT_OPERATOR
+#define RUST_HIR_DOT_OPERATOR
+
+#include "rust-hir-path-probe.h"
+
+namespace Rust {
+namespace Resolver {
+
+struct MethodCandidate
+{
+  PathProbeCandidate candidate;
+  std::vector<Adjustment> adjustments;
+
+  static MethodCandidate get_error ()
+  {
+    return {PathProbeCandidate::get_error (), {}};
+  }
+
+  bool is_error () const { return candidate.is_error (); }
+};
+
+class MethodResolver : protected AutoderefCycle
+{
+public:
+  struct predicate_candidate
+  {
+    TyTy::TypeBoundPredicateItem lookup;
+    TyTy::FnType *fntype;
+  };
+
+  static MethodCandidate Probe (const TyTy::BaseType *receiver,
+				const HIR::PathIdentSegment &segment_name,
+				bool autoderef_flag = false);
+
+  static std::vector<predicate_candidate> get_predicate_items (
+    const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
+    const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);
+
+protected:
+  MethodResolver (bool autoderef_flag,
+		  const HIR::PathIdentSegment &segment_name);
+
+  void try_hook (const TyTy::BaseType &r) override;
+
+  bool select (const TyTy::BaseType &receiver) override;
+
+private:
+  // context info
+  Analysis::Mappings *mappings;
+  TypeCheckContext *context;
+
+  // search
+  const HIR::PathIdentSegment &segment_name;
+  std::vector<MethodResolver::predicate_candidate> predicate_items;
+
+  // mutable fields
+  MethodCandidate try_result;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_DOT_OPERATOR
diff --git a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
new file mode 100644
index 00000000000..2890b54a00d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
@@ -0,0 +1,186 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
+#define RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ImplItemToName : private TypeCheckBase, private HIR::HIRImplVisitor
+{
+public:
+  static bool resolve (HIR::ImplItem *item, std::string &name_result)
+  {
+    ImplItemToName resolver (name_result);
+    item->accept_vis (resolver);
+    return resolver.ok;
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    ok = true;
+    result.assign (alias.get_new_type_name ());
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    ok = true;
+    result.assign (function.get_function_name ());
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    ok = true;
+    result.assign (constant.get_identifier ());
+  }
+
+private:
+  ImplItemToName (std::string &result)
+    : TypeCheckBase (), ok (false), result (result)
+  {}
+
+  bool ok;
+  std::string &result;
+};
+
+class OverlappingImplItemPass : public TypeCheckBase
+{
+public:
+  static void go ()
+  {
+    OverlappingImplItemPass pass;
+
+    // generate mappings
+    pass.mappings->iterate_impl_items (
+      [&] (HirId id, HIR::ImplItem *impl_item, HIR::ImplBlock *impl) -> bool {
+	// ignoring trait-impls might need thought later on
+	if (impl->has_trait_ref ())
+	  return true;
+
+	pass.process_impl_item (id, impl_item, impl);
+	return true;
+      });
+
+    pass.scan ();
+  }
+
+  void process_impl_item (HirId id, HIR::ImplItem *impl_item,
+			  HIR::ImplBlock *impl)
+  {
+    // lets make a mapping of impl-item Self type to (impl-item,name):
+    // {
+    //   impl-type -> [ (item, name), ... ]
+    // }
+
+    HirId impl_type_id = impl->get_type ()->get_mappings ().get_hirid ();
+    TyTy::BaseType *impl_type = nullptr;
+    bool ok = context->lookup_type (impl_type_id, &impl_type);
+    rust_assert (ok);
+
+    std::string impl_item_name;
+    ok = ImplItemToName::resolve (impl_item, impl_item_name);
+    rust_assert (ok);
+
+    std::pair<HIR::ImplItem *, std::string> elem (impl_item, impl_item_name);
+    impl_mappings[impl_type].insert (std::move (elem));
+  }
+
+  void scan ()
+  {
+    // we can now brute force the map looking for can_eq on each of the
+    // impl_items_types to look for possible colliding impl blocks;
+    for (auto it = impl_mappings.begin (); it != impl_mappings.end (); it++)
+      {
+	TyTy::BaseType *query = it->first;
+
+	for (auto iy = impl_mappings.begin (); iy != impl_mappings.end (); iy++)
+	  {
+	    TyTy::BaseType *candidate = iy->first;
+	    if (query == candidate)
+	      continue;
+
+	    if (query->can_eq (candidate, false))
+	      {
+		// we might be in the case that we have:
+		//
+		// *const T vs *const [T]
+		//
+		// so lets use an equality check when the
+		// candidates are both generic to be sure we dont emit a false
+		// positive
+
+		bool a = query->is_concrete ();
+		bool b = candidate->is_concrete ();
+		bool both_generic = !a && !b;
+		if (both_generic)
+		  {
+		    if (!query->is_equal (*candidate))
+		      continue;
+		  }
+
+		possible_collision (it->second, iy->second);
+	      }
+	  }
+      }
+  }
+
+  void possible_collision (
+    std::set<std::pair<HIR::ImplItem *, std::string> > query,
+    std::set<std::pair<HIR::ImplItem *, std::string> > candidate)
+  {
+    for (auto &q : query)
+      {
+	HIR::ImplItem *query_impl_item = q.first;
+	std::string query_impl_item_name = q.second;
+
+	for (auto &c : candidate)
+	  {
+	    HIR::ImplItem *candidate_impl_item = c.first;
+	    std::string candidate_impl_item_name = c.second;
+
+	    if (query_impl_item_name.compare (candidate_impl_item_name) == 0)
+	      collision_detected (query_impl_item, candidate_impl_item,
+				  candidate_impl_item_name);
+	  }
+      }
+  }
+
+  void collision_detected (HIR::ImplItem *query, HIR::ImplItem *dup,
+			   const std::string &name)
+  {
+    RichLocation r (dup->get_locus ());
+    r.add_range (query->get_locus ());
+    rust_error_at (r, "duplicate definitions with name %s", name.c_str ());
+  }
+
+private:
+  OverlappingImplItemPass () : TypeCheckBase () {}
+
+  std::map<TyTy::BaseType *,
+	   std::set<std::pair<HIR::ImplItem *, std::string> > >
+    impl_mappings;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h
new file mode 100644
index 00000000000..bd4f91e49bf
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-path-probe.h
@@ -0,0 +1,540 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_PATH_PROBE_H
+#define RUST_HIR_PATH_PROBE_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-tyty.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-type-bounds.h"
+
+namespace Rust {
+namespace Resolver {
+
+struct PathProbeCandidate
+{
+  enum CandidateType
+  {
+    ERROR,
+
+    ENUM_VARIANT,
+
+    IMPL_CONST,
+    IMPL_TYPE_ALIAS,
+    IMPL_FUNC,
+
+    TRAIT_ITEM_CONST,
+    TRAIT_TYPE_ALIAS,
+    TRAIT_FUNC,
+  };
+
+  struct EnumItemCandidate
+  {
+    const TyTy::ADTType *parent;
+    const TyTy::VariantDef *variant;
+  };
+
+  struct ImplItemCandidate
+  {
+    HIR::ImplItem *impl_item;
+    HIR::ImplBlock *parent;
+  };
+
+  struct TraitItemCandidate
+  {
+    const TraitReference *trait_ref;
+    const TraitItemReference *item_ref;
+    HIR::ImplBlock *impl;
+  };
+
+  CandidateType type;
+  TyTy::BaseType *ty;
+  Location locus;
+  union Candidate
+  {
+    EnumItemCandidate enum_field;
+    ImplItemCandidate impl;
+    TraitItemCandidate trait;
+
+    Candidate (EnumItemCandidate enum_field) : enum_field (enum_field) {}
+    Candidate (ImplItemCandidate impl) : impl (impl) {}
+    Candidate (TraitItemCandidate trait) : trait (trait) {}
+  } item;
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      EnumItemCandidate enum_field)
+    : type (type), ty (ty), item (enum_field)
+  {}
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      ImplItemCandidate impl)
+    : type (type), ty (ty), item (impl)
+  {}
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      TraitItemCandidate trait)
+    : type (type), ty (ty), item (trait)
+  {}
+
+  std::string as_string () const
+  {
+    return "PathProbe candidate TODO - as_string";
+  }
+
+  bool is_enum_candidate () const { return type == ENUM_VARIANT; }
+
+  bool is_impl_candidate () const
+  {
+    return type == IMPL_CONST || type == IMPL_TYPE_ALIAS || type == IMPL_FUNC;
+  }
+
+  bool is_trait_candidate () const
+  {
+    return type == TRAIT_ITEM_CONST || type == TRAIT_TYPE_ALIAS
+	   || type == TRAIT_FUNC;
+  }
+
+  bool is_full_trait_item_candidate () const
+  {
+    return is_trait_candidate () && item.trait.impl == nullptr;
+  }
+
+  static PathProbeCandidate get_error ()
+  {
+    return PathProbeCandidate (ERROR, nullptr, Location (),
+			       ImplItemCandidate{nullptr, nullptr});
+  }
+
+  bool is_error () const { return type == ERROR; }
+};
+
+class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor
+{
+public:
+  static std::vector<PathProbeCandidate>
+  Probe (const TyTy::BaseType *receiver,
+	 const HIR::PathIdentSegment &segment_name, bool probe_impls,
+	 bool probe_bounds, bool ignore_mandatory_trait_items,
+	 DefId specific_trait_id = UNKNOWN_DEFID)
+  {
+    PathProbeType probe (receiver, segment_name, specific_trait_id);
+    if (probe_impls)
+      {
+	if (receiver->get_kind () == TyTy::TypeKind::ADT)
+	  {
+	    const TyTy::ADTType *adt
+	      = static_cast<const TyTy::ADTType *> (receiver);
+	    if (adt->is_enum ())
+	      probe.process_enum_item_for_candiates (adt);
+	  }
+
+	probe.process_impl_items_for_candidates ();
+      }
+
+    if (!probe_bounds)
+      return probe.candidates;
+
+    if (!probe.is_reciever_generic ())
+      {
+	std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> probed_bounds
+	  = TypeBoundsProbe::Probe (receiver);
+	for (auto &candidate : probed_bounds)
+	  {
+	    const TraitReference *trait_ref = candidate.first;
+	    if (specific_trait_id != UNKNOWN_DEFID)
+	      {
+		if (trait_ref->get_mappings ().get_defid ()
+		    != specific_trait_id)
+		  continue;
+	      }
+
+	    HIR::ImplBlock *impl = candidate.second;
+	    probe.process_associated_trait_for_candidates (
+	      trait_ref, impl, ignore_mandatory_trait_items);
+	  }
+      }
+
+    for (const TyTy::TypeBoundPredicate &predicate :
+	 receiver->get_specified_bounds ())
+      {
+	const TraitReference *trait_ref = predicate.get ();
+	if (specific_trait_id != UNKNOWN_DEFID)
+	  {
+	    if (trait_ref->get_mappings ().get_defid () != specific_trait_id)
+	      continue;
+	  }
+
+	probe.process_predicate_for_candidates (predicate,
+						ignore_mandatory_trait_items);
+      }
+
+    return probe.candidates;
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    Identifier name = alias.get_new_type_name ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = alias.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&alias,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS, ty,
+	  alias.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    Identifier name = constant.get_identifier ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = constant.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&constant,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_CONST, ty,
+	  constant.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    Identifier name = function.get_function_name ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = function.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&function,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_FUNC, ty,
+	  function.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+protected:
+  void process_enum_item_for_candiates (const TyTy::ADTType *adt)
+  {
+    if (specific_trait_id != UNKNOWN_DEFID)
+      return;
+
+    TyTy::VariantDef *v;
+    if (!adt->lookup_variant (search.as_string (), &v))
+      return;
+
+    PathProbeCandidate::EnumItemCandidate enum_item_candidate{adt, v};
+    PathProbeCandidate candidate{
+      PathProbeCandidate::CandidateType::ENUM_VARIANT, receiver->clone (),
+      mappings->lookup_location (adt->get_ty_ref ()), enum_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+  void process_impl_items_for_candidates ()
+  {
+    mappings->iterate_impl_items ([&] (HirId id, HIR::ImplItem *item,
+				       HIR::ImplBlock *impl) mutable -> bool {
+      process_impl_item_candidate (id, item, impl);
+      return true;
+    });
+  }
+
+  void process_impl_item_candidate (HirId id, HIR::ImplItem *item,
+				    HIR::ImplBlock *impl)
+  {
+    current_impl = impl;
+    HirId impl_ty_id = impl->get_type ()->get_mappings ().get_hirid ();
+    TyTy::BaseType *impl_block_ty = nullptr;
+    if (!context->lookup_type (impl_ty_id, &impl_block_ty))
+      return;
+
+    if (!receiver->can_eq (impl_block_ty, false))
+      {
+	if (!impl_block_ty->can_eq (receiver, false))
+	  return;
+      }
+
+    // lets visit the impl_item
+    item->accept_vis (*this);
+  }
+
+  void
+  process_associated_trait_for_candidates (const TraitReference *trait_ref,
+					   HIR::ImplBlock *impl,
+					   bool ignore_mandatory_trait_items)
+  {
+    const TraitItemReference *trait_item_ref = nullptr;
+    if (!trait_ref->lookup_trait_item (search.as_string (), &trait_item_ref))
+      return;
+
+    bool trait_item_needs_implementation = !trait_item_ref->is_optional ();
+    if (ignore_mandatory_trait_items && trait_item_needs_implementation)
+      return;
+
+    PathProbeCandidate::CandidateType candidate_type;
+    switch (trait_item_ref->get_trait_item_type ())
+      {
+      case TraitItemReference::TraitItemType::FN:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
+	break;
+      case TraitItemReference::TraitItemType::CONST:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
+	break;
+      case TraitItemReference::TraitItemType::TYPE:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+	break;
+
+      case TraitItemReference::TraitItemType::ERROR:
+      default:
+	gcc_unreachable ();
+	break;
+      }
+
+    TyTy::BaseType *trait_item_tyty = trait_item_ref->get_tyty ();
+
+    // we can substitute the Self with the receiver here
+    if (trait_item_tyty->get_kind () == TyTy::TypeKind::FNDEF)
+      {
+	TyTy::FnType *fn = static_cast<TyTy::FnType *> (trait_item_tyty);
+	TyTy::SubstitutionParamMapping *param = nullptr;
+	for (auto &param_mapping : fn->get_substs ())
+	  {
+	    const HIR::TypeParam &type_param
+	      = param_mapping.get_generic_param ();
+	    if (type_param.get_type_representation ().compare ("Self") == 0)
+	      {
+		param = &param_mapping;
+		break;
+	      }
+	  }
+	rust_assert (param != nullptr);
+
+	std::vector<TyTy::SubstitutionArg> mappings;
+	mappings.push_back (TyTy::SubstitutionArg (param, receiver->clone ()));
+
+	Location locus; // FIXME
+	TyTy::SubstitutionArgumentMappings args (std::move (mappings), locus);
+	trait_item_tyty = SubstMapperInternal::Resolve (trait_item_tyty, args);
+      }
+
+    PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
+								trait_item_ref,
+								impl};
+
+    PathProbeCandidate candidate{candidate_type, trait_item_tyty,
+				 trait_ref->get_locus (), trait_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+  void
+  process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate,
+				    bool ignore_mandatory_trait_items)
+  {
+    const TraitReference *trait_ref = predicate.get ();
+
+    TyTy::TypeBoundPredicateItem item
+      = predicate.lookup_associated_item (search.as_string ());
+    if (item.is_error ())
+      return;
+
+    if (ignore_mandatory_trait_items && item.needs_implementation ())
+      return;
+
+    const TraitItemReference *trait_item_ref = item.get_raw_item ();
+    PathProbeCandidate::CandidateType candidate_type;
+    switch (trait_item_ref->get_trait_item_type ())
+      {
+      case TraitItemReference::TraitItemType::FN:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
+	break;
+      case TraitItemReference::TraitItemType::CONST:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
+	break;
+      case TraitItemReference::TraitItemType::TYPE:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+	break;
+
+      case TraitItemReference::TraitItemType::ERROR:
+      default:
+	gcc_unreachable ();
+	break;
+      }
+
+    TyTy::BaseType *trait_item_tyty = item.get_tyty_for_receiver (receiver);
+    PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
+								trait_item_ref,
+								nullptr};
+    PathProbeCandidate candidate{candidate_type, trait_item_tyty,
+				 trait_item_ref->get_locus (),
+				 trait_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+protected:
+  PathProbeType (const TyTy::BaseType *receiver,
+		 const HIR::PathIdentSegment &query, DefId specific_trait_id)
+    : TypeCheckBase (), receiver (receiver), search (query),
+      current_impl (nullptr), specific_trait_id (specific_trait_id)
+  {}
+
+  std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>>
+  union_bounds (
+    const std::vector<std::pair</*const*/ TraitReference *, HIR::ImplBlock *>>
+      a,
+    const std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> b)
+    const
+  {
+    std::map<DefId, std::pair<const TraitReference *, HIR::ImplBlock *>> mapper;
+    for (auto &ref : a)
+      {
+	mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
+      }
+    for (auto &ref : b)
+      {
+	mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
+      }
+
+    std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> union_set;
+    for (auto it = mapper.begin (); it != mapper.end (); it++)
+      {
+	union_set.push_back ({it->second.first, it->second.second});
+      }
+    return union_set;
+  }
+
+  bool is_reciever_generic () const
+  {
+    const TyTy::BaseType *root = receiver->get_root ();
+    bool receiver_is_type_param = root->get_kind () == TyTy::TypeKind::PARAM;
+    bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC;
+    return receiver_is_type_param || receiver_is_dyn;
+  }
+
+  const TyTy::BaseType *receiver;
+  const HIR::PathIdentSegment &search;
+  std::vector<PathProbeCandidate> candidates;
+  HIR::ImplBlock *current_impl;
+  DefId specific_trait_id;
+};
+
+class ReportMultipleCandidateError : private TypeCheckBase,
+				     private HIR::HIRImplVisitor
+{
+public:
+  static void Report (std::vector<PathProbeCandidate> &candidates,
+		      const HIR::PathIdentSegment &query, Location query_locus)
+  {
+    RichLocation r (query_locus);
+    ReportMultipleCandidateError visitor (r);
+    for (auto &c : candidates)
+      {
+	switch (c.type)
+	  {
+	  case PathProbeCandidate::CandidateType::ERROR:
+	  case PathProbeCandidate::CandidateType::ENUM_VARIANT:
+	    gcc_unreachable ();
+	    break;
+
+	  case PathProbeCandidate::CandidateType::IMPL_CONST:
+	  case PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS:
+	  case PathProbeCandidate::CandidateType::IMPL_FUNC:
+	    c.item.impl.impl_item->accept_vis (visitor);
+	    break;
+
+	  case PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST:
+	  case PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS:
+	  case PathProbeCandidate::CandidateType::TRAIT_FUNC:
+	    r.add_range (c.item.trait.item_ref->get_locus ());
+	    break;
+	  }
+      }
+
+    rust_error_at (r, "multiple applicable items in scope for: %s",
+		   query.as_string ().c_str ());
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    r.add_range (alias.get_locus ());
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    r.add_range (constant.get_locus ());
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    r.add_range (function.get_locus ());
+  }
+
+private:
+  ReportMultipleCandidateError (RichLocation &r) : TypeCheckBase (), r (r) {}
+
+  RichLocation &r;
+};
+
+class PathProbeImplTrait : public PathProbeType
+{
+public:
+  static std::vector<PathProbeCandidate>
+  Probe (const TyTy::BaseType *receiver,
+	 const HIR::PathIdentSegment &segment_name,
+	 const TraitReference *trait_reference)
+  {
+    PathProbeImplTrait probe (receiver, segment_name, trait_reference);
+    // iterate all impls for this trait and receiver
+    // then search for possible candidates using base class behaviours
+    probe.process_trait_impl_items_for_candidates ();
+    return probe.candidates;
+  }
+
+private:
+  void process_trait_impl_items_for_candidates ();
+
+  PathProbeImplTrait (const TyTy::BaseType *receiver,
+		      const HIR::PathIdentSegment &query,
+		      const TraitReference *trait_reference)
+    : PathProbeType (receiver, query, UNKNOWN_DEFID),
+      trait_reference (trait_reference)
+  {}
+
+  const TraitReference *trait_reference;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_PATH_PROBE_H
diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h
new file mode 100644
index 00000000000..6eec461e8a5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-ref.h
@@ -0,0 +1,472 @@
+// Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TRAIT_REF_H
+#define RUST_HIR_TRAIT_REF_H
+
+#include "rust-hir-full.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-type-check-util.h"
+
+namespace Rust {
+namespace Resolver {
+
+// Data Objects for the associated trait items in a structure we can work with
+// https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html
+class TypeCheckContext;
+class TraitItemReference
+{
+public:
+  enum TraitItemType
+  {
+    FN,
+    CONST,
+    TYPE,
+    ERROR
+  };
+
+  TraitItemReference (std::string identifier, bool optional, TraitItemType type,
+		      HIR::TraitItem *hir_trait_item, TyTy::BaseType *self,
+		      std::vector<TyTy::SubstitutionParamMapping> substitutions,
+		      Location locus);
+
+  TraitItemReference (TraitItemReference const &other);
+
+  TraitItemReference &operator= (TraitItemReference const &other);
+
+  static TraitItemReference error ()
+  {
+    return TraitItemReference ("", false, ERROR, nullptr, nullptr, {},
+			       Location ());
+  }
+
+  static TraitItemReference &error_node ()
+  {
+    static TraitItemReference error = TraitItemReference::error ();
+    return error;
+  }
+
+  bool is_error () const { return type == ERROR; }
+
+  std::string as_string () const
+  {
+    return "(" + trait_item_type_as_string (type) + " " + identifier + " "
+	   + ")";
+  }
+
+  static std::string trait_item_type_as_string (TraitItemType ty)
+  {
+    switch (ty)
+      {
+      case FN:
+	return "FN";
+      case CONST:
+	return "CONST";
+      case TYPE:
+	return "TYPE";
+      case ERROR:
+	return "ERROR";
+      }
+    return "ERROR";
+  }
+
+  bool is_optional () const { return optional_flag; }
+
+  std::string get_identifier () const { return identifier; }
+
+  TraitItemType get_trait_item_type () const { return type; }
+
+  HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; }
+
+  Location get_locus () const { return locus; }
+
+  const Analysis::NodeMapping get_mappings () const
+  {
+    return hir_trait_item->get_mappings ();
+  }
+
+  TyTy::BaseType *get_tyty () const
+  {
+    rust_assert (hir_trait_item != nullptr);
+
+    switch (type)
+      {
+      case CONST:
+	return get_type_from_constant (
+	  static_cast</*const*/ HIR::TraitItemConst &> (*hir_trait_item));
+	break;
+
+      case TYPE:
+	return get_type_from_typealias (
+	  static_cast</*const*/ HIR::TraitItemType &> (*hir_trait_item));
+
+      case FN:
+	return get_type_from_fn (
+	  static_cast</*const*/ HIR::TraitItemFunc &> (*hir_trait_item));
+	break;
+
+      default:
+	return get_error ();
+      }
+
+    gcc_unreachable ();
+    return get_error ();
+  }
+
+  Analysis::NodeMapping get_parent_trait_mappings () const;
+
+  // this is called when the trait is completed resolution and gives the items a
+  // chance to run their specific type resolution passes. If we call their
+  // resolution on construction it can lead to a case where the trait being
+  // resolved recursively trying to resolve the trait itself infinitely since
+  // the trait will not be stored in its own map yet
+  void on_resolved ();
+
+  void associated_type_set (TyTy::BaseType *ty) const;
+
+  void associated_type_reset () const;
+
+  bool is_object_safe () const;
+
+private:
+  TyTy::ErrorType *get_error () const
+  {
+    return new TyTy::ErrorType (get_mappings ().get_hirid ());
+  }
+
+  TyTy::BaseType *get_type_from_typealias (/*const*/
+					   HIR::TraitItemType &type) const;
+
+  TyTy::BaseType *
+  get_type_from_constant (/*const*/ HIR::TraitItemConst &constant) const;
+
+  TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const;
+
+  bool is_item_resolved () const;
+  void resolve_item (HIR::TraitItemType &type);
+  void resolve_item (HIR::TraitItemConst &constant);
+  void resolve_item (HIR::TraitItemFunc &func);
+
+  std::string identifier;
+  bool optional_flag;
+  TraitItemType type;
+  HIR::TraitItem *hir_trait_item;
+  std::vector<TyTy::SubstitutionParamMapping> inherited_substitutions;
+  Location locus;
+
+  TyTy::BaseType
+    *self; // this is the implict Self TypeParam required for methods
+  Resolver::TypeCheckContext *context;
+};
+
+// this wraps up the HIR::Trait so we can do analysis on it
+
+class TraitReference
+{
+public:
+  TraitReference (const HIR::Trait *hir_trait_ref,
+		  std::vector<TraitItemReference> item_refs,
+		  std::vector<const TraitReference *> super_traits,
+		  std::vector<TyTy::SubstitutionParamMapping> substs)
+    : hir_trait_ref (hir_trait_ref), item_refs (item_refs),
+      super_traits (super_traits)
+  {
+    trait_substs.clear ();
+    trait_substs.reserve (substs.size ());
+    for (const auto &p : substs)
+      trait_substs.push_back (p.clone ());
+  }
+
+  TraitReference (TraitReference const &other)
+    : hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs),
+      super_traits (other.super_traits)
+  {
+    trait_substs.clear ();
+    trait_substs.reserve (other.trait_substs.size ());
+    for (const auto &p : other.trait_substs)
+      trait_substs.push_back (p.clone ());
+  }
+
+  TraitReference &operator= (TraitReference const &other)
+  {
+    hir_trait_ref = other.hir_trait_ref;
+    item_refs = other.item_refs;
+    super_traits = other.super_traits;
+
+    trait_substs.clear ();
+    trait_substs.reserve (other.trait_substs.size ());
+    for (const auto &p : other.trait_substs)
+      trait_substs.push_back (p.clone ());
+
+    return *this;
+  }
+
+  TraitReference (TraitReference &&other) = default;
+  TraitReference &operator= (TraitReference &&other) = default;
+
+  static TraitReference error ()
+  {
+    return TraitReference (nullptr, {}, {}, {});
+  }
+
+  bool is_error () const { return hir_trait_ref == nullptr; }
+
+  static TraitReference &error_node ()
+  {
+    static TraitReference trait_error_node = TraitReference::error ();
+    return trait_error_node;
+  }
+
+  Location get_locus () const { return hir_trait_ref->get_locus (); }
+
+  std::string get_name () const
+  {
+    rust_assert (!is_error ());
+    return hir_trait_ref->get_name ();
+  }
+
+  std::string as_string () const
+  {
+    if (is_error ())
+      return "<trait-ref-error-node>";
+
+    std::string item_buf;
+    for (auto &item : item_refs)
+      {
+	item_buf += item.as_string () + ", ";
+      }
+    return "HIR Trait: " + get_name () + "->"
+	   + hir_trait_ref->get_mappings ().as_string () + " [" + item_buf
+	   + "]";
+  }
+
+  const HIR::Trait *get_hir_trait_ref () const { return hir_trait_ref; }
+
+  const Analysis::NodeMapping &get_mappings () const
+  {
+    return hir_trait_ref->get_mappings ();
+  }
+
+  DefId get_defid () const { return get_mappings ().get_defid (); }
+
+  bool lookup_hir_trait_item (const HIR::TraitItem &item,
+			      TraitItemReference **ref)
+  {
+    return lookup_trait_item (item.trait_identifier (), ref);
+  }
+
+  bool lookup_trait_item (const std::string &ident, TraitItemReference **ref)
+  {
+    for (auto &item : item_refs)
+      {
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_trait_item_by_type (const std::string &ident,
+				  TraitItemReference::TraitItemType type,
+				  TraitItemReference **ref)
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_trait_item_by_type (const std::string &ident,
+				  TraitItemReference::TraitItemType type,
+				  const TraitItemReference **ref) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_hir_trait_item (const HIR::TraitItem &item,
+			      const TraitItemReference **ref) const
+  {
+    return lookup_trait_item (item.trait_identifier (), ref);
+  }
+
+  bool lookup_trait_item (const std::string &ident,
+			  const TraitItemReference **ref) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  const TraitItemReference *
+  lookup_trait_item (const std::string &ident,
+		     TraitItemReference::TraitItemType type) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  return &item;
+      }
+    return &TraitItemReference::error_node ();
+  }
+
+  size_t size () const { return item_refs.size (); }
+
+  const std::vector<TraitItemReference> &get_trait_items () const
+  {
+    return item_refs;
+  }
+
+  void on_resolved ()
+  {
+    for (auto &item : item_refs)
+      {
+	item.on_resolved ();
+      }
+  }
+
+  void clear_associated_types ()
+  {
+    for (auto &item : item_refs)
+      {
+	bool is_assoc_type = item.get_trait_item_type ()
+			     == TraitItemReference::TraitItemType::TYPE;
+	if (is_assoc_type)
+	  item.associated_type_reset ();
+      }
+  }
+
+  bool is_equal (const TraitReference &other) const
+  {
+    DefId this_id = get_mappings ().get_defid ();
+    DefId other_id = other.get_mappings ().get_defid ();
+    return this_id == other_id;
+  }
+
+  const std::vector<const TraitReference *> get_super_traits () const
+  {
+    return super_traits;
+  }
+
+  bool is_object_safe (bool emit_error, Location locus) const
+  {
+    // https: // doc.rust-lang.org/reference/items/traits.html#object-safety
+    std::vector<const TraitReference *> non_object_super_traits;
+    for (auto &item : super_traits)
+      {
+	if (!item->is_object_safe (false, Location ()))
+	  non_object_super_traits.push_back (item);
+      }
+
+    std::vector<const Resolver::TraitItemReference *> non_object_safe_items;
+    for (auto &item : get_trait_items ())
+      {
+	if (!item.is_object_safe ())
+	  non_object_safe_items.push_back (&item);
+      }
+
+    bool is_safe
+      = non_object_super_traits.empty () && non_object_safe_items.empty ();
+    if (emit_error && !is_safe)
+      {
+	RichLocation r (locus);
+	for (auto &item : non_object_super_traits)
+	  r.add_range (item->get_locus ());
+	for (auto &item : non_object_safe_items)
+	  r.add_range (item->get_locus ());
+
+	rust_error_at (r, "trait bound is not object safe");
+      }
+
+    return is_safe;
+  }
+
+  bool trait_has_generics () const { return !trait_substs.empty (); }
+
+  std::vector<TyTy::SubstitutionParamMapping> get_trait_substs () const
+  {
+    return trait_substs;
+  }
+
+private:
+  const HIR::Trait *hir_trait_ref;
+  std::vector<TraitItemReference> item_refs;
+  std::vector<const TraitReference *> super_traits;
+  std::vector<TyTy::SubstitutionParamMapping> trait_substs;
+};
+
+class AssociatedImplTrait
+{
+public:
+  AssociatedImplTrait (TraitReference *trait, HIR::ImplBlock *impl,
+		       TyTy::BaseType *self,
+		       Resolver::TypeCheckContext *context)
+    : trait (trait), impl (impl), self (self), context (context)
+  {}
+
+  TraitReference *get_trait () { return trait; }
+
+  HIR::ImplBlock *get_impl_block () { return impl; }
+
+  TyTy::BaseType *get_self () { return self; }
+
+  void setup_associated_types (const TyTy::BaseType *self,
+			       const TyTy::TypeBoundPredicate &bound);
+
+  void reset_associated_types ();
+
+private:
+  TraitReference *trait;
+  HIR::ImplBlock *impl;
+  TyTy::BaseType *self;
+  Resolver::TypeCheckContext *context;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TRAIT_REF_H
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
new file mode 100644
index 00000000000..5ad9540868c
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
@@ -0,0 +1,599 @@
+// Copyright (C) 2021-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-trait-resolve.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemType &type)
+{
+  // create trait-item-ref
+  Location locus = type.get_locus ();
+  bool is_optional = false;
+  std::string identifier = type.get_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::TYPE, &type,
+				 self, substitutions, locus);
+}
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemConst &cst)
+{
+  // create trait-item-ref
+  Location locus = cst.get_locus ();
+  bool is_optional = cst.has_expr ();
+  std::string identifier = cst.get_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::CONST, &cst,
+				 self, substitutions, locus);
+}
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemFunc &fn)
+{
+  // create trait-item-ref
+  Location locus = fn.get_locus ();
+  bool is_optional = fn.has_block_defined ();
+  std::string identifier = fn.get_decl ().get_function_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::FN, &fn,
+				 self, std::move (substitutions), locus);
+}
+
+ResolveTraitItemToRef::ResolveTraitItemToRef (
+  TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> &&substitutions)
+  : TypeCheckBase (), resolved (TraitItemReference::error ()), self (self),
+    substitutions (std::move (substitutions))
+{}
+
+// TraitItemReference items
+
+TraitReference *
+TraitResolver::Resolve (HIR::TypePath &path)
+{
+  TraitResolver resolver;
+  return resolver.resolve_path (path);
+}
+
+TraitReference *
+TraitResolver::Resolve (HIR::Trait &trait)
+{
+  TraitResolver resolver;
+  return resolver.resolve_trait (&trait);
+}
+
+TraitReference *
+TraitResolver::Lookup (HIR::TypePath &path)
+{
+  TraitResolver resolver;
+  return resolver.lookup_path (path);
+}
+
+TraitResolver::TraitResolver ()
+  : TypeCheckBase (), resolved_trait_reference (nullptr)
+{}
+
+TraitReference *
+TraitResolver::resolve_path (HIR::TypePath &path)
+{
+  NodeId ref;
+  if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (),
+				       &ref))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to node-id");
+      return &TraitReference::error_node ();
+    }
+
+  HirId hir_node = UNKNOWN_HIRID;
+  if (!mappings->lookup_node_to_hir (ref, &hir_node))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to hir-id");
+      return &TraitReference::error_node ();
+    }
+
+  HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node);
+
+  rust_assert (resolved_item != nullptr);
+  resolved_item->accept_vis (*this);
+  rust_assert (resolved_trait_reference != nullptr);
+
+  return resolve_trait (resolved_trait_reference);
+}
+
+TraitReference *
+TraitResolver::resolve_trait (HIR::Trait *trait_reference)
+{
+  TraitReference *tref = &TraitReference::error_node ();
+  if (context->lookup_trait_reference (
+	trait_reference->get_mappings ().get_defid (), &tref))
+    {
+      return tref;
+    }
+
+  TyTy::BaseType *self = nullptr;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  for (auto &generic_param : trait_reference->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);
+
+	    auto &typaram = static_cast<HIR::TypeParam &> (*generic_param);
+	    substitutions.push_back (
+	      TyTy::SubstitutionParamMapping (typaram, param_type));
+
+	    if (typaram.get_type_representation ().compare ("Self") == 0)
+	      {
+		self = param_type;
+	      }
+	  }
+	  break;
+	}
+    }
+  rust_assert (self != nullptr);
+
+  // Check if there is a super-trait, and apply this bound to the Self
+  // TypeParam
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+
+  // copy the substitition mappings
+  std::vector<TyTy::SubstitutionParamMapping> self_subst_copy;
+  for (auto &sub : substitutions)
+    self_subst_copy.push_back (sub.clone ());
+
+  // They also inherit themselves as a bound this enables a trait item to
+  // reference other Self::trait_items
+  auto self_hrtb
+    = TyTy::TypeBoundPredicate (trait_reference->get_mappings ().get_defid (),
+				std::move (self_subst_copy),
+				trait_reference->get_locus ());
+  specified_bounds.push_back (self_hrtb);
+
+  // look for any
+  std::vector<const TraitReference *> super_traits;
+  if (trait_reference->has_type_param_bounds ())
+    {
+      for (auto &bound : trait_reference->get_type_param_bounds ())
+	{
+	  if (bound->get_bound_type ()
+	      == HIR::TypeParamBound::BoundType::TRAITBOUND)
+	    {
+	      HIR::TraitBound *b
+		= static_cast<HIR::TraitBound *> (bound.get ());
+
+	      // FIXME this might be recursive we need a check for that
+	      auto predicate = get_predicate_from_bound (b->get_path ());
+	      specified_bounds.push_back (predicate);
+	      super_traits.push_back (predicate.get ());
+	    }
+	}
+    }
+  self->inherit_bounds (specified_bounds);
+
+  std::vector<TraitItemReference> item_refs;
+  for (auto &item : trait_reference->get_trait_items ())
+    {
+      // make a copy of the substs
+      std::vector<TyTy::SubstitutionParamMapping> item_subst;
+      for (auto &sub : substitutions)
+	item_subst.push_back (sub.clone ());
+
+      TraitItemReference trait_item_ref
+	= ResolveTraitItemToRef::Resolve (*item.get (), self,
+					  std::move (item_subst));
+      item_refs.push_back (std::move (trait_item_ref));
+    }
+
+  TraitReference trait_object (trait_reference, item_refs,
+			       std::move (super_traits),
+			       std::move (substitutions));
+  context->insert_trait_reference (
+    trait_reference->get_mappings ().get_defid (), std::move (trait_object));
+
+  tref = &TraitReference::error_node ();
+  bool ok = context->lookup_trait_reference (
+    trait_reference->get_mappings ().get_defid (), &tref);
+  rust_assert (ok);
+
+  // hook to allow the trait to resolve its optional item blocks, we cant
+  // resolve the blocks of functions etc because it can end up in a recursive
+  // loop of trying to resolve traits as required by the types
+  tref->on_resolved ();
+
+  return tref;
+}
+
+TraitReference *
+TraitResolver::lookup_path (HIR::TypePath &path)
+{
+  NodeId ref;
+  if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (),
+				       &ref))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to node-id");
+      return &TraitReference::error_node ();
+    }
+
+  HirId hir_node = UNKNOWN_HIRID;
+  if (!mappings->lookup_node_to_hir (ref, &hir_node))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to hir-id");
+      return &TraitReference::error_node ();
+    }
+
+  HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node);
+
+  rust_assert (resolved_item != nullptr);
+  resolved_item->accept_vis (*this);
+  rust_assert (resolved_trait_reference != nullptr);
+
+  TraitReference *tref = &TraitReference::error_node ();
+  if (context->lookup_trait_reference (
+	resolved_trait_reference->get_mappings ().get_defid (), &tref))
+    {
+      return tref;
+    }
+  return &TraitReference::error_node ();
+}
+
+void
+TraitItemReference::on_resolved ()
+{
+  switch (type)
+    {
+    case CONST:
+      resolve_item (static_cast<HIR::TraitItemConst &> (*hir_trait_item));
+      break;
+
+    case TYPE:
+      resolve_item (static_cast<HIR::TraitItemType &> (*hir_trait_item));
+      break;
+
+    case FN:
+      resolve_item (static_cast<HIR::TraitItemFunc &> (*hir_trait_item));
+      break;
+
+    default:
+      break;
+    }
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemType &type)
+{
+  TyTy::BaseType *ty
+    = new TyTy::PlaceholderType (type.get_name (),
+				 type.get_mappings ().get_hirid ());
+  context->insert_type (type.get_mappings (), ty);
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemConst &constant)
+{
+  // TODO
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemFunc &func)
+{
+  if (!is_optional ())
+    return;
+
+  TyTy::BaseType *item_tyty = get_tyty ();
+  if (item_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  // check the block and return types
+  rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+
+  // need to get the return type from this
+  TyTy::FnType *resolved_fn_type = static_cast<TyTy::FnType *> (item_tyty);
+  auto expected_ret_tyty = resolved_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (&func), expected_ret_tyty);
+
+  auto block_expr_ty = TypeCheckExpr::Resolve (func.get_block_expr ().get ());
+
+  context->pop_return_type ();
+
+  if (block_expr_ty->get_kind () != TyTy::NEVER)
+    expected_ret_tyty->unify (block_expr_ty);
+}
+
+void
+TraitItemReference::associated_type_set (TyTy::BaseType *ty) const
+{
+  rust_assert (get_trait_item_type () == TraitItemType::TYPE);
+
+  TyTy::BaseType *item_ty = get_tyty ();
+  rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER);
+  TyTy::PlaceholderType *placeholder
+    = static_cast<TyTy::PlaceholderType *> (item_ty);
+
+  placeholder->set_associated_type (ty->get_ty_ref ());
+}
+
+void
+TraitItemReference::associated_type_reset () const
+{
+  rust_assert (get_trait_item_type () == TraitItemType::TYPE);
+
+  TyTy::BaseType *item_ty = get_tyty ();
+  rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER);
+  TyTy::PlaceholderType *placeholder
+    = static_cast<TyTy::PlaceholderType *> (item_ty);
+
+  placeholder->clear_associated_type ();
+}
+
+void
+AssociatedImplTrait::setup_associated_types (
+  const TyTy::BaseType *self, const TyTy::TypeBoundPredicate &bound)
+{
+  // compute the constrained impl block generic arguments based on self and the
+  // higher ranked trait bound
+  TyTy::BaseType *receiver = self->clone ();
+
+  // impl<Y> SliceIndex<[Y]> for Range<usize>
+  // vs
+  // I: SliceIndex<[<integer>]> and Range<<integer>>
+  //
+  // we need to figure out what Y is
+
+  TyTy::BaseType *associated_self = get_self ();
+  rust_assert (associated_self->can_eq (self, false));
+
+  // grab the parameters
+  HIR::ImplBlock &impl_block = *get_impl_block ();
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  for (auto &generic_param : impl_block.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: {
+	    TyTy::BaseType *l = nullptr;
+	    bool ok = context->lookup_type (
+	      generic_param->get_mappings ().get_hirid (), &l);
+	    if (ok && l->get_kind () == TyTy::TypeKind::PARAM)
+	      {
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param),
+		  static_cast<TyTy::ParamType *> (l)));
+	      }
+	  }
+	  break;
+	}
+    }
+
+  // generate inference variables for these bound arguments so we can compute
+  // their values
+  Location locus;
+  std::vector<TyTy::SubstitutionArg> args;
+  for (auto &p : substitutions)
+    {
+      if (p.needs_substitution ())
+	{
+	  TyTy::TyVar infer_var = TyTy::TyVar::get_implicit_infer_var (locus);
+	  args.push_back (TyTy::SubstitutionArg (&p, infer_var.get_tyty ()));
+	}
+      else
+	{
+	  args.push_back (
+	    TyTy::SubstitutionArg (&p, p.get_param_ty ()->resolve ()));
+	}
+    }
+
+  // this callback gives us the parameters that get substituted so we can
+  // compute the constrained type parameters for this impl block
+  std::map<std::string, HirId> param_mappings;
+  TyTy::ParamSubstCb param_subst_cb
+    = [&] (const TyTy::ParamType &p, const TyTy::SubstitutionArg &a) {
+	param_mappings[p.get_symbol ()] = a.get_tyty ()->get_ref ();
+      };
+
+  TyTy::SubstitutionArgumentMappings infer_arguments (std::move (args), locus,
+						      param_subst_cb);
+  TyTy::BaseType *impl_self_infer
+    = (associated_self->needs_generic_substitutions ())
+	? SubstMapperInternal::Resolve (associated_self, infer_arguments)
+	: associated_self;
+
+  // FIXME this needs to do a lookup for the trait-reference DefId instead of
+  // assuming its the first one in the list
+  rust_assert (associated_self->num_specified_bounds () > 0);
+  TyTy::TypeBoundPredicate &impl_predicate
+    = associated_self->get_specified_bounds ().at (0);
+
+  // infer the arguments on the predicate
+  std::vector<TyTy::BaseType *> impl_trait_predicate_args;
+  for (const auto &arg : impl_predicate.get_substs ())
+    {
+      const TyTy::ParamType *p = arg.get_param_ty ();
+      if (p->get_symbol ().compare ("Self") == 0)
+	continue;
+
+      TyTy::BaseType *r = p->resolve ();
+      r = SubstMapperInternal::Resolve (r, infer_arguments);
+      impl_trait_predicate_args.push_back (r);
+    }
+
+  // we need to unify the receiver with the impl-block Self so that we compute
+  // the type correctly as our receiver may be generic and we are inferring its
+  // generic arguments and this Self might be the concrete version or vice
+  // versa.
+  auto result = receiver->unify (impl_self_infer);
+  rust_assert (result->get_kind () != TyTy::TypeKind::ERROR);
+
+  // unify the bounds arguments
+  std::vector<TyTy::BaseType *> hrtb_bound_arguments;
+  for (const auto &arg : bound.get_substs ())
+    {
+      const TyTy::ParamType *p = arg.get_param_ty ();
+      if (p->get_symbol ().compare ("Self") == 0)
+	continue;
+
+      TyTy::BaseType *r = p->resolve ();
+      hrtb_bound_arguments.push_back (r);
+    }
+
+  rust_assert (impl_trait_predicate_args.size ()
+	       == hrtb_bound_arguments.size ());
+  for (size_t i = 0; i < impl_trait_predicate_args.size (); i++)
+    {
+      TyTy::BaseType *a = impl_trait_predicate_args.at (i);
+      TyTy::BaseType *b = hrtb_bound_arguments.at (i);
+
+      result = a->unify (b);
+      rust_assert (result->get_kind () != TyTy::TypeKind::ERROR);
+    }
+
+  // create the argument list
+  std::vector<TyTy::SubstitutionArg> associated_arguments;
+  for (auto &p : substitutions)
+    {
+      std::string symbol = p.get_param_ty ()->get_symbol ();
+      auto it = param_mappings.find (symbol);
+      rust_assert (it != param_mappings.end ());
+
+      HirId id = it->second;
+      TyTy::BaseType *argument = nullptr;
+      bool ok = context->lookup_type (id, &argument);
+      rust_assert (ok);
+
+      TyTy::SubstitutionArg arg (&p, argument);
+      associated_arguments.push_back (arg);
+    }
+
+  TyTy::SubstitutionArgumentMappings associated_type_args (
+    std::move (associated_arguments), locus);
+
+  ImplTypeIterator iter (*impl, [&] (HIR::TypeAlias &type) {
+    TraitItemReference *resolved_trait_item = nullptr;
+    bool ok = trait->lookup_trait_item (type.get_new_type_name (),
+					&resolved_trait_item);
+    if (!ok)
+      return;
+    if (resolved_trait_item->get_trait_item_type ()
+	!= TraitItemReference::TraitItemType::TYPE)
+      return;
+
+    TyTy::BaseType *lookup;
+    if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup))
+      return;
+
+    // this might be generic
+    TyTy::BaseType *substituted
+      = SubstMapperInternal::Resolve (lookup, associated_type_args);
+    resolved_trait_item->associated_type_set (substituted);
+  });
+  iter.go ();
+}
+
+void
+AssociatedImplTrait::reset_associated_types ()
+{
+  trait->clear_associated_types ();
+}
+
+Analysis::NodeMapping
+TraitItemReference::get_parent_trait_mappings () const
+{
+  auto mappings = Analysis::Mappings::get ();
+
+  HIR::Trait *trait
+    = mappings->lookup_trait_item_mapping (get_mappings ().get_hirid ());
+  rust_assert (trait != nullptr);
+
+  return trait->get_mappings ();
+}
+
+bool
+TraitItemReference::is_object_safe () const
+{
+  // https://doc.rust-lang.org/reference/items/traits.html#object-safety
+  switch (get_trait_item_type ())
+    {
+      case TraitItemReference::TraitItemType::FN: {
+	// lets be boring and just check that this is indeed a method will do
+	// for now
+	const HIR::TraitItem *item = get_hir_trait_item ();
+	const HIR::TraitItemFunc *fn
+	  = static_cast<const HIR::TraitItemFunc *> (item);
+	return fn->get_decl ().is_method ();
+      }
+
+      // constants are not available via dyn dispatch and so is not object safe
+    case TraitItemReference::TraitItemType::CONST:
+      return false;
+
+      // types are object safe since they are not available via dyn dispatch
+    case TraitItemReference::TraitItemType::TYPE:
+      return true;
+
+      // this is just an error so lets just fail it
+    case TraitItemReference::TraitItemType::ERROR:
+      return false;
+    }
+  return false;
+}
+
+// rust-hir-path-probe.h
+
+void
+PathProbeImplTrait::process_trait_impl_items_for_candidates ()
+{
+  mappings->iterate_impl_items (
+    [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
+      // just need to check if this is an impl block for this trait the next
+      // function checks the receiver
+      if (!impl->has_trait_ref ())
+	return true;
+
+      TraitReference *resolved
+	= TraitResolver::Lookup (*(impl->get_trait_ref ().get ()));
+      if (!trait_reference->is_equal (*resolved))
+	return true;
+
+      process_impl_item_candidate (id, item, impl);
+      return true;
+    });
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h
new file mode 100644
index 00000000000..c4aaf42b141
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TRAIT_RESOLVE_H
+#define RUST_HIR_TRAIT_RESOLVE_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-ref.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveTraitItemToRef : public TypeCheckBase,
+			      private HIR::HIRTraitItemVisitor
+{
+public:
+  static TraitItemReference
+  Resolve (HIR::TraitItem &item, TyTy::BaseType *self,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  {
+    ResolveTraitItemToRef resolver (self, std::move (substitutions));
+    item.accept_vis (resolver);
+    return std::move (resolver.resolved);
+  }
+
+  void visit (HIR::TraitItemType &type) override;
+
+  void visit (HIR::TraitItemConst &cst) override;
+
+  void visit (HIR::TraitItemFunc &fn) override;
+
+private:
+  ResolveTraitItemToRef (
+    TyTy::BaseType *self,
+    std::vector<TyTy::SubstitutionParamMapping> &&substitutions);
+
+  TraitItemReference resolved;
+  TyTy::BaseType *self;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+class TraitResolver : public TypeCheckBase, private HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  static TraitReference *Resolve (HIR::TypePath &path);
+
+  static TraitReference *Resolve (HIR::Trait &trait);
+
+  static TraitReference *Lookup (HIR::TypePath &path);
+
+private:
+  TraitResolver ();
+
+  TraitReference *resolve_path (HIR::TypePath &path);
+
+  TraitReference *resolve_trait (HIR::Trait *trait_reference);
+
+  TraitReference *lookup_path (HIR::TypePath &path);
+
+  HIR::Trait *resolved_trait_reference;
+
+public:
+  void visit (HIR::Trait &trait) override { resolved_trait_reference = &trait; }
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TRAIT_RESOLVE_H
diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h
new file mode 100644
index 00000000000..44400efbbf7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-bounds.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_BOUNDS_H
+#define RUST_HIR_TYPE_BOUNDS_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeBoundsProbe : public TypeCheckBase
+{
+public:
+  static std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
+  Probe (const TyTy::BaseType *receiver)
+  {
+    TypeBoundsProbe probe (receiver);
+    probe.scan ();
+    return probe.trait_references;
+  }
+
+  static bool is_bound_satisfied_for_type (TyTy::BaseType *receiver,
+					   TraitReference *ref)
+  {
+    for (auto &bound : receiver->get_specified_bounds ())
+      {
+	const TraitReference *b = bound.get ();
+	if (b->is_equal (*ref))
+	  return true;
+      }
+
+    std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
+      = Probe (receiver);
+    for (auto &bound : bounds)
+      {
+	const TraitReference *b = bound.first;
+	if (b->is_equal (*ref))
+	  return true;
+      }
+
+    return false;
+  }
+
+private:
+  void scan ();
+
+private:
+  TypeBoundsProbe (const TyTy::BaseType *receiver)
+    : TypeCheckBase (), receiver (receiver)
+  {}
+
+  const TyTy::BaseType *receiver;
+  std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> trait_references;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_BOUNDS_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc
new file mode 100644
index 00000000000..ac5c3b97475
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -0,0 +1,439 @@
+// 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-base.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-coercion.h"
+#include "rust-casts.h"
+
+namespace Rust {
+namespace Resolver {
+
+bool
+TypeCheckBase::check_for_unconstrained (
+  const std::vector<TyTy::SubstitutionParamMapping> &params_to_constrain,
+  const TyTy::SubstitutionArgumentMappings &constraint_a,
+  const TyTy::SubstitutionArgumentMappings &constraint_b,
+  const TyTy::BaseType *reference)
+{
+  std::set<HirId> symbols_to_constrain;
+  std::map<HirId, Location> symbol_to_location;
+  for (const auto &p : params_to_constrain)
+    {
+      HirId ref = p.get_param_ty ()->get_ref ();
+      symbols_to_constrain.insert (ref);
+      symbol_to_location.insert ({ref, p.get_param_locus ()});
+    }
+
+  // set up the set of constrained symbols
+  std::set<HirId> constrained_symbols;
+  for (const auto &c : constraint_a.get_mappings ())
+    {
+      const TyTy::BaseType *arg = c.get_tyty ();
+      if (arg != nullptr)
+	{
+	  const TyTy::BaseType *p = arg->get_root ();
+	  constrained_symbols.insert (p->get_ty_ref ());
+	}
+    }
+  for (const auto &c : constraint_b.get_mappings ())
+    {
+      const TyTy::BaseType *arg = c.get_tyty ();
+      if (arg != nullptr)
+	{
+	  const TyTy::BaseType *p = arg->get_root ();
+	  constrained_symbols.insert (p->get_ty_ref ());
+	}
+    }
+
+  const auto root = reference->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      const TyTy::ParamType *p = static_cast<const TyTy::ParamType *> (root);
+      constrained_symbols.insert (p->get_ty_ref ());
+    }
+
+  // check for unconstrained
+  bool unconstrained = false;
+  for (auto &sym : symbols_to_constrain)
+    {
+      bool used = constrained_symbols.find (sym) != constrained_symbols.end ();
+      if (!used)
+	{
+	  Location locus = symbol_to_location.at (sym);
+	  rust_error_at (locus, "unconstrained type parameter");
+	  unconstrained = true;
+	}
+    }
+  return unconstrained;
+}
+
+TyTy::BaseType *
+TypeCheckBase::resolve_literal (const Analysis::NodeMapping &expr_mappings,
+				HIR::Literal &literal, Location locus)
+{
+  TyTy::BaseType *infered = nullptr;
+  switch (literal.get_lit_type ())
+    {
+      case HIR::Literal::LitType::INT: {
+	bool ok = false;
+
+	switch (literal.get_type_hint ())
+	  {
+	  case CORETYPE_I8:
+	    ok = context->lookup_builtin ("i8", &infered);
+	    break;
+	  case CORETYPE_I16:
+	    ok = context->lookup_builtin ("i16", &infered);
+	    break;
+	  case CORETYPE_I32:
+	    ok = context->lookup_builtin ("i32", &infered);
+	    break;
+	  case CORETYPE_I64:
+	    ok = context->lookup_builtin ("i64", &infered);
+	    break;
+	  case CORETYPE_I128:
+	    ok = context->lookup_builtin ("i128", &infered);
+	    break;
+
+	  case CORETYPE_U8:
+	    ok = context->lookup_builtin ("u8", &infered);
+	    break;
+	  case CORETYPE_U16:
+	    ok = context->lookup_builtin ("u16", &infered);
+	    break;
+	  case CORETYPE_U32:
+	    ok = context->lookup_builtin ("u32", &infered);
+	    break;
+	  case CORETYPE_U64:
+	    ok = context->lookup_builtin ("u64", &infered);
+	    break;
+	  case CORETYPE_U128:
+	    ok = context->lookup_builtin ("u128", &infered);
+	    break;
+
+	  case CORETYPE_F32:
+	    literal.set_lit_type (HIR::Literal::LitType::FLOAT);
+	    ok = context->lookup_builtin ("f32", &infered);
+	    break;
+	  case CORETYPE_F64:
+	    literal.set_lit_type (HIR::Literal::LitType::FLOAT);
+	    ok = context->lookup_builtin ("f64", &infered);
+	    break;
+
+	  case CORETYPE_ISIZE:
+	    ok = context->lookup_builtin ("isize", &infered);
+	    break;
+
+	  case CORETYPE_USIZE:
+	    ok = context->lookup_builtin ("usize", &infered);
+	    break;
+
+	  default:
+	    ok = true;
+	    infered
+	      = new TyTy::InferType (expr_mappings.get_hirid (),
+				     TyTy::InferType::InferTypeKind::INTEGRAL,
+				     locus);
+	    break;
+	  }
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::FLOAT: {
+	bool ok = false;
+
+	switch (literal.get_type_hint ())
+	  {
+	  case CORETYPE_F32:
+	    ok = context->lookup_builtin ("f32", &infered);
+	    break;
+	  case CORETYPE_F64:
+	    ok = context->lookup_builtin ("f64", &infered);
+	    break;
+
+	  default:
+	    ok = true;
+	    infered
+	      = new TyTy::InferType (expr_mappings.get_hirid (),
+				     TyTy::InferType::InferTypeKind::FLOAT,
+				     locus);
+	    break;
+	  }
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::BOOL: {
+	auto ok = context->lookup_builtin ("bool", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::CHAR: {
+	auto ok = context->lookup_builtin ("char", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::BYTE: {
+	auto ok = context->lookup_builtin ("u8", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::STRING: {
+	TyTy::BaseType *base = nullptr;
+	auto ok = context->lookup_builtin ("str", &base);
+	rust_assert (ok);
+
+	infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
+					   TyTy::TyVar (base->get_ref ()),
+					   Mutability::Imm);
+      }
+      break;
+
+      case HIR::Literal::LitType::BYTE_STRING: {
+	/* This is an arraytype of u8 reference (&[u8;size]). It isn't in
+	   UTF-8, but really just a byte array. Code to construct the array
+	   reference copied from ArrayElemsValues and ArrayType. */
+	TyTy::BaseType *u8;
+	auto ok = context->lookup_builtin ("u8", &u8);
+	rust_assert (ok);
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping capacity_mapping (crate_num, UNKNOWN_NODEID,
+						mappings->get_next_hir_id (
+						  crate_num),
+						UNKNOWN_LOCAL_DEFID);
+
+	/* Capacity is the size of the string (number of chars).
+	   It is a constant, but for fold it to get a tree.  */
+	std::string capacity_str
+	  = std::to_string (literal.as_string ().size ());
+	HIR::LiteralExpr *literal_capacity
+	  = new HIR::LiteralExpr (capacity_mapping, capacity_str,
+				  HIR::Literal::LitType::INT,
+				  PrimitiveCoreType::CORETYPE_USIZE, locus, {});
+
+	// mark the type for this implicit node
+	TyTy::BaseType *expected_ty = nullptr;
+	ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (capacity_mapping, expected_ty);
+
+	Analysis::NodeMapping array_mapping (crate_num, UNKNOWN_NODEID,
+					     mappings->get_next_hir_id (
+					       crate_num),
+					     UNKNOWN_LOCAL_DEFID);
+
+	TyTy::ArrayType *array
+	  = new TyTy::ArrayType (array_mapping.get_hirid (), locus,
+				 *literal_capacity,
+				 TyTy::TyVar (u8->get_ref ()));
+	context->insert_type (array_mapping, array);
+
+	infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
+					   TyTy::TyVar (array->get_ref ()),
+					   Mutability::Imm);
+      }
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  return infered;
+}
+
+TyTy::ADTType::ReprOptions
+TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, Location locus)
+{
+  TyTy::ADTType::ReprOptions repr;
+  repr.pack = 0;
+  repr.align = 0;
+
+  for (const auto &attr : attrs)
+    {
+      bool is_repr = attr.get_path ().as_string ().compare ("repr") == 0;
+      if (is_repr)
+	{
+	  const AST::AttrInput &input = attr.get_attr_input ();
+	  bool is_token_tree = input.get_attr_input_type ()
+			       == AST::AttrInput::AttrInputType::TOKEN_TREE;
+	  rust_assert (is_token_tree);
+	  const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+	  AST::AttrInputMetaItemContainer *meta_items
+	    = option.parse_to_meta_item ();
+
+	  const std::string inline_option
+	    = meta_items->get_items ().at (0)->as_string ();
+
+	  // TODO: it would probably be better to make the MetaItems more aware
+	  // of constructs with nesting like #[repr(packed(2))] rather than
+	  // manually parsing the string "packed(2)" here.
+
+	  size_t oparen = inline_option.find ('(', 0);
+	  bool is_pack = false, is_align = false;
+	  unsigned char value = 1;
+
+	  if (oparen == std::string::npos)
+	    {
+	      is_pack = inline_option.compare ("packed") == 0;
+	      is_align = inline_option.compare ("align") == 0;
+	    }
+
+	  else
+	    {
+	      std::string rep = inline_option.substr (0, oparen);
+	      is_pack = rep.compare ("packed") == 0;
+	      is_align = rep.compare ("align") == 0;
+
+	      size_t cparen = inline_option.find (')', oparen);
+	      if (cparen == std::string::npos)
+		{
+		  rust_error_at (locus, "malformed attribute");
+		}
+
+	      std::string value_str = inline_option.substr (oparen, cparen);
+	      value = strtoul (value_str.c_str () + 1, NULL, 10);
+	    }
+
+	  if (is_pack)
+	    repr.pack = value;
+	  else if (is_align)
+	    repr.align = value;
+
+	  // Multiple repr options must be specified with e.g. #[repr(C,
+	  // packed(2))].
+	  break;
+	}
+    }
+
+  return repr;
+}
+
+TyTy::BaseType *
+TypeCheckBase::coercion_site (HirId id, TyTy::BaseType *expected,
+			      TyTy::BaseType *expr, Location locus)
+{
+  rust_debug ("coercion_site id={%u} expected={%s} expr={%s}", id,
+	      expected->debug_str ().c_str (), expr->debug_str ().c_str ());
+
+  auto context = TypeCheckContext::get ();
+  if (expected->get_kind () == TyTy::TypeKind::ERROR
+      || expr->get_kind () == TyTy::TypeKind::ERROR)
+    return expr;
+
+  // can we autoderef it?
+  auto result = TypeCoercionRules::Coerce (expr, expected, locus);
+
+  // the result needs to be unified
+  TyTy::BaseType *receiver = expr;
+  if (!result.is_error ())
+    {
+      receiver = result.tyty;
+    }
+
+  rust_debug ("coerce_default_unify(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+  TyTy::BaseType *coerced = expected->unify (receiver);
+  context->insert_autoderef_mappings (id, std::move (result.adjustments));
+  return coerced;
+}
+
+TyTy::BaseType *
+TypeCheckBase::cast_site (HirId id, TyTy::TyWithLocation from,
+			  TyTy::TyWithLocation to, Location cast_locus)
+{
+  rust_debug ("cast_site id={%u} from={%s} to={%s}", id,
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  auto context = TypeCheckContext::get ();
+  if (from.get_ty ()->get_kind () == TyTy::TypeKind::ERROR
+      || to.get_ty ()->get_kind () == TyTy::TypeKind::ERROR)
+    return to.get_ty ();
+
+  // do the cast
+  auto result = TypeCastRules::resolve (cast_locus, from, to);
+
+  // we assume error has already been emitted
+  if (result.is_error ())
+    return to.get_ty ();
+
+  // the result needs to be unified
+  TyTy::BaseType *casted_result = result.tyty;
+  rust_debug ("cast_default_unify(a={%s}, b={%s})",
+	      casted_result->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+  TyTy::BaseType *casted = to.get_ty ()->unify (casted_result);
+  context->insert_cast_autoderef_mappings (id, std::move (result.adjustments));
+  return casted;
+}
+
+void
+TypeCheckBase::resolve_generic_params (
+  const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params,
+  std::vector<TyTy::SubstitutionParamMapping> &substitutions)
+{
+  for (auto &generic_param : generic_params)
+    {
+      switch (generic_param.get ()->get_kind ())
+	{
+	case HIR::GenericParam::GenericKind::LIFETIME:
+	  // FIXME: Skipping Lifetime completely until better
+	  // handling.
+	  break;
+	  case HIR::GenericParam::GenericKind::CONST: {
+	    auto param
+	      = static_cast<HIR::ConstGenericParam *> (generic_param.get ());
+	    auto specified_type
+	      = TypeCheckType::Resolve (param->get_type ().get ());
+
+	    if (param->has_default_expression ())
+	      {
+		auto expr_type = TypeCheckExpr::Resolve (
+		  param->get_default_expression ().get ());
+
+		specified_type->unify (expr_type);
+	      }
+
+	    context->insert_type (generic_param->get_mappings (),
+				  specified_type);
+	  }
+	  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;
+	}
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h
new file mode 100644
index 00000000000..aa42d9d6dfd
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_BASE
+#define RUST_HIR_TYPE_CHECK_BASE
+
+#include "rust-diagnostics.h"
+#include "rust-hir-type-check.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-backend.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TraitReference;
+class TypeCheckBase
+{
+public:
+  virtual ~TypeCheckBase () {}
+
+  static TyTy::BaseType *coercion_site (HirId id, TyTy::BaseType *lhs,
+					TyTy::BaseType *rhs,
+					Location coercion_locus);
+
+  static TyTy::BaseType *cast_site (HirId id, TyTy::TyWithLocation from,
+				    TyTy::TyWithLocation to,
+				    Location cast_locus);
+
+protected:
+  TypeCheckBase ()
+    : mappings (Analysis::Mappings::get ()), resolver (Resolver::get ()),
+      context (TypeCheckContext::get ())
+  {}
+
+  TraitReference *resolve_trait_path (HIR::TypePath &);
+
+  TyTy::TypeBoundPredicate get_predicate_from_bound (HIR::TypePath &path);
+
+  bool check_for_unconstrained (
+    const std::vector<TyTy::SubstitutionParamMapping> &params_to_constrain,
+    const TyTy::SubstitutionArgumentMappings &constraint_a,
+    const TyTy::SubstitutionArgumentMappings &constraint_b,
+    const TyTy::BaseType *reference);
+
+  TyTy::BaseType *resolve_literal (const Analysis::NodeMapping &mappings,
+				   HIR::Literal &literal, Location locus);
+
+  TyTy::ADTType::ReprOptions parse_repr_options (const AST::AttrVec &attrs,
+						 Location locus);
+
+  void resolve_generic_params (
+    const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params,
+    std::vector<TyTy::SubstitutionParamMapping> &substitutions);
+
+  Analysis::Mappings *mappings;
+  Resolver *resolver;
+  TypeCheckContext *context;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_BASE
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
new file mode 100644
index 00000000000..c771ea3782d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_ENUMITEM
+#define RUST_HIR_TYPE_CHECK_ENUMITEM
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckEnumItem : public TypeCheckBase
+{
+public:
+  static TyTy::VariantDef *Resolve (HIR::EnumItem *item,
+				    int64_t last_discriminant);
+
+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);
+
+  TyTy::VariantDef *variant;
+  int64_t last_discriminant;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_ENUMITEM
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
new file mode 100644
index 00000000000..4371f5a59a5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -0,0 +1,1567 @@
+// 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-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::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: {
+	TyTy::TyWithLocation from (rhs, expr.get_rhs ()->get_locus ());
+	TyTy::TyWithLocation to (lhs, expr.get_lhs ()->get_locus ());
+	infered = cast_site (expr.get_mappings ().get_hirid (), from, to,
+			     expr.get_locus ());
+      }
+      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)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+  TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+  TyTy::BaseType *unified = from_ty->unify (to_ty);
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFromExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FROM;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeToExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_TO;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFullExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FULL;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->is_unit ());
+
+  infered = item_type;
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFromToInclExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_INCLUSIVE;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+  TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+  TyTy::BaseType *unified = from_ty->unify (to_ty);
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
+{
+  auto array_expr_ty = TypeCheckExpr::Resolve (expr.get_array_expr ());
+  if (array_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  auto index_expr_ty = TypeCheckExpr::Resolve (expr.get_index_expr ());
+  if (index_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  // first attempt to use direct array index logic
+  auto direct_array_expr_ty = array_expr_ty;
+  if (direct_array_expr_ty->get_kind () == TyTy::TypeKind::REF)
+    {
+      // lets try and deref it since rust allows this
+      auto ref = static_cast<TyTy::ReferenceType *> (direct_array_expr_ty);
+      auto base = ref->get_base ();
+      if (base->get_kind () == TyTy::TypeKind::ARRAY)
+	direct_array_expr_ty = base;
+    }
+
+  TyTy::BaseType *size_ty;
+  bool ok = context->lookup_builtin ("usize", &size_ty);
+  rust_assert (ok);
+
+  bool maybe_simple_array_access = index_expr_ty->can_eq (size_ty, false);
+  if (maybe_simple_array_access
+      && direct_array_expr_ty->get_kind () == TyTy::TypeKind::ARRAY)
+    {
+      auto resolved_index_expr = size_ty->unify (index_expr_ty);
+      if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      TyTy::ArrayType *array_type
+	= static_cast<TyTy::ArrayType *> (direct_array_expr_ty);
+      infered = array_type->get_element_type ()->clone ();
+      return;
+    }
+
+  // is this a case of core::ops::index?
+  auto lang_item_type = Analysis::RustLangItem::ItemType::INDEX;
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, array_expr_ty,
+				 index_expr_ty);
+  if (operator_overloaded)
+    {
+      // index and index mut always return a reference to the element
+      TyTy::BaseType *resolved = infered;
+      rust_assert (resolved->get_kind () == TyTy::TypeKind::REF);
+      TyTy::ReferenceType *ref = static_cast<TyTy::ReferenceType *> (resolved);
+
+      infered = ref->get_base ()->clone ();
+      return;
+    }
+
+  // error[E0277]: the type `[{integer}]` cannot be indexed by `u32`
+  RichLocation r (expr.get_locus ());
+  r.add_range (expr.get_array_expr ()->get_locus ());
+  r.add_range (expr.get_index_expr ()->get_locus ());
+  rust_error_at (r, "the type %<%s%> cannot be indexed by %<%s%>",
+		 array_expr_ty->get_name ().c_str (),
+		 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)
+{
+  auto receiver_tyty = TypeCheckExpr::Resolve (expr.get_receiver ().get ());
+  if (receiver_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_receiver ()->get_locus (),
+		     "failed to resolve receiver in MethodCallExpr");
+      return;
+    }
+
+  context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);
+
+  auto candidate
+    = MethodResolver::Probe (receiver_tyty,
+			     expr.get_method_name ().get_segment ());
+  if (candidate.is_error ())
+    {
+      rust_error_at (
+	expr.get_method_name ().get_locus (),
+	"failed to resolve method for %<%s%>",
+	expr.get_method_name ().get_segment ().as_string ().c_str ());
+      return;
+    }
+
+  // Get the adjusted self
+  Adjuster adj (receiver_tyty);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // store the adjustments for code-generation to know what to do which must be
+  // stored onto the receiver to so as we don't trigger duplicate deref mappings
+  // ICE when an argument is a method call
+  HirId autoderef_mappings_id
+    = expr.get_receiver ()->get_mappings ().get_hirid ();
+  context->insert_autoderef_mappings (autoderef_mappings_id,
+				      std::move (candidate.adjustments));
+
+  PathProbeCandidate &resolved_candidate = candidate.candidate;
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+  NodeId resolved_node_id
+    = resolved_candidate.is_impl_candidate ()
+	? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
+	    .get_nodeid ()
+	: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+  if (lookup_tyty->get_kind () != TyTy::TypeKind::FNDEF)
+    {
+      RichLocation r (expr.get_method_name ().get_locus ());
+      r.add_range (resolved_candidate.locus);
+      rust_error_at (r, "associated impl item is not a method");
+      return;
+    }
+
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  if (!fn->is_method ())
+    {
+      RichLocation r (expr.get_method_name ().get_locus ());
+      r.add_range (resolved_candidate.locus);
+      rust_error_at (r, "associated function is not a method");
+      return;
+    }
+
+  auto root = receiver_tyty->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::ADT)
+    {
+      const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
+      if (adt->has_substitutions () && fn->needs_substitution ())
+	{
+	  // consider the case where we have:
+	  //
+	  // struct Foo<X,Y>(X,Y);
+	  //
+	  // impl<T> Foo<T, i32> {
+	  //   fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
+	  // }
+	  //
+	  // In this case we end up with an fn type of:
+	  //
+	  // fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
+	  //
+	  // This means the instance or self we are calling this method for
+	  // will be substituted such that we can get the inherited type
+	  // arguments but then need to use the turbo fish if available or
+	  // infer the remaining arguments. Luckily rust does not allow for
+	  // default types GenericParams on impl blocks since these must
+	  // always be at the end of the list
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+    }
+
+  // apply any remaining generic arguments
+  if (expr.get_method_name ().has_generic_args ())
+    {
+      HIR::GenericArgs &args = expr.get_method_name ().get_generic_args ();
+      lookup
+	= SubstMapper::Resolve (lookup, expr.get_method_name ().get_locus (),
+				&args);
+      if (lookup->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+  else if (lookup->needs_generic_substitutions ())
+    {
+      lookup = SubstMapper::InferSubst (lookup,
+					expr.get_method_name ().get_locus ());
+    }
+
+  TyTy::BaseType *function_ret_tyty
+    = TyTy::TypeCheckMethodCallExpr::go (lookup, expr, adjusted_self, context);
+  if (function_ret_tyty == nullptr
+      || function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup type to MethodCallExpr");
+      return;
+    }
+
+  // store the expected fntype
+  context->insert_type (expr.get_method_name ().get_mappings (), lookup);
+
+  // set up the resolved name on the path
+  resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				  resolved_node_id);
+
+  // return the result of the function back
+  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 ());
+
+  TyTy::TyWithLocation from (expr_to_convert,
+			     expr.get_casted_expr ()->get_locus ());
+  TyTy::TyWithLocation to (tyty_to_convert_to,
+			   expr.get_type_to_convert_to ()->get_locus ());
+  infered = cast_site (expr.get_mappings ().get_hirid (), from, to,
+		       expr.get_locus ());
+}
+
+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,
+  TyTy::BaseType *lhs, TyTy::BaseType *rhs)
+{
+  // look up lang item for arithmetic type
+  std::string associated_item_name
+    = Analysis::RustLangItem::ToString (lang_item_type);
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // probe for the lang-item
+  if (!lang_item_defined)
+    return false;
+
+  auto segment = HIR::PathIdentSegment (associated_item_name);
+  auto candidate
+    = MethodResolver::Probe (lhs, HIR::PathIdentSegment (associated_item_name));
+
+  bool have_implementation_for_lang_item = !candidate.is_error ();
+  if (!have_implementation_for_lang_item)
+    return false;
+
+  // Get the adjusted self
+  Adjuster adj (lhs);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // is this the case we are recursive
+  // handle the case where we are within the impl block for this lang_item
+  // otherwise we end up with a recursive operator overload such as the i32
+  // operator overload trait
+  TypeCheckContextItem &fn_context = context->peek_context ();
+  if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+    {
+      auto &impl_item = fn_context.get_impl_item ();
+      HIR::ImplBlock *parent = impl_item.first;
+      HIR::Function *fn = impl_item.second;
+
+      if (parent->has_trait_ref ()
+	  && fn->get_function_name ().compare (associated_item_name) == 0)
+	{
+	  TraitReference *trait_reference
+	    = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
+	  if (!trait_reference->is_error ())
+	    {
+	      TyTy::BaseType *lookup = nullptr;
+	      bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
+					      &lookup);
+	      rust_assert (ok);
+	      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+
+	      TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+	      rust_assert (fntype->is_method ());
+
+	      bool is_lang_item_impl
+		= trait_reference->get_mappings ().get_defid ()
+		  == respective_lang_item_id;
+	      bool self_is_lang_item_self
+		= fntype->get_self_type ()->is_equal (*adjusted_self);
+	      bool recursive_operator_overload
+		= is_lang_item_impl && self_is_lang_item_self;
+
+	      if (recursive_operator_overload)
+		return false;
+	    }
+	}
+    }
+
+  // store the adjustments for code-generation to know what to do
+  context->insert_autoderef_mappings (expr.get_lvalue_mappings ().get_hirid (),
+				      std::move (candidate.adjustments));
+
+  // now its just like a method-call-expr
+  context->insert_receiver (expr.get_mappings ().get_hirid (), lhs);
+
+  PathProbeCandidate &resolved_candidate = candidate.candidate;
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+  NodeId resolved_node_id
+    = resolved_candidate.is_impl_candidate ()
+	? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
+	    .get_nodeid ()
+	: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (fn->is_method ());
+
+  auto root = lhs->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::ADT)
+    {
+      const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
+      if (adt->has_substitutions () && fn->needs_substitution ())
+	{
+	  // consider the case where we have:
+	  //
+	  // struct Foo<X,Y>(X,Y);
+	  //
+	  // impl<T> Foo<T, i32> {
+	  //   fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
+	  // }
+	  //
+	  // In this case we end up with an fn type of:
+	  //
+	  // fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
+	  //
+	  // This means the instance or self we are calling this method for
+	  // will be substituted such that we can get the inherited type
+	  // arguments but then need to use the turbo fish if available or
+	  // infer the remaining arguments. Luckily rust does not allow for
+	  // default types GenericParams on impl blocks since these must
+	  // always be at the end of the list
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+    }
+
+  // handle generics
+  if (lookup->needs_generic_substitutions ())
+    lookup = SubstMapper::InferSubst (lookup, expr.get_locus ());
+
+  // type check the arguments if required
+  TyTy::FnType *type = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (type->num_params () > 0);
+  auto fnparam = type->param_at (0);
+  fnparam.second->unify (adjusted_self); // typecheck the self
+  if (rhs == nullptr)
+    {
+      rust_assert (type->num_params () == 1);
+    }
+  else
+    {
+      rust_assert (type->num_params () == 2);
+      auto fnparam = type->param_at (1);
+      fnparam.second->unify (rhs); // typecheck the rhs
+    }
+
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+  fn = static_cast<TyTy::FnType *> (lookup);
+  fn->monomorphize ();
+
+  // get the return type
+  TyTy::BaseType *function_ret_tyty
+    = type->get_return_type ()->monomorphized_clone ();
+
+  // store the expected fntype
+  context->insert_operator_overload (expr.get_mappings ().get_hirid (), type);
+
+  // set up the resolved name on the path
+  resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				  resolved_node_id);
+
+  // return the result of the function back
+  infered = function_ret_tyty;
+
+  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
new file mode 100644
index 00000000000..19a6c791a9d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -0,0 +1,131 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_EXPR
+#define RUST_HIR_TYPE_CHECK_EXPR
+
+#include "rust-hir-type-check-base.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckExpr : public TypeCheckBase, private HIR::HIRExpressionVisitor
+{
+public:
+  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;
+  void visit (HIR::CompoundAssignmentExpr &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;
+  void visit (HIR::ArrayIndexExpr &expr) override;
+  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;
+  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
+  {
+    gcc_unreachable ();
+  }
+  void visit (HIR::StructExprFieldIdentifierValue &field) override
+  {
+    gcc_unreachable ();
+  }
+  void visit (HIR::StructExprFieldIndexValue &field) override
+  {
+    gcc_unreachable ();
+  }
+
+protected:
+  bool
+  resolve_operator_overload (Analysis::RustLangItem::ItemType lang_item_type,
+			     HIR::OperatorExprMeta expr, TyTy::BaseType *lhs,
+			     TyTy::BaseType *rhs);
+
+private:
+  TypeCheckExpr ();
+
+  TyTy::BaseType *resolve_root_path (HIR::PathInExpression &expr,
+				     size_t *offset,
+				     NodeId *root_resolved_node_id);
+
+  void resolve_segments (NodeId root_resolved_node_id,
+			 std::vector<HIR::PathExprSegment> &segments,
+			 size_t offset, TyTy::BaseType *tyseg,
+			 const Analysis::NodeMapping &expr_mappings,
+			 Location expr_locus);
+
+  bool
+  validate_arithmetic_type (const TyTy::BaseType *tyty,
+			    HIR::ArithmeticOrLogicalExpr::ExprType expr_type);
+
+  /* The return value of TypeCheckExpr::Resolve */
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_EXPR
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
new file mode 100644
index 00000000000..f2f3faab9e0
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
@@ -0,0 +1,114 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_IMPLITEM_H
+#define RUST_HIR_TYPE_CHECK_IMPLITEM_H
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckTopLevelExternItem : public TypeCheckBase,
+				    public HIR::HIRExternalItemVisitor
+{
+public:
+  static void Resolve (HIR::ExternalItem *item, const HIR::ExternBlock &parent);
+
+  void visit (HIR::ExternalStaticItem &item) override;
+  void visit (HIR::ExternalFunctionItem &function) override;
+
+private:
+  TypeCheckTopLevelExternItem (const HIR::ExternBlock &parent);
+
+  const HIR::ExternBlock &parent;
+};
+
+class TypeCheckTopLevelImplItem : public TypeCheckBase,
+				  public HIR::HIRImplVisitor
+{
+public:
+  static void
+  Resolve (HIR::ImplItem *item, TyTy::BaseType *self,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  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);
+
+  TyTy::BaseType *self;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+class TypeCheckImplItem : public TypeCheckBase, public HIR::HIRImplVisitor
+{
+public:
+  static void Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item,
+		       TyTy::BaseType *self);
+
+  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);
+
+  HIR::ImplBlock *parent;
+  TyTy::BaseType *self;
+};
+
+class TypeCheckImplItemWithTrait : public TypeCheckImplItem
+{
+public:
+  static TyTy::TypeBoundPredicateItem
+  Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self,
+	   TyTy::TypeBoundPredicate &trait_reference,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  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);
+
+private:
+  TypeCheckImplItemWithTrait (
+    HIR::ImplBlock *parent, TyTy::BaseType *self,
+    TyTy::TypeBoundPredicate &trait_reference,
+    std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  bool is_trait_impl_block () const;
+
+  TyTy::TypeBoundPredicate &trait_reference;
+  TyTy::TypeBoundPredicateItem resolved_trait_item;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_IMPLITEM_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc
new file mode 100644
index 00000000000..d31a6df4777
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -0,0 +1,237 @@
+// 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-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)
+{
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
+  TypeCheckItem resolver;
+  vis_item.accept_vis (resolver);
+}
+
+void
+TypeCheckItem::visit (HIR::ImplBlock &impl_block)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (impl_block.has_generics ())
+    {
+      for (auto &generic_param : impl_block.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: {
+		TyTy::BaseType *l = nullptr;
+		bool ok = context->lookup_type (
+		  generic_param->get_mappings ().get_hirid (), &l);
+		if (ok && l->get_kind () == TyTy::TypeKind::PARAM)
+		  {
+		    substitutions.push_back (TyTy::SubstitutionParamMapping (
+		      static_cast<HIR::TypeParam &> (*generic_param),
+		      static_cast<TyTy::ParamType *> (l)));
+		  }
+	      }
+	      break;
+	    }
+	}
+    }
+
+  auto specified_bound = TyTy::TypeBoundPredicate::error ();
+  TraitReference *trait_reference = &TraitReference::error_node ();
+  if (impl_block.has_trait_ref ())
+    {
+      std::unique_ptr<HIR::TypePath> &ref = impl_block.get_trait_ref ();
+      trait_reference = TraitResolver::Resolve (*ref.get ());
+      rust_assert (!trait_reference->is_error ());
+
+      // we don't error out here see: gcc/testsuite/rust/compile/traits2.rs
+      // for example
+      specified_bound = get_predicate_from_bound (*ref.get ());
+    }
+
+  TyTy::BaseType *self = nullptr;
+  if (!context->lookup_type (
+	impl_block.get_type ()->get_mappings ().get_hirid (), &self))
+    {
+      rust_error_at (impl_block.get_locus (),
+		     "failed to resolve Self for ImplBlock");
+      return;
+    }
+
+  // inherit the bounds
+  if (!specified_bound.is_error ())
+    self->inherit_bounds ({specified_bound});
+
+  // check for any unconstrained type-params
+  const TyTy::SubstitutionArgumentMappings trait_constraints
+    = specified_bound.get_substitution_arguments ();
+  const TyTy::SubstitutionArgumentMappings impl_constraints
+    = GetUsedSubstArgs::From (self);
+
+  bool impl_block_has_unconstrained_typarams
+    = check_for_unconstrained (substitutions, trait_constraints,
+			       impl_constraints, self);
+  if (impl_block_has_unconstrained_typarams)
+    return;
+
+  // validate the impl items
+  bool is_trait_impl_block = !trait_reference->is_error ();
+  std::vector<const TraitItemReference *> trait_item_refs;
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      if (!is_trait_impl_block)
+	TypeCheckImplItem::Resolve (&impl_block, impl_item.get (), self);
+      else
+	{
+	  auto trait_item_ref
+	    = TypeCheckImplItemWithTrait::Resolve (&impl_block,
+						   impl_item.get (), self,
+						   specified_bound,
+						   substitutions);
+	  trait_item_refs.push_back (trait_item_ref.get_raw_item ());
+	}
+    }
+
+  bool impl_block_missing_trait_items
+    = is_trait_impl_block
+      && trait_reference->size () != trait_item_refs.size ();
+  if (impl_block_missing_trait_items)
+    {
+      // filter the missing impl_items
+      std::vector<std::reference_wrapper<const TraitItemReference>>
+	missing_trait_items;
+      for (const auto &trait_item_ref : trait_reference->get_trait_items ())
+	{
+	  bool found = false;
+	  for (auto implemented_trait_item : trait_item_refs)
+	    {
+	      std::string trait_item_name = trait_item_ref.get_identifier ();
+	      std::string impl_item_name
+		= implemented_trait_item->get_identifier ();
+	      found = trait_item_name.compare (impl_item_name) == 0;
+	      if (found)
+		break;
+	    }
+
+	  bool is_required_trait_item = !trait_item_ref.is_optional ();
+	  if (!found && is_required_trait_item)
+	    missing_trait_items.push_back (trait_item_ref);
+	}
+
+      if (missing_trait_items.size () > 0)
+	{
+	  std::string missing_items_buf;
+	  RichLocation r (impl_block.get_locus ());
+	  for (size_t i = 0; i < missing_trait_items.size (); i++)
+	    {
+	      bool has_more = (i + 1) < missing_trait_items.size ();
+	      const TraitItemReference &missing_trait_item
+		= missing_trait_items.at (i);
+	      missing_items_buf += missing_trait_item.get_identifier ()
+				   + (has_more ? ", " : "");
+	      r.add_range (missing_trait_item.get_locus ());
+	    }
+
+	  rust_error_at (r, "missing %s in implementation of trait %<%s%>",
+			 missing_items_buf.c_str (),
+			 trait_reference->get_name ().c_str ());
+	}
+    }
+
+  if (is_trait_impl_block)
+    {
+      trait_reference->clear_associated_types ();
+
+      AssociatedImplTrait associated (trait_reference, &impl_block, self,
+				      context);
+      context->insert_associated_trait_impl (
+	impl_block.get_mappings ().get_hirid (), std::move (associated));
+      context->insert_associated_impl_mapping (
+	trait_reference->get_mappings ().get_hirid (), self,
+	impl_block.get_mappings ().get_hirid ());
+    }
+}
+
+void
+TypeCheckItem::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 *resolved_fn_type = static_cast<TyTy::FnType *> (lookup);
+  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);
+}
+
+void
+TypeCheckItem::visit (HIR::Module &module)
+{
+  for (auto &item : module.get_items ())
+    TypeCheckItem::Resolve (*item.get ());
+}
+
+void
+TypeCheckItem::visit (HIR::Trait &trait)
+{
+  TraitResolver::Resolve (trait);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h
new file mode 100644
index 00000000000..ba4de19c9c7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_ITEM
+#define RUST_HIR_TYPE_CHECK_ITEM
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckItem : private TypeCheckBase, private HIR::HIRVisItemVisitor
+{
+public:
+  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 ();
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_ITEM
diff --git a/gcc/rust/typecheck/rust-hir-type-check-path.cc b/gcc/rust/typecheck/rust-hir-type-check-path.cc
new file mode 100644
index 00000000000..84f3b6ea6e6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-path.cc
@@ -0,0 +1,467 @@
+// 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-expr.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
+{
+  HIR::QualifiedPathType qual_path_type = expr.get_path_type ();
+  TyTy::BaseType *root
+    = TypeCheckType::Resolve (qual_path_type.get_type ().get ());
+  if (root->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  if (!qual_path_type.has_as_clause ())
+    {
+      NodeId root_resolved_node_id = UNKNOWN_NODEID;
+      resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root,
+			expr.get_mappings (), expr.get_locus ());
+      return;
+    }
+
+  // Resolve the trait now
+  std::unique_ptr<HIR::TypePath> &trait_path_ref = qual_path_type.get_trait ();
+  TraitReference *trait_ref = TraitResolver::Resolve (*trait_path_ref.get ());
+  if (trait_ref->is_error ())
+    return;
+
+  // does this type actually implement this type-bound?
+  if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
+    return;
+
+  // then we need to look at the next segment to create perform the correct
+  // projection type
+  if (expr.get_segments ().empty ())
+    return;
+
+  // get the predicate for the bound
+  auto specified_bound = get_predicate_from_bound (*trait_path_ref.get ());
+  if (specified_bound.is_error ())
+    return;
+
+  // inherit the bound
+  root->inherit_bounds ({specified_bound});
+
+  // setup the associated types
+  const TraitReference *specified_bound_ref = specified_bound.get ();
+  auto candidates = TypeBoundsProbe::Probe (root);
+  AssociatedImplTrait *associated_impl_trait = nullptr;
+  for (auto &probed_bound : candidates)
+    {
+      const TraitReference *bound_trait_ref = probed_bound.first;
+      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+      HirId impl_block_id = associated_impl->get_mappings ().get_hirid ();
+      AssociatedImplTrait *associated = nullptr;
+      bool found_impl_trait
+	= context->lookup_associated_trait_impl (impl_block_id, &associated);
+      if (found_impl_trait)
+	{
+	  bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref);
+	  bool found_self = associated->get_self ()->can_eq (root, false);
+	  if (found_trait && found_self)
+	    {
+	      associated_impl_trait = associated;
+	      break;
+	    }
+	}
+    }
+
+  if (associated_impl_trait != nullptr)
+    {
+      associated_impl_trait->setup_associated_types (root, specified_bound);
+    }
+
+  // lookup the associated item from the specified bound
+  HIR::PathExprSegment &item_seg = expr.get_segments ().at (0);
+  HIR::PathIdentSegment item_seg_identifier = item_seg.get_segment ();
+  TyTy::TypeBoundPredicateItem item
+    = specified_bound.lookup_associated_item (item_seg_identifier.as_string ());
+  if (item.is_error ())
+    {
+      rust_error_at (item_seg.get_locus (), "unknown associated item");
+      return;
+    }
+
+  // infer the root type
+  infered = item.get_tyty_for_receiver (root);
+
+  // turbo-fish segment path::<ty>
+  if (item_seg.has_generic_args ())
+    {
+      if (!infered->can_substitute ())
+	{
+	  rust_error_at (item_seg.get_locus (),
+			 "substitutions not supported for %s",
+			 infered->as_string ().c_str ());
+	  infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	  return;
+	}
+      infered = SubstMapper::Resolve (infered, expr.get_locus (),
+				      &item_seg.get_generic_args ());
+    }
+
+  // continue on as a path-in-expression
+  const TraitItemReference *trait_item_ref = item.get_raw_item ();
+  NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid ();
+  bool fully_resolved = expr.get_segments ().size () <= 1;
+
+  if (fully_resolved)
+    {
+      resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				      root_resolved_node_id);
+      context->insert_receiver (expr.get_mappings ().get_hirid (), root);
+      return;
+    }
+
+  resolve_segments (root_resolved_node_id, expr.get_segments (), 1, infered,
+		    expr.get_mappings (), expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::PathInExpression &expr)
+{
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  size_t offset = -1;
+  TyTy::BaseType *tyseg = resolve_root_path (expr, &offset, &resolved_node_id);
+  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  if (tyseg->needs_generic_substitutions ())
+    {
+      tyseg = SubstMapper::InferSubst (tyseg, expr.get_locus ());
+    }
+
+  bool fully_resolved = offset == expr.get_segments ().size ();
+  if (fully_resolved)
+    {
+      infered = tyseg;
+      return;
+    }
+
+  resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg,
+		    expr.get_mappings (), expr.get_locus ());
+}
+
+TyTy::BaseType *
+TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
+				  NodeId *root_resolved_node_id)
+{
+  TyTy::BaseType *root_tyty = nullptr;
+  *offset = 0;
+  for (size_t i = 0; i < expr.get_num_segments (); i++)
+    {
+      HIR::PathExprSegment &seg = expr.get_segments ().at (i);
+
+      bool have_more_segments = (expr.get_num_segments () - 1 != i);
+      bool is_root = *offset == 0;
+      NodeId ast_node_id = seg.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);
+	}
+
+      // ref_node_id is the NodeId that the segments refers to.
+      if (ref_node_id == UNKNOWN_NODEID)
+	{
+	  if (root_tyty != nullptr && *offset > 0)
+	    {
+	      // then we can let the impl path probe take over now
+	      return root_tyty;
+	    }
+
+	  rust_error_at (seg.get_locus (),
+			 "failed to type resolve root segment");
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      // node back to HIR
+      HirId ref;
+      if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+	{
+	  rust_error_at (seg.get_locus (), "456 reverse lookup failure");
+	  rust_debug_loc (seg.get_locus (),
+			  "failure with [%s] mappings [%s] ref_node_id [%u]",
+			  seg.as_string ().c_str (),
+			  seg.get_mappings ().as_string ().c_str (),
+			  ref_node_id);
+
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      auto seg_is_module = (nullptr != mappings->lookup_module (ref));
+      auto seg_is_crate = mappings->is_local_hirid_crate (ref);
+      if (seg_is_module || seg_is_crate)
+	{
+	  // A::B::C::this_is_a_module::D::E::F
+	  //          ^^^^^^^^^^^^^^^^
+	  //          Currently handling this.
+	  if (have_more_segments)
+	    {
+	      (*offset)++;
+	      continue;
+	    }
+
+	  // In the case of :
+	  // A::B::C::this_is_a_module
+	  //          ^^^^^^^^^^^^^^^^
+	  // This is an error, we are not expecting a module.
+	  rust_error_at (seg.get_locus (), "expected value");
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      TyTy::BaseType *lookup = nullptr;
+      if (!context->lookup_type (ref, &lookup))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg.get_locus (),
+			     "failed to resolve root segment");
+	      return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // if we have a previous segment type
+      if (root_tyty != nullptr)
+	{
+	  // if this next segment needs substitution we must apply the
+	  // previous type arguments
+	  //
+	  // such as: GenericStruct::<_>::new(123, 456)
+	  if (lookup->needs_generic_substitutions ())
+	    {
+	      if (!root_tyty->needs_generic_substitutions ())
+		{
+		  auto used_args_in_prev_segment
+		    = GetUsedSubstArgs::From (root_tyty);
+		  lookup
+		    = SubstMapperInternal::Resolve (lookup,
+						    used_args_in_prev_segment);
+		}
+	    }
+	}
+
+      // turbo-fish segment path::<ty>
+      if (seg.has_generic_args ())
+	{
+	  if (!lookup->can_substitute ())
+	    {
+	      rust_error_at (expr.get_locus (),
+			     "substitutions not supported for %s",
+			     root_tyty->as_string ().c_str ());
+	      return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	    }
+
+	  lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
+					 &seg.get_generic_args ());
+	  if (lookup->get_kind () == TyTy::TypeKind::ERROR)
+	    return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      *root_resolved_node_id = ref_node_id;
+      *offset = *offset + 1;
+      root_tyty = lookup;
+    }
+
+  return root_tyty;
+}
+
+void
+TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
+				 std::vector<HIR::PathExprSegment> &segments,
+				 size_t offset, TyTy::BaseType *tyseg,
+				 const Analysis::NodeMapping &expr_mappings,
+				 Location expr_locus)
+{
+  NodeId resolved_node_id = root_resolved_node_id;
+  TyTy::BaseType *prev_segment = tyseg;
+  bool reciever_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM;
+
+  for (size_t i = offset; i < segments.size (); i++)
+    {
+      HIR::PathExprSegment &seg = segments.at (i);
+
+      bool probe_bounds = true;
+      bool probe_impls = !reciever_is_generic;
+      bool ignore_mandatory_trait_items = !reciever_is_generic;
+
+      // probe the path is done in two parts one where we search impls if no
+      // candidate is found then we search extensions from traits
+      auto candidates
+	= PathProbeType::Probe (prev_segment, seg.get_segment (), probe_impls,
+				false, ignore_mandatory_trait_items);
+      if (candidates.size () == 0)
+	{
+	  candidates
+	    = PathProbeType::Probe (prev_segment, seg.get_segment (), false,
+				    probe_bounds, ignore_mandatory_trait_items);
+
+	  if (candidates.size () == 0)
+	    {
+	      rust_error_at (
+		seg.get_locus (),
+		"failed to resolve path segment using an impl Probe");
+	      return;
+	    }
+	}
+
+      if (candidates.size () > 1)
+	{
+	  ReportMultipleCandidateError::Report (candidates, seg.get_segment (),
+						seg.get_locus ());
+	  return;
+	}
+
+      auto &candidate = candidates.at (0);
+      prev_segment = tyseg;
+      tyseg = candidate.ty;
+
+      HIR::ImplBlock *associated_impl_block = nullptr;
+      if (candidate.is_enum_candidate ())
+	{
+	  const TyTy::VariantDef *variant = candidate.item.enum_field.variant;
+
+	  HirId variant_id = variant->get_id ();
+	  HIR::Item *enum_item = mappings->lookup_hir_item (variant_id);
+	  rust_assert (enum_item != nullptr);
+
+	  resolved_node_id = enum_item->get_mappings ().get_nodeid ();
+
+	  // insert the id of the variant we are resolved to
+	  context->insert_variant_definition (expr_mappings.get_hirid (),
+					      variant_id);
+	}
+      else if (candidate.is_impl_candidate ())
+	{
+	  resolved_node_id
+	    = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
+
+	  associated_impl_block = candidate.item.impl.parent;
+	}
+      else
+	{
+	  resolved_node_id
+	    = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+	  // lookup the associated-impl-trait
+	  HIR::ImplBlock *impl = candidate.item.trait.impl;
+	  if (impl != nullptr)
+	    {
+	      // get the associated impl block
+	      associated_impl_block = impl;
+	    }
+	}
+
+      if (associated_impl_block != nullptr)
+	{
+	  // get the type of the parent Self
+	  HirId impl_ty_id
+	    = associated_impl_block->get_type ()->get_mappings ().get_hirid ();
+	  TyTy::BaseType *impl_block_ty = nullptr;
+	  bool ok = context->lookup_type (impl_ty_id, &impl_block_ty);
+	  rust_assert (ok);
+
+	  if (impl_block_ty->needs_generic_substitutions ())
+	    impl_block_ty
+	      = SubstMapper::InferSubst (impl_block_ty, seg.get_locus ());
+
+	  prev_segment = prev_segment->unify (impl_block_ty);
+	}
+
+      if (tyseg->needs_generic_substitutions ())
+	{
+	  if (!prev_segment->needs_generic_substitutions ())
+	    {
+	      auto used_args_in_prev_segment
+		= GetUsedSubstArgs::From (prev_segment);
+
+	      if (!used_args_in_prev_segment.is_error ())
+		{
+		  if (SubstMapperInternal::mappings_are_bound (
+			tyseg, used_args_in_prev_segment))
+		    {
+		      tyseg = SubstMapperInternal::Resolve (
+			tyseg, used_args_in_prev_segment);
+		    }
+		}
+	    }
+	}
+
+      if (seg.has_generic_args ())
+	{
+	  if (!tyseg->can_substitute ())
+	    {
+	      rust_error_at (expr_locus, "substitutions not supported for %s",
+			     tyseg->as_string ().c_str ());
+	      return;
+	    }
+
+	  tyseg = SubstMapper::Resolve (tyseg, expr_locus,
+					&seg.get_generic_args ());
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+      else if (tyseg->needs_generic_substitutions () && !reciever_is_generic)
+	{
+	  Location locus = seg.get_locus ();
+	  tyseg = SubstMapper::InferSubst (tyseg, locus);
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+    }
+
+  rust_assert (resolved_node_id != UNKNOWN_NODEID);
+  if (tyseg->needs_generic_substitutions () && !reciever_is_generic)
+    {
+      Location locus = segments.back ().get_locus ();
+      tyseg = SubstMapper::InferSubst (tyseg, locus);
+      if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+
+  context->insert_receiver (expr_mappings.get_hirid (), prev_segment);
+
+  // name scope first
+  if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+    {
+      resolver->insert_resolved_name (expr_mappings.get_nodeid (),
+				      resolved_node_id);
+    }
+  // check the type scope
+  else if (resolver->get_type_scope ().decl_was_declared_here (
+	     resolved_node_id))
+    {
+      resolver->insert_resolved_type (expr_mappings.get_nodeid (),
+				      resolved_node_id);
+    }
+
+  infered = tyseg;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
new file mode 100644
index 00000000000..429511d0292
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
@@ -0,0 +1,416 @@
+// 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-pattern.h"
+#include "rust-hir-type-check-expr.h"
+
+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)
+{
+  infered = TypeCheckExpr::Resolve (&pattern);
+}
+
+void
+TypeCheckPattern::visit (HIR::TupleStructPattern &pattern)
+{
+  infered = TypeCheckExpr::Resolve (&pattern.get_path ());
+  if (infered->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  rust_assert (infered->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (infered);
+  rust_assert (adt->number_of_variants () > 0);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = context->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+
+  // error[E0532]: expected tuple struct or tuple variant, found struct variant
+  // `Foo::D`
+  if (variant->get_variant_type () != TyTy::VariantDef::VariantType::TUPLE)
+    {
+      std::string variant_type
+	= TyTy::VariantDef::variant_type_string (variant->get_variant_type ());
+
+      rust_error_at (
+	pattern.get_locus (),
+	"expected tuple struct or tuple variant, found %s variant %<%s::%s%>",
+	variant_type.c_str (), adt->get_name ().c_str (),
+	variant->get_identifier ().c_str ());
+      return;
+    }
+
+  // check the elements
+  // error[E0023]: this pattern has 2 fields, but the corresponding tuple
+  // variant has 1 field
+  // error[E0023]: this pattern has 0 fields, but the corresponding tuple
+  // variant has 1 field
+
+  std::unique_ptr<HIR::TupleStructItems> &items = pattern.get_items ();
+  switch (items->get_item_type ())
+    {
+      case HIR::TupleStructItems::RANGE: {
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+
+      case HIR::TupleStructItems::NO_RANGE: {
+	HIR::TupleStructItemsNoRange &items_no_range
+	  = static_cast<HIR::TupleStructItemsNoRange &> (*items.get ());
+
+	if (items_no_range.get_patterns ().size () != variant->num_fields ())
+	  {
+	    rust_error_at (
+	      pattern.get_locus (),
+	      "this pattern has %lu fields but the corresponding "
+	      "tuple variant has %lu field",
+	      (unsigned long) items_no_range.get_patterns ().size (),
+	      (unsigned long) variant->num_fields ());
+	    // we continue on to try and setup the types as best we can for
+	    // type checking
+	  }
+
+	// iterate the fields and set them up, I wish we had ZIP
+	size_t i = 0;
+	for (auto &pattern : items_no_range.get_patterns ())
+	  {
+	    if (i >= variant->num_fields ())
+	      break;
+
+	    TyTy::StructFieldType *field = variant->get_field_at_index (i++);
+	    TyTy::BaseType *fty = field->get_field_type ();
+
+	    // setup the type on this pattern type
+	    context->insert_type (pattern->get_pattern_mappings (), fty);
+	  }
+      }
+      break;
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::StructPattern &pattern)
+{
+  infered = TypeCheckExpr::Resolve (&pattern.get_path ());
+  if (infered->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  rust_assert (infered->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (infered);
+  rust_assert (adt->number_of_variants () > 0);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = context->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+
+  // error[E0532]: expected tuple struct or tuple variant, found struct variant
+  // `Foo::D`
+  if (variant->get_variant_type () != TyTy::VariantDef::VariantType::STRUCT)
+    {
+      std::string variant_type
+	= TyTy::VariantDef::variant_type_string (variant->get_variant_type ());
+      rust_error_at (pattern.get_locus (),
+		     "expected struct variant, found %s variant %s",
+		     variant_type.c_str (),
+		     variant->get_identifier ().c_str ());
+      return;
+    }
+
+  // check the elements
+  // error[E0027]: pattern does not mention fields `x`, `y`
+  // error[E0026]: variant `Foo::D` does not have a field named `b`
+
+  std::vector<std::string> named_fields;
+  auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+	{
+	  case HIR::StructPatternField::ItemType::TUPLE_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT: {
+	    HIR::StructPatternFieldIdent &ident
+	      = static_cast<HIR::StructPatternFieldIdent &> (*field.get ());
+
+	    TyTy::StructFieldType *field = nullptr;
+	    if (!variant->lookup_field (ident.get_identifier (), &field,
+					nullptr))
+	      {
+		rust_error_at (ident.get_locus (),
+			       "variant %s does not have a field named %s",
+			       variant->get_identifier ().c_str (),
+			       ident.get_identifier ().c_str ());
+		break;
+	      }
+	    named_fields.push_back (ident.get_identifier ());
+
+	    // setup the type on this pattern
+	    TyTy::BaseType *fty = field->get_field_type ();
+	    context->insert_type (ident.get_mappings (), fty);
+	  }
+	  break;
+	}
+    }
+
+  if (named_fields.size () != variant->num_fields ())
+    {
+      std::map<std::string, bool> missing_names;
+
+      // populate with all fields
+      for (auto &field : variant->get_fields ())
+	missing_names[field->get_name ()] = true;
+
+      // then eliminate with named_fields
+      for (auto &named : named_fields)
+	missing_names.erase (named);
+
+      // then get the list of missing names
+      size_t i = 0;
+      std::string missing_fields_str;
+      for (auto it = missing_names.begin (); it != missing_names.end (); it++)
+	{
+	  bool has_next = (i + 1) < missing_names.size ();
+	  missing_fields_str += it->first + (has_next ? ", " : "");
+	  i++;
+	}
+
+      rust_error_at (pattern.get_locus (), "pattern does not mention fields %s",
+		     missing_fields_str.c_str ());
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::WildcardPattern &pattern)
+{
+  // wildcard patterns within the MatchArm's are simply just the same type as
+  // the parent
+  infered = parent->clone ();
+  infered->set_ref (pattern.get_pattern_mappings ().get_hirid ());
+}
+
+void
+TypeCheckPattern::visit (HIR::TuplePattern &pattern)
+{
+  std::unique_ptr<HIR::TuplePatternItems> items;
+  switch (pattern.get_items ()->get_pattern_type ())
+    {
+      case HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
+	HIR::TuplePatternItemsMultiple &ref
+	  = *static_cast<HIR::TuplePatternItemsMultiple *> (
+	    pattern.get_items ().get ());
+
+	std::vector<TyTy::TyVar> pattern_elems;
+	for (size_t i = 0; i < ref.get_patterns ().size (); i++)
+	  {
+	    auto &p = ref.get_patterns ()[i];
+	    TyTy::BaseType *par_type = parent;
+	    if (parent->get_kind () == TyTy::TUPLE)
+	      {
+		TyTy::TupleType &par = *static_cast<TyTy::TupleType *> (parent);
+		par_type = par.get_field (i);
+	      }
+
+	    TyTy::BaseType *elem
+	      = TypeCheckPattern::Resolve (p.get (), par_type);
+	    pattern_elems.push_back (TyTy::TyVar (elem->get_ref ()));
+	  }
+	infered
+	  = new TyTy::TupleType (pattern.get_pattern_mappings ().get_hirid (),
+				 pattern.get_locus (), pattern_elems);
+      }
+      break;
+
+      case HIR::TuplePatternItems::TuplePatternItemType::RANGED: {
+	// HIR::TuplePatternItemsRanged &ref
+	//   = *static_cast<HIR::TuplePatternItemsRanged *> (
+	//     pattern.get_items ().get ());
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::LiteralPattern &pattern)
+{
+  infered = resolve_literal (pattern.get_pattern_mappings (),
+			     pattern.get_literal (), pattern.get_locus ());
+}
+
+void
+TypeCheckPattern::visit (HIR::RangePattern &pattern)
+{
+  // Resolve the upper and lower bounds, and ensure they are compatible types
+  TyTy::BaseType *upper = nullptr, *lower = nullptr;
+
+  // TODO: It would be nice to factor this out into a helper since the logic for
+  // both bounds is exactly the same...
+  switch (pattern.get_upper_bound ()->get_bound_type ())
+    {
+      case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
+	HIR::RangePatternBoundLiteral &ref
+	  = *static_cast<HIR::RangePatternBoundLiteral *> (
+	    pattern.get_upper_bound ().get ());
+
+	HIR::Literal lit = ref.get_literal ();
+
+	upper = resolve_literal (pattern.get_pattern_mappings (), lit,
+				 pattern.get_locus ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::PATH: {
+	HIR::RangePatternBoundPath &ref
+	  = *static_cast<HIR::RangePatternBoundPath *> (
+	    pattern.get_upper_bound ().get ());
+
+	upper = TypeCheckExpr::Resolve (&ref.get_path ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	HIR::RangePatternBoundQualPath &ref
+	  = *static_cast<HIR::RangePatternBoundQualPath *> (
+	    pattern.get_upper_bound ().get ());
+
+	upper = TypeCheckExpr::Resolve (&ref.get_qualified_path ());
+      }
+      break;
+    }
+
+  switch (pattern.get_lower_bound ()->get_bound_type ())
+    {
+      case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
+	HIR::RangePatternBoundLiteral &ref
+	  = *static_cast<HIR::RangePatternBoundLiteral *> (
+	    pattern.get_lower_bound ().get ());
+
+	HIR::Literal lit = ref.get_literal ();
+
+	lower = resolve_literal (pattern.get_pattern_mappings (), lit,
+				 pattern.get_locus ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::PATH: {
+	HIR::RangePatternBoundPath &ref
+	  = *static_cast<HIR::RangePatternBoundPath *> (
+	    pattern.get_lower_bound ().get ());
+
+	lower = TypeCheckExpr::Resolve (&ref.get_path ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	HIR::RangePatternBoundQualPath &ref
+	  = *static_cast<HIR::RangePatternBoundQualPath *> (
+	    pattern.get_lower_bound ().get ());
+
+	lower = TypeCheckExpr::Resolve (&ref.get_qualified_path ());
+      }
+      break;
+    }
+
+  infered = upper->unify (lower);
+}
+
+void
+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
new file mode 100644
index 00000000000..8af106033b7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_PATTERN
+#define RUST_HIR_TYPE_CHECK_PATTERN
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckPattern : public TypeCheckBase, public HIR::HIRPatternVisitor
+{
+public:
+  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);
+
+  static TyTy::BaseType *
+  typecheck_range_pattern_bound (HIR::RangePatternBound *bound,
+				 Analysis::NodeMapping mappings,
+				 Location locus);
+
+  TyTy::BaseType *parent;
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_PATTERN
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
new file mode 100644
index 00000000000..a79f17a59ce
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.h
@@ -0,0 +1,96 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_STMT
+#define RUST_HIR_TYPE_CHECK_STMT
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckStmt : private TypeCheckBase, private HIR::HIRStmtVisitor
+{
+public:
+  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::EnumItemStruct &) override
+  { /* TODO? */
+  }
+  void visit (HIR::EnumItem &item) override
+  { /* TODO? */
+  }
+  void visit (HIR::EnumItemDiscriminant &) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypePathSegmentFunction &segment) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypePath &path) override
+  { /* TODO? */
+  }
+  void visit (HIR::QualifiedPathInType &path) override
+  { /* TODO? */
+  }
+  void visit (HIR::Module &module) override
+  { /* TODO? */
+  }
+  void visit (HIR::ExternCrate &crate) override
+  { /* TODO? */
+  }
+  void visit (HIR::UseDeclaration &use_decl) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypeAlias &type_alias) override
+  { /* TODO? */
+  }
+  void visit (HIR::StaticItem &static_item) override
+  { /* TODO? */
+  }
+  void visit (HIR::Trait &trait) override
+  { /* TODO? */
+  }
+  void visit (HIR::ImplBlock &impl) override
+  { /* TODO? */
+  }
+
+private:
+  TypeCheckStmt () : TypeCheckBase (), infered (nullptr) {}
+
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_STMT
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct-field.h b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
new file mode 100644
index 00000000000..22af1aad4c3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_STRUCT_FIELD
+#define RUST_HIR_TYPE_CHECK_STRUCT_FIELD
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckStructExpr : public TypeCheckBase
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::StructExprStructFields *expr);
+
+protected:
+  void resolve (HIR::StructExprStructFields &struct_expr);
+
+  void visit (HIR::StructExprFieldIdentifierValue &field);
+  void visit (HIR::StructExprFieldIndexValue &field);
+  void visit (HIR::StructExprFieldIdentifier &field);
+
+private:
+  TypeCheckStructExpr (HIR::Expr *e);
+
+  // result
+  TyTy::BaseType *resolved;
+
+  // internal state:
+  TyTy::ADTType *struct_path_resolved;
+  TyTy::VariantDef *variant;
+  TyTy::BaseType *resolved_field_value_expr;
+  std::set<std::string> fields_assigned;
+  std::map<size_t, HIR::StructExprField *> adtFieldIndexToField;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_STRUCT_FIELD
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct.cc b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
new file mode 100644
index 00000000000..b2261e8cdb3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
@@ -0,0 +1,340 @@
+// 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.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-struct-field.h"
+
+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::resolve (HIR::StructExprStructFields &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;
+    }
+
+  struct_path_resolved = static_cast<TyTy::ADTType *> (struct_path_ty);
+  TyTy::ADTType *struct_def = struct_path_resolved;
+  if (struct_expr.has_struct_base ())
+    {
+      TyTy::BaseType *base_resolved
+	= TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ());
+      struct_def = static_cast<TyTy::ADTType *> (
+	struct_path_resolved->unify (base_resolved));
+      if (struct_def == nullptr)
+	{
+	  rust_fatal_error (struct_expr.struct_base->base_struct->get_locus (),
+			    "incompatible types for base struct reference");
+	  return;
+	}
+    }
+
+  // figure out the variant
+  if (struct_path_resolved->is_enum ())
+    {
+      // lookup variant id
+      HirId variant_id;
+      bool ok = context->lookup_variant_definition (
+	struct_expr.get_struct_name ().get_mappings ().get_hirid (),
+	&variant_id);
+      rust_assert (ok);
+
+      ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+  else
+    {
+      rust_assert (struct_path_resolved->number_of_variants () == 1);
+      variant = struct_path_resolved->get_variants ().at (0);
+    }
+
+  std::vector<TyTy::StructFieldType *> infered_fields;
+  bool ok = true;
+
+  for (auto &field : struct_expr.get_fields ())
+    {
+      resolved_field_value_expr = nullptr;
+
+      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 (),
+			    "failed to resolve type for field");
+	  ok = false;
+	  break;
+	}
+
+      context->insert_type (field->get_mappings (), resolved_field_value_expr);
+    }
+
+  // something failed setting up the fields
+  if (!ok)
+    {
+      rust_error_at (struct_expr.get_locus (),
+		     "constructor type resolution failure");
+      return;
+    }
+
+  // check the arguments are all assigned and fix up the ordering
+  if (fields_assigned.size () != variant->num_fields ())
+    {
+      if (struct_def->is_union ())
+	{
+	  if (fields_assigned.size () != 1 || struct_expr.has_struct_base ())
+	    {
+	      rust_error_at (
+		struct_expr.get_locus (),
+		"union must have exactly one field variant assigned");
+	      return;
+	    }
+	}
+      else if (!struct_expr.has_struct_base ())
+	{
+	  rust_error_at (struct_expr.get_locus (),
+			 "constructor is missing fields");
+	  return;
+	}
+      else
+	{
+	  // we have a struct base to assign the missing fields from.
+	  // the missing fields can be implicit FieldAccessExprs for the value
+	  std::set<std::string> missing_fields;
+	  for (auto &field : variant->get_fields ())
+	    {
+	      auto it = fields_assigned.find (field->get_name ());
+	      if (it == fields_assigned.end ())
+		missing_fields.insert (field->get_name ());
+	    }
+
+	  // we can generate FieldAccessExpr or TupleAccessExpr for the
+	  // values of the missing fields.
+	  for (auto &missing : missing_fields)
+	    {
+	      HIR::Expr *receiver
+		= struct_expr.struct_base->base_struct->clone_expr_impl ();
+
+	      HIR::StructExprField *implicit_field = nullptr;
+
+	      AST::AttrVec outer_attribs;
+	      auto crate_num = mappings->get_current_crate ();
+	      Analysis::NodeMapping mapping (
+		crate_num,
+		struct_expr.struct_base->base_struct->get_mappings ()
+		  .get_nodeid (),
+		mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
+
+	      HIR::Expr *field_value = new HIR::FieldAccessExpr (
+		mapping, std::unique_ptr<HIR::Expr> (receiver), missing,
+		std::move (outer_attribs),
+		struct_expr.struct_base->base_struct->get_locus ());
+
+	      implicit_field = new HIR::StructExprFieldIdentifierValue (
+		mapping, missing, std::unique_ptr<HIR::Expr> (field_value),
+		struct_expr.struct_base->base_struct->get_locus ());
+
+	      size_t field_index;
+	      bool ok = variant->lookup_field (missing, nullptr, &field_index);
+	      rust_assert (ok);
+
+	      adtFieldIndexToField[field_index] = implicit_field;
+	      struct_expr.get_fields ().push_back (
+		std::unique_ptr<HIR::StructExprField> (implicit_field));
+	    }
+	}
+    }
+
+  if (struct_def->is_union ())
+    {
+      // There is exactly one field in this constructor, we need to
+      // figure out the field index to make sure we initialize the
+      // right union field.
+      for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
+	{
+	  if (adtFieldIndexToField[i])
+	    {
+	      struct_expr.union_index = i;
+	      break;
+	    }
+	}
+      rust_assert (struct_expr.union_index != -1);
+    }
+  else
+    {
+      // everything is ok, now we need to ensure all field values are ordered
+      // correctly. The GIMPLE backend uses a simple algorithm that assumes each
+      // assigned field in the constructor is in the same order as the field in
+      // the type
+      for (auto &field : struct_expr.get_fields ())
+	field.release ();
+
+      std::vector<std::unique_ptr<HIR::StructExprField> > ordered_fields;
+      for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
+	{
+	  ordered_fields.push_back (
+	    std::unique_ptr<HIR::StructExprField> (adtFieldIndexToField[i]));
+	}
+      struct_expr.set_fields_as_owner (std::move (ordered_fields));
+    }
+
+  resolved = struct_def;
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field)
+{
+  auto it = fields_assigned.find (field.field_name);
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field.field_name, &field_type, &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+    {
+      fields_assigned.insert (field.field_name);
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field)
+{
+  std::string field_name (std::to_string (field.get_tuple_index ()));
+  auto it = fields_assigned.find (field_name);
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field_name, &field_type, &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+    {
+      fields_assigned.insert (field_name);
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
+{
+  auto it = fields_assigned.find (field.get_field_name ());
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field.get_field_name (), &field_type,
+				   &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  // we can make the field look like a path expr to take advantage of existing
+  // code
+  Analysis::NodeMapping mappings_copy1 = field.get_mappings ();
+  Analysis::NodeMapping mappings_copy2 = field.get_mappings ();
+
+  HIR::PathIdentSegment ident_seg (field.get_field_name ());
+  HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
+			    HIR::GenericArgs::create_empty ());
+  HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
+			      {});
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr);
+
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+
+    {
+      fields_assigned.insert (field.get_field_name ());
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
new file mode 100644
index 00000000000..27f36b642fc
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
@@ -0,0 +1,364 @@
+// 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-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)
+{
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
+  TypeCheckTopLevel resolver;
+  vis_item.accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevel::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
+TypeCheckTopLevel::visit (HIR::TupleStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    resolve_generic_params (struct_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  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 ()};
+
+  // its a single variant ADT
+  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(X)] 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);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Module &module)
+{
+  for (auto &item : module.get_items ())
+    TypeCheckTopLevel::Resolve (*item.get ());
+}
+
+void
+TypeCheckTopLevel::visit (HIR::StructStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    resolve_generic_params (struct_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  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 ()};
+
+  // its a single variant ADT
+  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(X)] 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);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Enum &enum_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (enum_decl.has_generics ())
+    resolve_generic_params (enum_decl.get_generic_params (), substitutions);
+
+  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 ()};
+
+  // multi variant ADT
+  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);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Union &union_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (union_decl.has_generics ())
+    resolve_generic_params (union_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : union_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  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);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::StaticItem &var)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (var.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (var.get_expr ());
+
+  context->insert_type (var.get_mappings (), type->unify (expr_type));
+}
+
+void
+TypeCheckTopLevel::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
+TypeCheckTopLevel::visit (HIR::Function &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    resolve_generic_params (function.get_generic_params (), substitutions);
+
+  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->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  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);
+    }
+
+  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);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::ImplBlock &impl_block)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (impl_block.has_generics ())
+    resolve_generic_params (impl_block.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : impl_block.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  auto self = TypeCheckType::Resolve (impl_block.get_type ().get ());
+  if (self->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  for (auto &impl_item : impl_block.get_impl_items ())
+    TypeCheckTopLevelImplItem::Resolve (impl_item.get (), self, substitutions);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::ExternBlock &extern_block)
+{
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block);
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
new file mode 100644
index 00000000000..d0db07d7281
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_TOPLEVEL
+#define RUST_HIR_TYPE_CHECK_TOPLEVEL
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckTopLevel : private TypeCheckBase, public HIR::HIRVisItemVisitor
+{
+public:
+  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::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::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 ();
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_TOPLEVEL
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
new file mode 100644
index 00000000000..3538d77b220
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -0,0 +1,838 @@
+// 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-type.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+HIR::GenericArgs
+TypeCheckResolveGenericArguments::resolve (HIR::TypePathSegment *segment)
+{
+  TypeCheckResolveGenericArguments resolver (segment->get_locus ());
+  switch (segment->get_type ())
+    {
+    case HIR::TypePathSegment::SegmentType::GENERIC:
+      resolver.visit (static_cast<HIR::TypePathSegmentGeneric &> (*segment));
+      break;
+
+    default:
+      break;
+    }
+  return resolver.args;
+}
+
+void
+TypeCheckResolveGenericArguments::visit (HIR::TypePathSegmentGeneric &generic)
+{
+  args = generic.get_generic_args ();
+}
+
+TyTy::BaseType *
+TypeCheckType::Resolve (HIR::Type *type)
+{
+  TypeCheckType resolver (type->get_mappings ().get_hirid ());
+  type->accept_vis (resolver);
+  rust_assert (resolver.translated != nullptr);
+  resolver.context->insert_type (type->get_mappings (), resolver.translated);
+  return resolver.translated;
+}
+
+void
+TypeCheckType::visit (HIR::BareFunctionType &fntype)
+{
+  TyTy::BaseType *return_type
+    = fntype.has_return_type ()
+	? TypeCheckType::Resolve (fntype.get_return_type ().get ())
+	: TyTy::TupleType::get_unit_type (fntype.get_mappings ().get_hirid ());
+
+  std::vector<TyTy::TyVar> params;
+  for (auto &param : fntype.get_function_params ())
+    {
+      TyTy::BaseType *ptype = TypeCheckType::Resolve (param.get_type ().get ());
+      params.push_back (TyTy::TyVar (ptype->get_ref ()));
+    }
+
+  translated = new TyTy::FnPtr (fntype.get_mappings ().get_hirid (),
+				fntype.get_locus (), std::move (params),
+				TyTy::TyVar (return_type->get_ref ()));
+}
+
+void
+TypeCheckType::visit (HIR::TupleType &tuple)
+{
+  if (tuple.is_unit_type ())
+    {
+      auto unit_node_id = resolver->get_unit_type_node_id ();
+      if (!context->lookup_builtin (unit_node_id, &translated))
+	{
+	  rust_error_at (tuple.get_locus (),
+			 "failed to lookup builtin unit type");
+	}
+      return;
+    }
+
+  std::vector<TyTy::TyVar> fields;
+  for (auto &elem : tuple.get_elems ())
+    {
+      auto field_ty = TypeCheckType::Resolve (elem.get ());
+      fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
+    }
+
+  translated = new TyTy::TupleType (tuple.get_mappings ().get_hirid (),
+				    tuple.get_locus (), fields);
+}
+
+void
+TypeCheckType::visit (HIR::TypePath &path)
+{
+  // lookup the Node this resolves to
+  NodeId ref;
+  auto nid = path.get_mappings ().get_nodeid ();
+  bool is_fully_resolved = resolver->lookup_resolved_type (nid, &ref);
+
+  TyTy::BaseType *lookup = nullptr;
+  if (!is_fully_resolved)
+    {
+      // this can happen so we need to look up the root then resolve the
+      // remaining segments if possible
+      size_t offset = 0;
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      TyTy::BaseType *root
+	= resolve_root_path (path, &offset, &resolved_node_id);
+
+      rust_assert (root != nullptr);
+      if (root->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      translated
+	= resolve_segments (resolved_node_id, path.get_mappings ().get_hirid (),
+			    path.get_segments (), offset, root,
+			    path.get_mappings (), path.get_locus ());
+      return;
+    }
+
+  HirId hir_lookup;
+  if (!context->lookup_type_by_node_id (ref, &hir_lookup))
+    {
+      rust_error_at (path.get_locus (), "failed to lookup HIR %d for node '%s'",
+		     ref, path.as_string ().c_str ());
+      return;
+    }
+
+  if (!context->lookup_type (hir_lookup, &lookup))
+    {
+      rust_error_at (path.get_locus (), "failed to lookup HIR TyTy");
+      return;
+    }
+
+  TyTy::BaseType *path_type = lookup->clone ();
+  path_type->set_ref (path.get_mappings ().get_hirid ());
+
+  HIR::TypePathSegment *final_seg = path.get_final_segment ().get ();
+  HIR::GenericArgs args = TypeCheckResolveGenericArguments::resolve (final_seg);
+
+  bool is_big_self = final_seg->is_ident_only ()
+		     && (final_seg->as_string ().compare ("Self") == 0);
+
+  if (path_type->needs_generic_substitutions ())
+    {
+      if (is_big_self)
+	{
+	  translated = path_type;
+	  return;
+	}
+
+      translated = SubstMapper::Resolve (path_type, path.get_locus (), &args);
+    }
+  else if (!args.is_empty ())
+    {
+      rust_error_at (path.get_locus (),
+		     "TypePath %s declares generic arguments but "
+		     "the type %s does not have any",
+		     path.as_string ().c_str (),
+		     path_type->as_string ().c_str ());
+    }
+  else
+    {
+      translated = path_type;
+    }
+}
+
+void
+TypeCheckType::visit (HIR::QualifiedPathInType &path)
+{
+  HIR::QualifiedPathType qual_path_type = path.get_path_type ();
+  TyTy::BaseType *root
+    = TypeCheckType::Resolve (qual_path_type.get_type ().get ());
+  if (root->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_debug_loc (path.get_locus (), "failed to resolve the root");
+      return;
+    }
+
+  if (!qual_path_type.has_as_clause ())
+    {
+      // then this is just a normal path-in-expression
+      NodeId root_resolved_node_id = UNKNOWN_NODEID;
+      bool ok = resolver->lookup_resolved_type (
+	qual_path_type.get_type ()->get_mappings ().get_nodeid (),
+	&root_resolved_node_id);
+      rust_assert (ok);
+
+      translated = resolve_segments (root_resolved_node_id,
+				     path.get_mappings ().get_hirid (),
+				     path.get_segments (), 0, translated,
+				     path.get_mappings (), path.get_locus ());
+
+      return;
+    }
+
+  // Resolve the trait now
+  TraitReference *trait_ref
+    = TraitResolver::Resolve (*qual_path_type.get_trait ().get ());
+  if (trait_ref->is_error ())
+    return;
+
+  // does this type actually implement this type-bound?
+  if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
+    {
+      rust_error_at (qual_path_type.get_locus (),
+		     "root does not satisfy specified trait-bound");
+      return;
+    }
+
+  // get the predicate for the bound
+  auto specified_bound
+    = get_predicate_from_bound (*qual_path_type.get_trait ().get ());
+  if (specified_bound.is_error ())
+    return;
+
+  // inherit the bound
+  root->inherit_bounds ({specified_bound});
+
+  // setup the associated types
+  const TraitReference *specified_bound_ref = specified_bound.get ();
+  auto candidates = TypeBoundsProbe::Probe (root);
+  AssociatedImplTrait *associated_impl_trait = nullptr;
+  for (auto &probed_bound : candidates)
+    {
+      const TraitReference *bound_trait_ref = probed_bound.first;
+      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+      HirId impl_block_id = associated_impl->get_mappings ().get_hirid ();
+      AssociatedImplTrait *associated = nullptr;
+      bool found_impl_trait
+	= context->lookup_associated_trait_impl (impl_block_id, &associated);
+      if (found_impl_trait)
+	{
+	  bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref);
+	  bool found_self = associated->get_self ()->can_eq (root, false);
+	  if (found_trait && found_self)
+	    {
+	      associated_impl_trait = associated;
+	      break;
+	    }
+	}
+    }
+
+  if (associated_impl_trait != nullptr)
+    {
+      associated_impl_trait->setup_associated_types (root, specified_bound);
+    }
+
+  // lookup the associated item from the specified bound
+  std::unique_ptr<HIR::TypePathSegment> &item_seg
+    = path.get_associated_segment ();
+  HIR::PathIdentSegment item_seg_identifier = item_seg->get_ident_segment ();
+  TyTy::TypeBoundPredicateItem item
+    = specified_bound.lookup_associated_item (item_seg_identifier.as_string ());
+  if (item.is_error ())
+    {
+      rust_error_at (item_seg->get_locus (), "unknown associated item");
+      return;
+    }
+
+  // infer the root type
+  translated = item.get_tyty_for_receiver (root);
+
+  // turbo-fish segment path::<ty>
+  if (item_seg->get_type () == HIR::TypePathSegment::SegmentType::GENERIC)
+    {
+      HIR::TypePathSegmentGeneric &generic_seg
+	= static_cast<HIR::TypePathSegmentGeneric &> (*item_seg.get ());
+
+      // turbo-fish segment path::<ty>
+      if (generic_seg.has_generic_args ())
+	{
+	  if (!translated->can_substitute ())
+	    {
+	      rust_error_at (item_seg->get_locus (),
+			     "substitutions not supported for %s",
+			     translated->as_string ().c_str ());
+	      translated
+		= new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	      return;
+	    }
+	  translated = SubstMapper::Resolve (translated, path.get_locus (),
+					     &generic_seg.get_generic_args ());
+	}
+    }
+
+  // continue on as a path-in-expression
+  const TraitItemReference *trait_item_ref = item.get_raw_item ();
+  NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid ();
+  bool fully_resolved = path.get_segments ().empty ();
+  if (fully_resolved)
+    {
+      resolver->insert_resolved_type (path.get_mappings ().get_nodeid (),
+				      root_resolved_node_id);
+      context->insert_receiver (path.get_mappings ().get_hirid (), root);
+      return;
+    }
+
+  translated
+    = resolve_segments (root_resolved_node_id,
+			path.get_mappings ().get_hirid (), path.get_segments (),
+			0, translated, path.get_mappings (), path.get_locus ());
+}
+
+TyTy::BaseType *
+TypeCheckType::resolve_root_path (HIR::TypePath &path, size_t *offset,
+				  NodeId *root_resolved_node_id)
+{
+  TyTy::BaseType *root_tyty = nullptr;
+  *offset = 0;
+  for (size_t i = 0; i < path.get_num_segments (); i++)
+    {
+      std::unique_ptr<HIR::TypePathSegment> &seg = path.get_segments ().at (i);
+
+      bool have_more_segments = (path.get_num_segments () - 1 != i);
+      bool is_root = *offset == 0;
+      NodeId ast_node_id = seg->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);
+	}
+
+      // ref_node_id is the NodeId that the segments refers to.
+      if (ref_node_id == UNKNOWN_NODEID)
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "unknown reference for resolved name: %<%s%>",
+			     seg->get_ident_segment ().as_string ().c_str ());
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // node back to HIR
+      HirId ref = UNKNOWN_HIRID;
+      if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (), "789 reverse lookup failure");
+	      rust_debug_loc (
+		seg->get_locus (),
+		"failure with [%s] mappings [%s] ref_node_id [%u]",
+		seg->as_string ().c_str (),
+		seg->get_mappings ().as_string ().c_str (), ref_node_id);
+
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+
+	  return root_tyty;
+	}
+
+      auto seg_is_module = (nullptr != mappings->lookup_module (ref));
+      auto seg_is_crate = mappings->is_local_hirid_crate (ref);
+      if (seg_is_module || seg_is_crate)
+	{
+	  // A::B::C::this_is_a_module::D::E::F
+	  //          ^^^^^^^^^^^^^^^^
+	  //          Currently handling this.
+	  if (have_more_segments)
+	    {
+	      (*offset)++;
+	      continue;
+	    }
+
+	  // In the case of :
+	  // A::B::C::this_is_a_module
+	  //          ^^^^^^^^^^^^^^^^
+	  // This is an error, we are not expecting a module.
+	  rust_error_at (seg->get_locus (), "expected value");
+	  return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	}
+
+      TyTy::BaseType *lookup = nullptr;
+      if (!context->lookup_type (ref, &lookup))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "failed to resolve root segment");
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // if we have a previous segment type
+      if (root_tyty != nullptr)
+	{
+	  // if this next segment needs substitution we must apply the
+	  // previous type arguments
+	  //
+	  // such as: GenericStruct::<_>::new(123, 456)
+	  if (lookup->needs_generic_substitutions ())
+	    {
+	      if (!root_tyty->needs_generic_substitutions ())
+		{
+		  auto used_args_in_prev_segment
+		    = GetUsedSubstArgs::From (root_tyty);
+		  lookup
+		    = SubstMapperInternal::Resolve (lookup,
+						    used_args_in_prev_segment);
+		}
+	    }
+	}
+
+      // turbo-fish segment path::<ty>
+      if (seg->is_generic_segment ())
+	{
+	  HIR::TypePathSegmentGeneric *generic_segment
+	    = static_cast<HIR::TypePathSegmentGeneric *> (seg.get ());
+
+	  if (!lookup->can_substitute ())
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "substitutions not supported for %s",
+			     lookup->as_string ().c_str ());
+	      return new TyTy::ErrorType (lookup->get_ref ());
+	    }
+	  lookup = SubstMapper::Resolve (lookup, path.get_locus (),
+					 &generic_segment->get_generic_args ());
+	}
+
+      *root_resolved_node_id = ref_node_id;
+      *offset = *offset + 1;
+      root_tyty = lookup;
+    }
+
+  return root_tyty;
+}
+
+TyTy::BaseType *
+TypeCheckType::resolve_segments (
+  NodeId root_resolved_node_id, HirId expr_id,
+  std::vector<std::unique_ptr<HIR::TypePathSegment>> &segments, size_t offset,
+  TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings,
+  Location expr_locus)
+{
+  NodeId resolved_node_id = root_resolved_node_id;
+  TyTy::BaseType *prev_segment = tyseg;
+  for (size_t i = offset; i < segments.size (); i++)
+    {
+      std::unique_ptr<HIR::TypePathSegment> &seg = segments.at (i);
+
+      bool reciever_is_generic
+	= prev_segment->get_kind () == TyTy::TypeKind::PARAM;
+      bool probe_bounds = true;
+      bool probe_impls = !reciever_is_generic;
+      bool ignore_mandatory_trait_items = !reciever_is_generic;
+
+      // probe the path is done in two parts one where we search impls if no
+      // candidate is found then we search extensions from traits
+      auto candidates
+	= PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
+				probe_impls, false,
+				ignore_mandatory_trait_items);
+      if (candidates.size () == 0)
+	{
+	  candidates
+	    = PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
+				    false, probe_bounds,
+				    ignore_mandatory_trait_items);
+
+	  if (candidates.size () == 0)
+	    {
+	      rust_error_at (
+		seg->get_locus (),
+		"failed to resolve path segment using an impl Probe");
+	      return new TyTy::ErrorType (expr_id);
+	    }
+	}
+
+      if (candidates.size () > 1)
+	{
+	  ReportMultipleCandidateError::Report (candidates,
+						seg->get_ident_segment (),
+						seg->get_locus ());
+	  return new TyTy::ErrorType (expr_id);
+	}
+
+      auto &candidate = candidates.at (0);
+      prev_segment = tyseg;
+      tyseg = candidate.ty;
+
+      if (candidate.is_impl_candidate ())
+	{
+	  resolved_node_id
+	    = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
+	}
+      else
+	{
+	  resolved_node_id
+	    = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+	}
+
+      if (seg->is_generic_segment ())
+	{
+	  HIR::TypePathSegmentGeneric *generic_segment
+	    = static_cast<HIR::TypePathSegmentGeneric *> (seg.get ());
+
+	  if (!tyseg->can_substitute ())
+	    {
+	      rust_error_at (expr_locus, "substitutions not supported for %s",
+			     tyseg->as_string ().c_str ());
+	      return new TyTy::ErrorType (expr_id);
+	    }
+
+	  tyseg = SubstMapper::Resolve (tyseg, expr_locus,
+					&generic_segment->get_generic_args ());
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return new TyTy::ErrorType (expr_id);
+	}
+    }
+
+  context->insert_receiver (expr_mappings.get_hirid (), prev_segment);
+  if (tyseg->needs_generic_substitutions ())
+    {
+      Location locus = segments.back ()->get_locus ();
+      if (!prev_segment->needs_generic_substitutions ())
+	{
+	  auto used_args_in_prev_segment
+	    = GetUsedSubstArgs::From (prev_segment);
+	  if (!used_args_in_prev_segment.is_error ())
+	    tyseg
+	      = SubstMapperInternal::Resolve (tyseg, used_args_in_prev_segment);
+	}
+      else
+	{
+	  tyseg = SubstMapper::InferSubst (tyseg, locus);
+	}
+
+      if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	return new TyTy::ErrorType (expr_id);
+    }
+
+  rust_assert (resolved_node_id != UNKNOWN_NODEID);
+
+  // lookup if the name resolver was able to canonically resolve this or not
+  NodeId path_resolved_id = UNKNOWN_NODEID;
+  if (resolver->lookup_resolved_name (expr_mappings.get_nodeid (),
+				      &path_resolved_id))
+    {
+      rust_assert (path_resolved_id == resolved_node_id);
+    }
+  // check the type scope
+  else if (resolver->lookup_resolved_type (expr_mappings.get_nodeid (),
+					   &path_resolved_id))
+    {
+      rust_assert (path_resolved_id == resolved_node_id);
+    }
+  else
+    {
+      // name scope first
+      if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+	{
+	  resolver->insert_resolved_name (expr_mappings.get_nodeid (),
+					  resolved_node_id);
+	}
+      // check the type scope
+      else if (resolver->get_type_scope ().decl_was_declared_here (
+		 resolved_node_id))
+	{
+	  resolver->insert_resolved_type (expr_mappings.get_nodeid (),
+					  resolved_node_id);
+	}
+    }
+
+  return tyseg;
+}
+
+void
+TypeCheckType::visit (HIR::TraitObjectType &type)
+{
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  for (auto &bound : type.get_type_param_bounds ())
+    {
+      if (bound->get_bound_type ()
+	  != HIR::TypeParamBound::BoundType::TRAITBOUND)
+	continue;
+
+      HIR::TypeParamBound &b = *bound.get ();
+      HIR::TraitBound &trait_bound = static_cast<HIR::TraitBound &> (b);
+
+      TyTy::TypeBoundPredicate predicate
+	= get_predicate_from_bound (trait_bound.get_path ());
+
+      if (!predicate.is_error ()
+	  && predicate.is_object_safe (true, type.get_locus ()))
+	specified_bounds.push_back (std::move (predicate));
+    }
+
+  RustIdent ident{CanonicalPath::create_empty (), type.get_locus ()};
+  translated
+    = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), ident,
+				   std::move (specified_bounds));
+}
+
+void
+TypeCheckType::visit (HIR::ArrayType &type)
+{
+  auto capacity_type = TypeCheckExpr::Resolve (type.get_size_expr ());
+  if (capacity_type->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BaseType *expected_ty = nullptr;
+  bool ok = context->lookup_builtin ("usize", &expected_ty);
+  rust_assert (ok);
+  context->insert_type (type.get_size_expr ()->get_mappings (), expected_ty);
+
+  auto unified = expected_ty->unify (capacity_type);
+  if (unified->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ());
+  translated = new TyTy::ArrayType (type.get_mappings ().get_hirid (),
+				    type.get_locus (), *type.get_size_expr (),
+				    TyTy::TyVar (base->get_ref ()));
+}
+
+void
+TypeCheckType::visit (HIR::SliceType &type)
+{
+  TyTy::BaseType *base
+    = TypeCheckType::Resolve (type.get_element_type ().get ());
+  translated
+    = new TyTy::SliceType (type.get_mappings ().get_hirid (), type.get_locus (),
+			   TyTy::TyVar (base->get_ref ()));
+}
+void
+TypeCheckType::visit (HIR::ReferenceType &type)
+{
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ());
+  translated
+    = new TyTy::ReferenceType (type.get_mappings ().get_hirid (),
+			       TyTy::TyVar (base->get_ref ()), type.get_mut ());
+}
+
+void
+TypeCheckType::visit (HIR::RawPointerType &type)
+{
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ());
+  translated
+    = new TyTy::PointerType (type.get_mappings ().get_hirid (),
+			     TyTy::TyVar (base->get_ref ()), type.get_mut ());
+}
+
+void
+TypeCheckType::visit (HIR::InferredType &type)
+{
+  translated = new TyTy::InferType (type.get_mappings ().get_hirid (),
+				    TyTy::InferType::InferTypeKind::GENERAL,
+				    type.get_locus ());
+}
+
+void
+TypeCheckType::visit (HIR::NeverType &type)
+{
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = context->lookup_builtin ("!", &lookup);
+  rust_assert (ok);
+
+  translated = lookup->clone ();
+}
+
+TyTy::ParamType *
+TypeResolveGenericParam::Resolve (HIR::GenericParam *param)
+{
+  TypeResolveGenericParam resolver;
+  switch (param->get_kind ())
+    {
+    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)
+{
+  if (param.has_type ())
+    TypeCheckType::Resolve (param.get_type ().get ());
+
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  if (param.has_type_param_bounds ())
+    {
+      for (auto &bound : param.get_type_param_bounds ())
+	{
+	  switch (bound->get_bound_type ())
+	    {
+	      case HIR::TypeParamBound::BoundType::TRAITBOUND: {
+		HIR::TraitBound *b
+		  = static_cast<HIR::TraitBound *> (bound.get ());
+
+		TyTy::TypeBoundPredicate predicate
+		  = get_predicate_from_bound (b->get_path ());
+		if (!predicate.is_error ())
+		  specified_bounds.push_back (std::move (predicate));
+	      }
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  resolved
+    = new TyTy::ParamType (param.get_type_representation (), param.get_locus (),
+			   param.get_mappings ().get_hirid (), param,
+			   specified_bounds);
+}
+
+void
+ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item)
+{
+  ResolveWhereClauseItem 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 &item)
+{}
+
+void
+ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item)
+{
+  auto &binding_type_path = item.get_bound_type ();
+  TyTy::BaseType *binding = TypeCheckType::Resolve (binding_type_path.get ());
+
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  for (auto &bound : item.get_type_param_bounds ())
+    {
+      switch (bound->get_bound_type ())
+	{
+	  case HIR::TypeParamBound::BoundType::TRAITBOUND: {
+	    HIR::TraitBound *b = static_cast<HIR::TraitBound *> (bound.get ());
+
+	    TyTy::TypeBoundPredicate predicate
+	      = get_predicate_from_bound (b->get_path ());
+	    if (!predicate.is_error ())
+	      specified_bounds.push_back (std::move (predicate));
+	  }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+  binding->inherit_bounds (specified_bounds);
+
+  // When we apply these bounds we must lookup which type this binding
+  // resolves to, as this is the type which will be used during resolution
+  // of the block.
+  NodeId ast_node_id = binding_type_path->get_mappings ().get_nodeid ();
+
+  // then lookup the reference_node_id
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
+    {
+      // FIXME
+      rust_error_at (Location (),
+		     "Failed to lookup type reference for node: %s",
+		     binding_type_path->as_string ().c_str ());
+      return;
+    }
+
+  // node back to HIR
+  HirId ref;
+  if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+    {
+      // FIXME
+      rust_error_at (Location (), "where-clause reverse lookup failure");
+      return;
+    }
+
+  // the base reference for this name _must_ have a type set
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (ref, &lookup))
+    {
+      rust_error_at (mappings->lookup_location (ref),
+		     "Failed to resolve where-clause binding type: %s",
+		     binding_type_path->as_string ().c_str ());
+      return;
+    }
+
+  // FIXME
+  // rust_assert (binding->is_equal (*lookup));
+  lookup->inherit_bounds (specified_bounds);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h
new file mode 100644
index 00000000000..90d5ddbb411
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.h
@@ -0,0 +1,130 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_TYPE
+#define RUST_HIR_TYPE_CHECK_TYPE
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-path-probe.h"
+
+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
+{
+public:
+  static HIR::GenericArgs resolve (HIR::TypePathSegment *segment);
+
+  void visit (HIR::TypePathSegmentGeneric &generic);
+
+private:
+  TypeCheckResolveGenericArguments (Location locus)
+    : TypeCheckBase (), args (HIR::GenericArgs::create_empty (locus))
+  {}
+
+  HIR::GenericArgs args;
+};
+
+class TypeCheckType : public TypeCheckBase, public HIR::HIRTypeVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::Type *type);
+
+  void visit (HIR::BareFunctionType &fntype) override;
+  void visit (HIR::TupleType &tuple) override;
+  void visit (HIR::TypePath &path) override;
+  void visit (HIR::QualifiedPathInType &path) override;
+  void visit (HIR::ArrayType &type) override;
+  void visit (HIR::SliceType &type) override;
+  void visit (HIR::ReferenceType &type) override;
+  void visit (HIR::RawPointerType &type) override;
+  void visit (HIR::InferredType &type) override;
+  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))
+  {}
+
+  TyTy::BaseType *resolve_root_path (HIR::TypePath &path, size_t *offset,
+				     NodeId *root_resolved_node_id);
+
+  TyTy::BaseType *resolve_segments (
+    NodeId root_resolved_node_id, HirId expr_id,
+    std::vector<std::unique_ptr<HIR::TypePathSegment>> &segments, size_t offset,
+    TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings,
+    Location expr_locus);
+
+  TyTy::BaseType *translated;
+};
+
+class TypeResolveGenericParam : public TypeCheckBase
+{
+public:
+  static TyTy::ParamType *Resolve (HIR::GenericParam *param);
+
+protected:
+  void visit (HIR::TypeParam &param);
+  void visit (HIR::LifetimeParam &param);
+  void visit (HIR::ConstGenericParam &param);
+
+private:
+  TypeResolveGenericParam () : TypeCheckBase (), resolved (nullptr) {}
+
+  TyTy::ParamType *resolved;
+};
+
+class ResolveWhereClauseItem : public TypeCheckBase
+{
+public:
+  static void Resolve (HIR::WhereClauseItem &item);
+
+protected:
+  void visit (HIR::LifetimeWhereClauseItem &item);
+  void visit (HIR::TypeBoundWhereClauseItem &item);
+
+private:
+  ResolveWhereClauseItem () : TypeCheckBase () {}
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_TYPE
diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.cc b/gcc/rust/typecheck/rust-hir-type-check-util.cc
new file mode 100644
index 00000000000..e25f431a507
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-util.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2021-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-util.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ImplTypeIterator::go ()
+{
+  for (auto &item : impl.get_impl_items ())
+    {
+      item->accept_vis (*this);
+    }
+}
+
+void
+ImplTypeIterator::visit (HIR::TypeAlias &alias)
+{
+  cb (alias);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.h b/gcc/rust/typecheck/rust-hir-type-check-util.h
new file mode 100644
index 00000000000..1a4b17a3303
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-util.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK_UTIL_H
+#define RUST_HIR_TYPE_CHECK_UTIL_H
+
+#include "rust-system.h"
+#include "rust-hir-visitor.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ImplTypeIterator : public HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  ImplTypeIterator (HIR::ImplBlock &impl,
+		    std::function<void (HIR::TypeAlias &alias)> cb)
+    : impl (impl), cb (cb)
+  {}
+
+  void go ();
+
+  void visit (HIR::TypeAlias &alias) override;
+
+private:
+  HIR::ImplBlock &impl;
+  std::function<void (HIR::TypeAlias &alias)> cb;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_UTIL_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc
new file mode 100644
index 00000000000..c314585cd3d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check.cc
@@ -0,0 +1,295 @@
+// 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.h"
+#include "rust-hir-full.h"
+#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"
+
+extern bool
+saw_errors (void);
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeResolution::Resolve (HIR::Crate &crate)
+{
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    TypeCheckTopLevel::Resolve (*it->get ());
+
+  if (saw_errors ())
+    return;
+
+  OverlappingImplItemPass::go ();
+  if (saw_errors ())
+    return;
+
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    TypeCheckItem::Resolve (*it->get ());
+
+  if (saw_errors ())
+    return;
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = TypeCheckContext::get ();
+
+  // default inference variables if possible
+  context->iterate ([&] (HirId id, TyTy::BaseType *ty) mutable -> bool {
+    // nothing to do
+    if (ty->get_kind () != TyTy::TypeKind::INFER)
+      return true;
+
+    TyTy::InferType *infer_var = static_cast<TyTy::InferType *> (ty);
+    TyTy::BaseType *default_type;
+    bool ok = infer_var->default_type (&default_type);
+    if (!ok)
+      {
+	rust_error_at (mappings->lookup_location (id),
+		       "type annotations needed");
+	return true;
+      }
+    else
+      {
+	auto result = ty->unify (default_type);
+	result->set_ref (id);
+	context->insert_type (
+	  Analysis::NodeMapping (mappings->get_current_crate (), 0, id,
+				 UNKNOWN_LOCAL_DEFID),
+	  result);
+      }
+
+    return true;
+  });
+}
+
+// rust-hir-trait-ref.h
+
+TraitItemReference::TraitItemReference (
+  std::string identifier, bool optional, TraitItemType type,
+  HIR::TraitItem *hir_trait_item, TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions, Location locus)
+  : identifier (identifier), optional_flag (optional), type (type),
+    hir_trait_item (hir_trait_item),
+    inherited_substitutions (std::move (substitutions)), locus (locus),
+    self (self), context (TypeCheckContext::get ())
+{}
+
+TraitItemReference::TraitItemReference (TraitItemReference const &other)
+  : identifier (other.identifier), optional_flag (other.optional_flag),
+    type (other.type), hir_trait_item (other.hir_trait_item),
+    locus (other.locus), self (other.self), context (TypeCheckContext::get ())
+{
+  inherited_substitutions.clear ();
+  inherited_substitutions.reserve (other.inherited_substitutions.size ());
+  for (size_t i = 0; i < other.inherited_substitutions.size (); i++)
+    inherited_substitutions.push_back (
+      other.inherited_substitutions.at (i).clone ());
+}
+
+TraitItemReference &
+TraitItemReference::operator= (TraitItemReference const &other)
+{
+  identifier = other.identifier;
+  optional_flag = other.optional_flag;
+  type = other.type;
+  hir_trait_item = other.hir_trait_item;
+  self = other.self;
+  locus = other.locus;
+  context = other.context;
+
+  inherited_substitutions.clear ();
+  inherited_substitutions.reserve (other.inherited_substitutions.size ());
+  for (size_t i = 0; i < other.inherited_substitutions.size (); i++)
+    inherited_substitutions.push_back (
+      other.inherited_substitutions.at (i).clone ());
+
+  return *this;
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_typealias (/*const*/
+					     HIR::TraitItemType &type) const
+{
+  TyTy::TyVar var (get_mappings ().get_hirid ());
+  return var.get_tyty ();
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_constant (
+  /*const*/ HIR::TraitItemConst &constant) const
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ().get ());
+  if (constant.has_expr ())
+    {
+      TyTy::BaseType *expr
+	= TypeCheckExpr::Resolve (constant.get_expr ().get ());
+
+      return type->unify (expr);
+    }
+  return type;
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions
+    = inherited_substitutions;
+
+  HIR::TraitFunctionDecl &function = fn.get_decl ();
+  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 (fn.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (fn.get_locus (), "failed to resolve return type");
+	  return get_error ();
+	}
+
+      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 mappings = Analysis::Mappings::get ();
+      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 ();
+      HIR::IdentifierPattern *self_pattern
+	= new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (),
+				      self_param.is_ref (),
+				      self_param.is_mut () ? Mutability::Mut
+							   : Mutability::Imm,
+				      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 nullptr;
+	    }
+	}
+
+      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);
+    }
+
+  auto mappings = Analysis::Mappings::get ();
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (fn.get_mappings ().get_nodeid (),
+					     &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, fn.get_locus ()};
+  auto resolved
+    = new TyTy::FnType (fn.get_mappings ().get_hirid (),
+			fn.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, substitutions);
+
+  context->insert_type (fn.get_mappings (), resolved);
+  return resolved;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check.h b/gcc/rust/typecheck/rust-hir-type-check.h
new file mode 100644
index 00000000000..21694dd302b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check.h
@@ -0,0 +1,379 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_TYPE_CHECK
+#define RUST_HIR_TYPE_CHECK
+
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+#include "rust-tyty.h"
+#include "rust-hir-trait-ref.h"
+#include "rust-autoderef.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckContextItem
+{
+public:
+  enum ItemType
+  {
+    ITEM,
+    IMPL_ITEM,
+    TRAIT_ITEM,
+  };
+
+  TypeCheckContextItem (HIR::Function *item)
+    : type (ItemType::ITEM), item (item)
+  {}
+
+  TypeCheckContextItem (HIR::ImplBlock *impl_block, HIR::Function *item)
+    : type (ItemType::IMPL_ITEM), item (impl_block, item)
+  {}
+
+  TypeCheckContextItem (HIR::TraitItemFunc *trait_item)
+    : type (ItemType::TRAIT_ITEM), item (trait_item)
+  {}
+
+  ItemType get_type () const { return type; }
+
+  HIR::Function *get_item ()
+  {
+    rust_assert (get_type () == ItemType::ITEM);
+    return item.item;
+  }
+
+  std::pair<HIR::ImplBlock *, HIR::Function *> &get_impl_item ()
+  {
+    rust_assert (get_type () == ItemType::IMPL_ITEM);
+    return item.impl_item;
+  };
+
+  HIR::TraitItemFunc *get_trait_item ()
+  {
+    rust_assert (get_type () == ItemType::TRAIT_ITEM);
+    return item.trait_item;
+  }
+
+private:
+  union Item
+  {
+    HIR::Function *item;
+    std::pair<HIR::ImplBlock *, HIR::Function *> impl_item;
+    HIR::TraitItemFunc *trait_item;
+
+    Item (HIR::Function *item) : item (item) {}
+
+    Item (HIR::ImplBlock *impl_block, HIR::Function *item)
+      : impl_item ({impl_block, item})
+    {}
+
+    Item (HIR::TraitItemFunc *trait_item) : trait_item (trait_item) {}
+  };
+
+  ItemType type;
+  Item item;
+};
+
+class TypeCheckContext
+{
+public:
+  static TypeCheckContext *get ();
+
+  ~TypeCheckContext ();
+
+  bool lookup_builtin (NodeId id, TyTy::BaseType **type);
+  bool lookup_builtin (std::string name, TyTy::BaseType **type);
+  void insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type);
+
+  void insert_type (const Analysis::NodeMapping &mappings,
+		    TyTy::BaseType *type);
+  void insert_implicit_type (TyTy::BaseType *type);
+  bool lookup_type (HirId id, TyTy::BaseType **type) const;
+
+  void insert_implicit_type (HirId id, TyTy::BaseType *type);
+
+  void insert_type_by_node_id (NodeId ref, HirId id);
+  bool lookup_type_by_node_id (NodeId ref, HirId *id);
+
+  TyTy::BaseType *peek_return_type ();
+  TypeCheckContextItem &peek_context ();
+  void push_return_type (TypeCheckContextItem item,
+			 TyTy::BaseType *return_type);
+  void pop_return_type ();
+
+  void iterate (std::function<bool (HirId, TyTy::BaseType *)> cb)
+  {
+    for (auto it = resolved.begin (); it != resolved.end (); it++)
+      {
+	if (!cb (it->first, it->second))
+	  return;
+      }
+  }
+
+  bool have_loop_context () const { return !loop_type_stack.empty (); }
+
+  void push_new_loop_context (HirId id, Location locus)
+  {
+    TyTy::BaseType *infer_var
+      = new TyTy::InferType (id, TyTy::InferType::InferTypeKind::GENERAL,
+			     locus);
+    loop_type_stack.push_back (infer_var);
+  }
+
+  void push_new_while_loop_context (HirId id)
+  {
+    TyTy::BaseType *infer_var = new TyTy::ErrorType (id);
+    loop_type_stack.push_back (infer_var);
+  }
+
+  TyTy::BaseType *peek_loop_context () { return loop_type_stack.back (); }
+
+  TyTy::BaseType *pop_loop_context ()
+  {
+    auto back = peek_loop_context ();
+    loop_type_stack.pop_back ();
+    return back;
+  }
+
+  void swap_head_loop_context (TyTy::BaseType *val)
+  {
+    loop_type_stack.pop_back ();
+    loop_type_stack.push_back (val);
+  }
+
+  void insert_trait_reference (DefId id, TraitReference &&ref)
+  {
+    rust_assert (trait_context.find (id) == trait_context.end ());
+    trait_context.emplace (id, std::move (ref));
+  }
+
+  bool lookup_trait_reference (DefId id, TraitReference **ref)
+  {
+    auto it = trait_context.find (id);
+    if (it == trait_context.end ())
+      return false;
+
+    *ref = &it->second;
+    return true;
+  }
+
+  void insert_receiver (HirId id, TyTy::BaseType *t)
+  {
+    receiver_context[id] = t;
+  }
+
+  bool lookup_receiver (HirId id, TyTy::BaseType **ref)
+  {
+    auto it = receiver_context.find (id);
+    if (it == receiver_context.end ())
+      return false;
+
+    *ref = it->second;
+    return true;
+  }
+
+  void insert_associated_trait_impl (HirId id, AssociatedImplTrait &&associated)
+  {
+    rust_assert (associated_impl_traits.find (id)
+		 == associated_impl_traits.end ());
+    associated_impl_traits.emplace (id, std::move (associated));
+  }
+
+  bool lookup_associated_trait_impl (HirId id, AssociatedImplTrait **associated)
+  {
+    auto it = associated_impl_traits.find (id);
+    if (it == associated_impl_traits.end ())
+      return false;
+
+    *associated = &it->second;
+    return true;
+  }
+
+  void insert_associated_type_mapping (HirId id, HirId mapping)
+  {
+    associated_type_mappings[id] = mapping;
+  }
+
+  void clear_associated_type_mapping (HirId id)
+  {
+    auto it = associated_type_mappings.find (id);
+    if (it != associated_type_mappings.end ())
+      associated_type_mappings.erase (it);
+  }
+
+  // lookup any associated type mappings, the out parameter of mapping is
+  // allowed to be nullptr which allows this interface to do a simple does exist
+  // check
+  bool lookup_associated_type_mapping (HirId id, HirId *mapping)
+  {
+    auto it = associated_type_mappings.find (id);
+    if (it == associated_type_mappings.end ())
+      return false;
+
+    if (mapping != nullptr)
+      *mapping = it->second;
+
+    return true;
+  }
+
+  void insert_associated_impl_mapping (HirId trait_id,
+				       const TyTy::BaseType *impl_type,
+				       HirId impl_id)
+  {
+    auto it = associated_traits_to_impls.find (trait_id);
+    if (it == associated_traits_to_impls.end ())
+      {
+	associated_traits_to_impls[trait_id] = {};
+      }
+
+    associated_traits_to_impls[trait_id].push_back ({impl_type, impl_id});
+  }
+
+  bool lookup_associated_impl_mapping_for_self (HirId trait_id,
+						const TyTy::BaseType *self,
+						HirId *mapping)
+  {
+    auto it = associated_traits_to_impls.find (trait_id);
+    if (it == associated_traits_to_impls.end ())
+      return false;
+
+    for (auto &item : it->second)
+      {
+	if (item.first->can_eq (self, false))
+	  {
+	    *mapping = item.second;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  void insert_autoderef_mappings (HirId id,
+				  std::vector<Adjustment> &&adjustments)
+  {
+    rust_assert (autoderef_mappings.find (id) == autoderef_mappings.end ());
+    autoderef_mappings.emplace (id, std::move (adjustments));
+  }
+
+  bool lookup_autoderef_mappings (HirId id,
+				  std::vector<Adjustment> **adjustments)
+  {
+    auto it = autoderef_mappings.find (id);
+    if (it == autoderef_mappings.end ())
+      return false;
+
+    *adjustments = &it->second;
+    return true;
+  }
+
+  void insert_cast_autoderef_mappings (HirId id,
+				       std::vector<Adjustment> &&adjustments)
+  {
+    rust_assert (cast_autoderef_mappings.find (id)
+		 == cast_autoderef_mappings.end ());
+    cast_autoderef_mappings.emplace (id, std::move (adjustments));
+  }
+
+  bool lookup_cast_autoderef_mappings (HirId id,
+				       std::vector<Adjustment> **adjustments)
+  {
+    auto it = cast_autoderef_mappings.find (id);
+    if (it == cast_autoderef_mappings.end ())
+      return false;
+
+    *adjustments = &it->second;
+    return true;
+  }
+
+  void insert_variant_definition (HirId id, HirId variant)
+  {
+    auto it = variants.find (id);
+    rust_assert (it == variants.end ());
+
+    variants[id] = variant;
+  }
+
+  bool lookup_variant_definition (HirId id, HirId *variant)
+  {
+    auto it = variants.find (id);
+    if (it == variants.end ())
+      return false;
+
+    *variant = it->second;
+    return true;
+  }
+
+  void insert_operator_overload (HirId id, TyTy::FnType *call_site)
+  {
+    auto it = operator_overloads.find (id);
+    rust_assert (it == operator_overloads.end ());
+
+    operator_overloads[id] = call_site;
+  }
+
+  bool lookup_operator_overload (HirId id, TyTy::FnType **call)
+  {
+    auto it = operator_overloads.find (id);
+    if (it == operator_overloads.end ())
+      return false;
+
+    *call = it->second;
+    return true;
+  }
+
+private:
+  TypeCheckContext ();
+
+  std::map<NodeId, HirId> node_id_refs;
+  std::map<HirId, TyTy::BaseType *> resolved;
+  std::vector<std::unique_ptr<TyTy::BaseType>> builtins;
+  std::vector<std::pair<TypeCheckContextItem, TyTy::BaseType *>>
+    return_type_stack;
+  std::vector<TyTy::BaseType *> loop_type_stack;
+  std::map<DefId, TraitReference> trait_context;
+  std::map<HirId, TyTy::BaseType *> receiver_context;
+  std::map<HirId, AssociatedImplTrait> associated_impl_traits;
+
+  // trait-id -> list of < self-tyty:impl-id>
+  std::map<HirId, std::vector<std::pair<const TyTy::BaseType *, HirId>>>
+    associated_traits_to_impls;
+
+  std::map<HirId, HirId> associated_type_mappings;
+
+  // adjustment mappings
+  std::map<HirId, std::vector<Adjustment>> autoderef_mappings;
+  std::map<HirId, std::vector<Adjustment>> cast_autoderef_mappings;
+
+  // operator overloads
+  std::map<HirId, TyTy::FnType *> operator_overloads;
+
+  // variants
+  std::map<HirId, HirId> variants;
+};
+
+class TypeResolution
+{
+public:
+  static void Resolve (HIR::Crate &crate);
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.cc b/gcc/rust/typecheck/rust-substitution-mapper.cc
new file mode 100644
index 00000000000..f80368a0339
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.cc
@@ -0,0 +1,77 @@
+// 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-substitution-mapper.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::BaseType *
+SubstMapperInternal::Resolve (TyTy::BaseType *base,
+			      TyTy::SubstitutionArgumentMappings &mappings)
+{
+  auto context = TypeCheckContext::get ();
+
+  SubstMapperInternal mapper (base->get_ref (), mappings);
+  base->accept_vis (mapper);
+  rust_assert (mapper.resolved != nullptr);
+
+  // insert these new implict types into the context
+  TyTy::BaseType *unused = nullptr;
+  bool is_ty_available
+    = context->lookup_type (mapper.resolved->get_ty_ref (), &unused);
+  if (!is_ty_available)
+    {
+      context->insert_type (
+	Analysis::NodeMapping (0, 0, mapper.resolved->get_ty_ref (), 0),
+	mapper.resolved);
+    }
+  bool is_ref_available
+    = context->lookup_type (mapper.resolved->get_ref (), &unused);
+  if (!is_ref_available)
+    {
+      context->insert_type (Analysis::NodeMapping (0, 0,
+						   mapper.resolved->get_ref (),
+						   0),
+			    mapper.resolved);
+    }
+
+  return mapper.resolved;
+}
+
+bool
+SubstMapperInternal::mappings_are_bound (
+  TyTy::BaseType *tyseg, TyTy::SubstitutionArgumentMappings &mappings)
+{
+  if (tyseg->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyseg);
+      return adt->are_mappings_bound (mappings);
+    }
+  else if (tyseg->get_kind () == TyTy::TypeKind::FNDEF)
+    {
+      TyTy::FnType *fn = static_cast<TyTy::FnType *> (tyseg);
+      return fn->are_mappings_bound (mappings);
+    }
+
+  return false;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.h b/gcc/rust/typecheck/rust-substitution-mapper.h
new file mode 100644
index 00000000000..028e10c0efe
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.h
@@ -0,0 +1,394 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_SUBSTITUTION_MAPPER_H
+#define RUST_SUBSTITUTION_MAPPER_H
+
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+
+namespace Rust {
+namespace Resolver {
+
+class SubstMapper : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *base, Location locus,
+				  HIR::GenericArgs *generics = nullptr)
+  {
+    SubstMapper mapper (base->get_ref (), generics, locus);
+    base->accept_vis (mapper);
+    rust_assert (mapper.resolved != nullptr);
+    return mapper.resolved;
+  }
+
+  static TyTy::BaseType *InferSubst (TyTy::BaseType *base, Location locus)
+  {
+    return SubstMapper::Resolve (base, locus, nullptr);
+  }
+
+  bool have_generic_args () const { return generics != nullptr; }
+
+  void visit (TyTy::FnType &type) override
+  {
+    TyTy::FnType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::FNDEF);
+	concrete = static_cast<TyTy::FnType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    TyTy::ADTType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::ADT);
+	concrete = static_cast<TyTy::ADTType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::PlaceholderType &type) override
+  {
+    rust_assert (type.can_resolve ());
+    resolved = SubstMapper::Resolve (type.resolve (), locus, generics);
+  }
+
+  void visit (TyTy::ProjectionType &type) override
+  {
+    TyTy::ProjectionType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::ADT);
+	concrete = static_cast<TyTy::ProjectionType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  // nothing to do for these
+  void visit (TyTy::InferType &) override { gcc_unreachable (); }
+  void visit (TyTy::TupleType &) override { gcc_unreachable (); }
+  void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
+  void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
+  void visit (TyTy::SliceType &) override { gcc_unreachable (); }
+  void visit (TyTy::BoolType &) override { gcc_unreachable (); }
+  void visit (TyTy::IntType &) override { gcc_unreachable (); }
+  void visit (TyTy::UintType &) override { gcc_unreachable (); }
+  void visit (TyTy::FloatType &) override { gcc_unreachable (); }
+  void visit (TyTy::USizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
+  void visit (TyTy::CharType &) override { gcc_unreachable (); }
+  void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
+  void visit (TyTy::PointerType &) override { gcc_unreachable (); }
+  void visit (TyTy::ParamType &) override { gcc_unreachable (); }
+  void visit (TyTy::StrType &) override { gcc_unreachable (); }
+  void visit (TyTy::NeverType &) override { gcc_unreachable (); }
+  void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
+  void visit (TyTy::ClosureType &) override { gcc_unreachable (); }
+
+private:
+  SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus)
+    : resolved (new TyTy::ErrorType (ref)), generics (generics), locus (locus)
+  {}
+
+  TyTy::BaseType *resolved;
+  HIR::GenericArgs *generics;
+  Location locus;
+};
+
+class SubstMapperInternal : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *base,
+				  TyTy::SubstitutionArgumentMappings &mappings);
+
+  static bool mappings_are_bound (TyTy::BaseType *ty,
+				  TyTy::SubstitutionArgumentMappings &mappings);
+
+  void visit (TyTy::FnType &type) override
+  {
+    TyTy::SubstitutionArgumentMappings adjusted
+      = type.adjust_mappings_for_this (mappings);
+    if (adjusted.is_error ())
+      return;
+
+    TyTy::BaseType *concrete = type.handle_substitions (adjusted);
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    TyTy::SubstitutionArgumentMappings adjusted
+      = type.adjust_mappings_for_this (mappings);
+    if (adjusted.is_error ())
+      return;
+
+    TyTy::BaseType *concrete = type.handle_substitions (adjusted);
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  // these don't support generic arguments but might contain a type param
+  void visit (TyTy::TupleType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ReferenceType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::PointerType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ParamType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::PlaceholderType &type) override
+  {
+    rust_assert (type.can_resolve ());
+    if (mappings.trait_item_mode ())
+      {
+	resolved = type.resolve ();
+      }
+    else
+      {
+	resolved = SubstMapperInternal::Resolve (type.resolve (), mappings);
+      }
+  }
+
+  void visit (TyTy::ProjectionType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ClosureType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ArrayType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::SliceType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  // nothing to do for these
+  void visit (TyTy::InferType &type) override { resolved = type.clone (); }
+  void visit (TyTy::FnPtr &type) override { resolved = type.clone (); }
+  void visit (TyTy::BoolType &type) override { resolved = type.clone (); }
+  void visit (TyTy::IntType &type) override { resolved = type.clone (); }
+  void visit (TyTy::UintType &type) override { resolved = type.clone (); }
+  void visit (TyTy::FloatType &type) override { resolved = type.clone (); }
+  void visit (TyTy::USizeType &type) override { resolved = type.clone (); }
+  void visit (TyTy::ISizeType &type) override { resolved = type.clone (); }
+  void visit (TyTy::ErrorType &type) override { resolved = type.clone (); }
+  void visit (TyTy::CharType &type) override { resolved = type.clone (); }
+  void visit (TyTy::StrType &type) override { resolved = type.clone (); }
+  void visit (TyTy::NeverType &type) override { resolved = type.clone (); }
+  void visit (TyTy::DynamicObjectType &type) override
+  {
+    resolved = type.clone ();
+  }
+
+private:
+  SubstMapperInternal (HirId ref, TyTy::SubstitutionArgumentMappings &mappings)
+    : resolved (new TyTy::ErrorType (ref)), mappings (mappings)
+  {}
+
+  TyTy::BaseType *resolved;
+  TyTy::SubstitutionArgumentMappings &mappings;
+};
+
+class SubstMapperFromExisting : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *concrete,
+				  TyTy::BaseType *receiver)
+  {
+    rust_assert (concrete->get_kind () == receiver->get_kind ());
+
+    SubstMapperFromExisting mapper (concrete, receiver);
+    concrete->accept_vis (mapper);
+    return mapper.resolved;
+  }
+
+  void visit (TyTy::FnType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::FnType *to_sub = static_cast<TyTy::FnType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::ADTType *to_sub = static_cast<TyTy::ADTType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::ClosureType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::ClosureType *to_sub = static_cast<TyTy::ClosureType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::InferType &) override { gcc_unreachable (); }
+  void visit (TyTy::TupleType &) override { gcc_unreachable (); }
+  void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
+  void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
+  void visit (TyTy::SliceType &) override { gcc_unreachable (); }
+  void visit (TyTy::BoolType &) override { gcc_unreachable (); }
+  void visit (TyTy::IntType &) override { gcc_unreachable (); }
+  void visit (TyTy::UintType &) override { gcc_unreachable (); }
+  void visit (TyTy::FloatType &) override { gcc_unreachable (); }
+  void visit (TyTy::USizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
+  void visit (TyTy::CharType &) override { gcc_unreachable (); }
+  void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
+  void visit (TyTy::PointerType &) override { gcc_unreachable (); }
+  void visit (TyTy::ParamType &) override { gcc_unreachable (); }
+  void visit (TyTy::StrType &) override { gcc_unreachable (); }
+  void visit (TyTy::NeverType &) override { gcc_unreachable (); }
+  void visit (TyTy::PlaceholderType &) override { gcc_unreachable (); }
+  void visit (TyTy::ProjectionType &) override { gcc_unreachable (); }
+  void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
+
+private:
+  SubstMapperFromExisting (TyTy::BaseType *concrete, TyTy::BaseType *receiver)
+    : concrete (concrete), receiver (receiver), resolved (nullptr)
+  {}
+
+  TyTy::BaseType *concrete;
+  TyTy::BaseType *receiver;
+
+  TyTy::BaseType *resolved;
+};
+
+class GetUsedSubstArgs : public TyTy::TyConstVisitor
+{
+public:
+  static TyTy::SubstitutionArgumentMappings From (const TyTy::BaseType *from)
+  {
+    GetUsedSubstArgs mapper;
+    from->accept_vis (mapper);
+    return mapper.args;
+  }
+
+  void visit (const TyTy::FnType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::ADTType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::ClosureType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::InferType &) override {}
+  void visit (const TyTy::TupleType &) override {}
+  void visit (const TyTy::FnPtr &) override {}
+  void visit (const TyTy::ArrayType &) override {}
+  void visit (const TyTy::SliceType &) override {}
+  void visit (const TyTy::BoolType &) override {}
+  void visit (const TyTy::IntType &) override {}
+  void visit (const TyTy::UintType &) override {}
+  void visit (const TyTy::FloatType &) override {}
+  void visit (const TyTy::USizeType &) override {}
+  void visit (const TyTy::ISizeType &) override {}
+  void visit (const TyTy::ErrorType &) override {}
+  void visit (const TyTy::CharType &) override {}
+  void visit (const TyTy::ReferenceType &) override {}
+  void visit (const TyTy::PointerType &) override {}
+  void visit (const TyTy::ParamType &) override {}
+  void visit (const TyTy::StrType &) override {}
+  void visit (const TyTy::NeverType &) override {}
+  void visit (const TyTy::PlaceholderType &) override {}
+  void visit (const TyTy::ProjectionType &) override {}
+  void visit (const TyTy::DynamicObjectType &) override {}
+
+private:
+  GetUsedSubstArgs () : args (TyTy::SubstitutionArgumentMappings::error ()) {}
+
+  TyTy::SubstitutionArgumentMappings args;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_SUBSTITUTION_MAPPER_H
diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h
new file mode 100644
index 00000000000..ccf0f625e4b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tycheck-dump.h
@@ -0,0 +1,239 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYCHECK_DUMP
+#define RUST_TYCHECK_DUMP
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeResolverDump : private TypeCheckBase, private HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  static void go (HIR::Crate &crate, std::ofstream &out)
+  {
+    TypeResolverDump dumper;
+    for (auto &item : crate.items)
+      {
+	item->accept_vis (dumper);
+	dumper.dump += "\n";
+      }
+
+    out << dumper.dump;
+  }
+
+  void visit (HIR::StructStruct &struct_decl) override
+  {
+    dump += indent () + "struct " + type_string (struct_decl.get_mappings ())
+	    + "\n";
+  }
+
+  void visit (HIR::Union &union_decl) override
+  {
+    dump
+      += indent () + "union " + type_string (union_decl.get_mappings ()) + "\n";
+  }
+
+  void visit (HIR::TupleStruct &struct_decl) override
+  {
+    dump += indent () + "struct" + type_string (struct_decl.get_mappings ())
+	    + "\n";
+  }
+
+  void visit (HIR::ImplBlock &impl_block) override
+  {
+    dump += indent () + "impl "
+	    + type_string (impl_block.get_type ()->get_mappings ()) + " {\n";
+    indentation_level++;
+
+    for (auto &impl_item : impl_block.get_impl_items ())
+      {
+	impl_item->accept_vis (*this);
+	dump += "\n";
+      }
+
+    indentation_level--;
+    dump += indent () + "}\n";
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    dump += indent () + "constant " + constant.get_identifier () + ":"
+	    + type_string (constant.get_mappings ()) + " = ";
+    constant.get_expr ()->accept_vis (*this);
+    dump += ";\n";
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    dump += indent () + "fn " + function.get_function_name () + " "
+	    + type_string (function.get_mappings ()) + "\n";
+    dump += indent () + "{\n";
+
+    HIR::BlockExpr *function_body = function.get_definition ().get ();
+    function_body->accept_vis (*this);
+
+    dump += indent () + "}\n";
+  }
+
+  void visit (HIR::BlockExpr &expr) override
+  {
+    dump += "{\n";
+    indentation_level++;
+
+    for (auto &s : expr.get_statements ())
+      {
+	dump += indent ();
+	s->accept_vis (*this);
+	dump += ";\n";
+      }
+
+    if (expr.has_expr ())
+      {
+	dump += indent ();
+	expr.expr->accept_vis (*this);
+	dump += ";\n";
+      }
+
+    indentation_level--;
+    dump += "}\n";
+  }
+
+  void visit (HIR::UnsafeBlockExpr &expr) override
+  {
+    dump += "unsafe ";
+    expr.get_block_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LetStmt &stmt) override
+  {
+    dump += "let " + stmt.get_pattern ()->as_string () + ":"
+	    + type_string (stmt.get_pattern ()->get_pattern_mappings ());
+    if (stmt.has_init_expr ())
+      {
+	dump += " = ";
+	stmt.get_init_expr ()->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::AssignmentExpr &expr) override
+  {
+    expr.get_lhs ()->accept_vis (*this);
+    dump += " = ";
+    expr.get_rhs ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LiteralExpr &expr) override
+  {
+    dump += expr.get_literal ().as_string () + ":"
+	    + type_string (expr.get_mappings ());
+  }
+
+  void visit (HIR::ArrayExpr &expr) override
+  {
+    dump += type_string (expr.get_mappings ()) + ":[";
+
+    HIR::ArrayElems *elements = expr.get_internal_elements ();
+    elements->accept_vis (*this);
+
+    dump += "]";
+  }
+
+  void visit (HIR::ArrayElemsValues &elems) override
+  {
+    for (auto &elem : elems.get_values ())
+      {
+	elem->accept_vis (*this);
+	dump += ",";
+      }
+  }
+
+  void visit (HIR::GroupedExpr &expr) override
+  {
+    HIR::Expr *paren_expr = expr.get_expr_in_parens ().get ();
+    dump += "(";
+    paren_expr->accept_vis (*this);
+    dump += ")";
+  }
+
+  void visit (HIR::PathInExpression &expr) override
+  {
+    dump += type_string (expr.get_mappings ());
+  }
+
+  void visit (HIR::StructExprStructFields &expr) override
+  {
+    dump += "ctor: " + type_string (expr.get_mappings ());
+  }
+
+protected:
+  std::string type_string (const Analysis::NodeMapping &mappings)
+  {
+    TyTy::BaseType *lookup = nullptr;
+    if (!context->lookup_type (mappings.get_hirid (), &lookup))
+      return "<error>";
+
+    std::string buf = "[";
+    for (auto &ref : lookup->get_combined_refs ())
+      {
+	buf += std::to_string (ref);
+	buf += ", ";
+      }
+    buf += "]";
+
+    std::string repr = lookup->as_string ();
+    return "<" + repr + " HIRID: " + std::to_string (mappings.get_hirid ())
+	   + " RF:" + std::to_string (lookup->get_ref ()) + " TF:"
+	   + std::to_string (lookup->get_ty_ref ()) + +" - " + buf + ">";
+  }
+
+  std::string indent ()
+  {
+    std::string buf;
+    for (size_t i = 0; i < indentation_level; ++i)
+      buf += "    ";
+
+    return buf;
+  }
+
+private:
+  TypeResolverDump () : TypeCheckBase (), indentation_level (0) {}
+
+  std::string dump;
+  size_t indentation_level;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_TYCHECK_DUMP
diff --git a/gcc/rust/typecheck/rust-tyctx.cc b/gcc/rust/typecheck/rust-tyctx.cc
new file mode 100644
index 00000000000..d8a49e8b9ea
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyctx.cc
@@ -0,0 +1,155 @@
+// 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.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckContext *
+TypeCheckContext::get ()
+{
+  static TypeCheckContext *instance;
+  if (instance == nullptr)
+    instance = new TypeCheckContext ();
+
+  return instance;
+}
+
+TypeCheckContext::TypeCheckContext () {}
+
+TypeCheckContext::~TypeCheckContext () {}
+
+bool
+TypeCheckContext::lookup_builtin (NodeId id, TyTy::BaseType **type)
+{
+  auto ref_it = node_id_refs.find (id);
+  if (ref_it == node_id_refs.end ())
+    return false;
+
+  auto it = resolved.find (ref_it->second);
+  if (it == resolved.end ())
+    return false;
+
+  *type = it->second;
+  return true;
+}
+
+bool
+TypeCheckContext::lookup_builtin (std::string name, TyTy::BaseType **type)
+{
+  for (auto &builtin : builtins)
+    {
+      if (name.compare (builtin->as_string ()) == 0)
+	{
+	  *type = builtin.get ();
+	  return true;
+	}
+    }
+  return false;
+}
+
+void
+TypeCheckContext::insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type)
+{
+  node_id_refs[ref] = id;
+  resolved[id] = type;
+  builtins.push_back (std::unique_ptr<TyTy::BaseType> (type));
+}
+
+void
+TypeCheckContext::insert_type (const Analysis::NodeMapping &mappings,
+			       TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  NodeId ref = mappings.get_nodeid ();
+  HirId id = mappings.get_hirid ();
+  node_id_refs[ref] = id;
+  resolved[id] = type;
+}
+
+void
+TypeCheckContext::insert_implicit_type (TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  resolved[type->get_ref ()] = type;
+}
+
+void
+TypeCheckContext::insert_implicit_type (HirId id, TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  resolved[id] = type;
+}
+
+bool
+TypeCheckContext::lookup_type (HirId id, TyTy::BaseType **type) const
+{
+  auto it = resolved.find (id);
+  if (it == resolved.end ())
+    return false;
+
+  *type = it->second;
+  return true;
+}
+
+void
+TypeCheckContext::insert_type_by_node_id (NodeId ref, HirId id)
+{
+  rust_assert (node_id_refs.find (ref) == node_id_refs.end ());
+  node_id_refs[ref] = id;
+}
+
+bool
+TypeCheckContext::lookup_type_by_node_id (NodeId ref, HirId *id)
+{
+  auto it = node_id_refs.find (ref);
+  if (it == node_id_refs.end ())
+    return false;
+
+  *id = it->second;
+  return true;
+}
+
+TyTy::BaseType *
+TypeCheckContext::peek_return_type ()
+{
+  return return_type_stack.back ().second;
+}
+
+void
+TypeCheckContext::push_return_type (TypeCheckContextItem item,
+				    TyTy::BaseType *return_type)
+{
+  return_type_stack.push_back ({std::move (item), return_type});
+}
+
+void
+TypeCheckContext::pop_return_type ()
+{
+  return_type_stack.pop_back ();
+}
+
+TypeCheckContextItem &
+TypeCheckContext::peek_context ()
+{
+  return return_type_stack.back ().first;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
new file mode 100644
index 00000000000..7a1562ab544
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -0,0 +1,462 @@
+// Copyright (C) 2021-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-bounds.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeBoundsProbe::scan ()
+{
+  std::vector<std::pair<HIR::TypePath *, HIR::ImplBlock *>>
+    possible_trait_paths;
+  mappings->iterate_impl_blocks (
+    [&] (HirId id, HIR::ImplBlock *impl) mutable -> bool {
+      // we are filtering for trait-impl-blocks
+      if (!impl->has_trait_ref ())
+	return true;
+
+      TyTy::BaseType *impl_type = nullptr;
+      bool ok
+	= context->lookup_type (impl->get_type ()->get_mappings ().get_hirid (),
+				&impl_type);
+      if (!ok)
+	return true;
+
+      if (!receiver->can_eq (impl_type, false))
+	{
+	  if (!impl_type->can_eq (receiver, false))
+	    return true;
+	}
+
+      possible_trait_paths.push_back ({impl->get_trait_ref ().get (), impl});
+      return true;
+    });
+
+  for (auto &path : possible_trait_paths)
+    {
+      HIR::TypePath *trait_path = path.first;
+      TraitReference *trait_ref = TraitResolver::Resolve (*trait_path);
+
+      if (!trait_ref->is_error ())
+	trait_references.push_back ({trait_ref, path.second});
+    }
+}
+
+TraitReference *
+TypeCheckBase::resolve_trait_path (HIR::TypePath &path)
+{
+  return TraitResolver::Resolve (path);
+}
+
+TyTy::TypeBoundPredicate
+TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path)
+{
+  TraitReference *trait = resolve_trait_path (type_path);
+  if (trait->is_error ())
+    return TyTy::TypeBoundPredicate::error ();
+
+  TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ());
+  HIR::GenericArgs args
+    = HIR::GenericArgs::create_empty (type_path.get_locus ());
+
+  auto &final_seg = type_path.get_final_segment ();
+  if (final_seg->is_generic_segment ())
+    {
+      auto final_generic_seg
+	= static_cast<HIR::TypePathSegmentGeneric *> (final_seg.get ());
+      if (final_generic_seg->has_generic_args ())
+	{
+	  args = final_generic_seg->get_generic_args ();
+	}
+    }
+
+  if (predicate.requires_generic_args ())
+    {
+      // this is applying generic arguments to a trait reference
+      predicate.apply_generic_arguments (&args);
+    }
+
+  return predicate;
+}
+
+} // namespace Resolver
+
+namespace TyTy {
+
+TypeBoundPredicate::TypeBoundPredicate (
+  const Resolver::TraitReference &trait_reference, Location locus)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (trait_reference.get_mappings ().get_defid ()), locus (locus),
+    error_flag (false)
+{
+  substitutions.clear ();
+  for (const auto &p : trait_reference.get_trait_substs ())
+    substitutions.push_back (p.clone ());
+
+  // we setup a dummy implict self argument
+  SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
+  used_arguments.get_mappings ().push_back (placeholder_self);
+}
+
+TypeBoundPredicate::TypeBoundPredicate (
+  DefId reference, std::vector<SubstitutionParamMapping> subst, Location locus)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (reference), locus (locus), error_flag (false)
+{
+  substitutions.clear ();
+  for (const auto &p : subst)
+    substitutions.push_back (p.clone ());
+
+  // we setup a dummy implict self argument
+  SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
+  used_arguments.get_mappings ().push_back (placeholder_self);
+}
+
+TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (other.reference), locus (other.locus),
+    error_flag (other.error_flag)
+{
+  substitutions.clear ();
+  for (const auto &p : other.get_substs ())
+    substitutions.push_back (p.clone ());
+
+  std::vector<SubstitutionArg> mappings;
+  for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
+    {
+      const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
+      SubstitutionArg arg (oa);
+      mappings.push_back (std::move (arg));
+    }
+
+  // we need to remap the argument mappings based on this copied constructor
+  std::vector<SubstitutionArg> copied_arg_mappings;
+  size_t i = 0;
+  for (const auto &m : other.used_arguments.get_mappings ())
+    {
+      TyTy::BaseType *argument
+	= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
+      SubstitutionArg c (&substitutions.at (i++), argument);
+      copied_arg_mappings.push_back (std::move (c));
+    }
+
+  used_arguments
+    = SubstitutionArgumentMappings (copied_arg_mappings,
+				    other.used_arguments.get_locus ());
+}
+
+TypeBoundPredicate &
+TypeBoundPredicate::operator= (const TypeBoundPredicate &other)
+{
+  reference = other.reference;
+  locus = other.locus;
+  error_flag = other.error_flag;
+  used_arguments = SubstitutionArgumentMappings::error ();
+
+  substitutions.clear ();
+  for (const auto &p : other.get_substs ())
+    substitutions.push_back (p.clone ());
+
+  std::vector<SubstitutionArg> mappings;
+  for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
+    {
+      const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
+      SubstitutionArg arg (oa);
+      mappings.push_back (std::move (arg));
+    }
+
+  // we need to remap the argument mappings based on this copied constructor
+  std::vector<SubstitutionArg> copied_arg_mappings;
+  size_t i = 0;
+  for (const auto &m : other.used_arguments.get_mappings ())
+    {
+      TyTy::BaseType *argument
+	= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
+      SubstitutionArg c (&substitutions.at (i++), argument);
+      copied_arg_mappings.push_back (std::move (c));
+    }
+
+  used_arguments
+    = SubstitutionArgumentMappings (copied_arg_mappings,
+				    other.used_arguments.get_locus ());
+
+  return *this;
+}
+
+TypeBoundPredicate
+TypeBoundPredicate::error ()
+{
+  auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ());
+  p.error_flag = true;
+  return p;
+}
+
+std::string
+TypeBoundPredicate::as_string () const
+{
+  return get ()->as_string () + subst_as_string ();
+}
+
+std::string
+TypeBoundPredicate::as_name () const
+{
+  return get ()->get_name () + subst_as_string ();
+}
+
+const Resolver::TraitReference *
+TypeBoundPredicate::get () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  Resolver::TraitReference *ref = nullptr;
+  bool ok = context->lookup_trait_reference (reference, &ref);
+  rust_assert (ok);
+
+  return ref;
+}
+
+std::string
+TypeBoundPredicate::get_name () const
+{
+  return get ()->get_name ();
+}
+
+bool
+TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const
+{
+  const Resolver::TraitReference *trait = get ();
+  rust_assert (trait != nullptr);
+  return trait->is_object_safe (emit_error, locus);
+}
+
+void
+TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args)
+{
+  // we need to get the substitutions argument mappings but also remember that
+  // we have an implicit Self argument which we must be careful to respect
+  rust_assert (!used_arguments.is_empty ());
+  rust_assert (!substitutions.empty ());
+
+  // now actually perform a substitution
+  used_arguments = get_mappings_from_generic_args (*generic_args);
+
+  error_flag |= used_arguments.is_error ();
+  auto &subst_mappings = used_arguments;
+  for (auto &sub : get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok && arg.get_tyty () != nullptr)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+}
+
+bool
+TypeBoundPredicate::contains_item (const std::string &search) const
+{
+  auto trait_ref = get ();
+  const Resolver::TraitItemReference *trait_item_ref = nullptr;
+  return trait_ref->lookup_trait_item (search, &trait_item_ref);
+}
+
+TypeBoundPredicateItem
+TypeBoundPredicate::lookup_associated_item (const std::string &search) const
+{
+  auto trait_ref = get ();
+  const Resolver::TraitItemReference *trait_item_ref = nullptr;
+  if (!trait_ref->lookup_trait_item (search, &trait_item_ref))
+    return TypeBoundPredicateItem::error ();
+
+  return TypeBoundPredicateItem (this, trait_item_ref);
+}
+
+TypeBoundPredicateItem
+TypeBoundPredicate::lookup_associated_item (
+  const Resolver::TraitItemReference *ref) const
+{
+  return lookup_associated_item (ref->get_identifier ());
+}
+
+BaseType *
+TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
+{
+  TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty ();
+  if (parent->get_substitution_arguments ().is_empty ())
+    return trait_item_tyty;
+
+  const Resolver::TraitItemReference *tref = get_raw_item ();
+  bool is_associated_type = tref->get_trait_item_type ();
+  if (is_associated_type)
+    return trait_item_tyty;
+
+  // set up the self mapping
+  SubstitutionArgumentMappings gargs = parent->get_substitution_arguments ();
+  rust_assert (!gargs.is_empty ());
+
+  // setup the adjusted mappings
+  std::vector<SubstitutionArg> adjusted_mappings;
+  for (size_t i = 0; i < gargs.get_mappings ().size (); i++)
+    {
+      auto &mapping = gargs.get_mappings ().at (i);
+
+      bool is_implicit_self = i == 0;
+      TyTy::BaseType *argument
+	= is_implicit_self ? receiver->clone () : mapping.get_tyty ();
+
+      SubstitutionArg arg (mapping.get_param_mapping (), argument);
+      adjusted_mappings.push_back (std::move (arg));
+    }
+
+  SubstitutionArgumentMappings adjusted (adjusted_mappings, gargs.get_locus (),
+					 gargs.get_subst_cb (),
+					 true /* trait-mode-flag */);
+  return Resolver::SubstMapperInternal::Resolve (trait_item_tyty, adjusted);
+}
+bool
+TypeBoundPredicate::is_error () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  Resolver::TraitReference *ref = nullptr;
+  bool ok = context->lookup_trait_reference (reference, &ref);
+
+  return !ok || error_flag;
+}
+
+BaseType *
+TypeBoundPredicate::handle_substitions (
+  SubstitutionArgumentMappings subst_mappings)
+{
+  for (auto &sub : get_substs ())
+    {
+      if (sub.get_param_ty () == nullptr)
+	continue;
+
+      ParamType *p = sub.get_param_ty ();
+      BaseType *r = p->resolve ();
+      BaseType *s = Resolver::SubstMapperInternal::Resolve (r, subst_mappings);
+
+      p->set_ty_ref (s->get_ty_ref ());
+    }
+
+  // FIXME more error handling at some point
+  // used_arguments = subst_mappings;
+  // error_flag |= used_arguments.is_error ();
+
+  return nullptr;
+}
+
+bool
+TypeBoundPredicate::requires_generic_args () const
+{
+  if (is_error ())
+    return false;
+
+  return substitutions.size () > 1;
+}
+
+// trait item reference
+
+const Resolver::TraitItemReference *
+TypeBoundPredicateItem::get_raw_item () const
+{
+  return trait_item_ref;
+}
+
+bool
+TypeBoundPredicateItem::needs_implementation () const
+{
+  return !get_raw_item ()->is_optional ();
+}
+
+Location
+TypeBoundPredicateItem::get_locus () const
+{
+  return get_raw_item ()->get_locus ();
+}
+
+// TypeBoundsMappings
+
+TypeBoundsMappings::TypeBoundsMappings (
+  std::vector<TypeBoundPredicate> specified_bounds)
+  : specified_bounds (specified_bounds)
+{}
+
+std::vector<TypeBoundPredicate> &
+TypeBoundsMappings::get_specified_bounds ()
+{
+  return specified_bounds;
+}
+
+const std::vector<TypeBoundPredicate> &
+TypeBoundsMappings::get_specified_bounds () const
+{
+  return specified_bounds;
+}
+
+size_t
+TypeBoundsMappings::num_specified_bounds () const
+{
+  return specified_bounds.size ();
+}
+
+std::string
+TypeBoundsMappings::raw_bounds_as_string () const
+{
+  std::string buf;
+  for (size_t i = 0; i < specified_bounds.size (); i++)
+    {
+      const TypeBoundPredicate &b = specified_bounds.at (i);
+      bool has_next = (i + 1) < specified_bounds.size ();
+      buf += b.as_string () + (has_next ? " + " : "");
+    }
+  return buf;
+}
+
+std::string
+TypeBoundsMappings::bounds_as_string () const
+{
+  return "bounds:[" + raw_bounds_as_string () + "]";
+}
+
+std::string
+TypeBoundsMappings::raw_bounds_as_name () const
+{
+  std::string buf;
+  for (size_t i = 0; i < specified_bounds.size (); i++)
+    {
+      const TypeBoundPredicate &b = specified_bounds.at (i);
+      bool has_next = (i + 1) < specified_bounds.size ();
+      buf += b.as_name () + (has_next ? " + " : "");
+    }
+
+  return buf;
+}
+
+void
+TypeBoundsMappings::add_bound (TypeBoundPredicate predicate)
+{
+  specified_bounds.push_back (predicate);
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-call.cc b/gcc/rust/typecheck/rust-tyty-call.cc
new file mode 100644
index 00000000000..1ce82c943f5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.cc
@@ -0,0 +1,263 @@
+// 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-tyty-call.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace TyTy {
+
+void
+TypeCheckCallExpr::visit (ADTType &type)
+{
+  rust_assert (!variant.is_error ());
+  if (variant.get_variant_type () != TyTy::VariantDef::VariantType::TUPLE)
+    {
+      rust_error_at (
+	call.get_locus (),
+	"expected function, tuple struct or tuple variant, found struct %<%s%>",
+	type.get_name ().c_str ());
+      return;
+    }
+
+  if (call.num_params () != variant.num_fields ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) variant.num_fields ());
+      return;
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      StructFieldType *field = variant.get_field_at_index (i);
+      BaseType *field_tyty = field->get_field_type ();
+
+      BaseType *arg = Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (arg->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "failed to resolve argument type");
+	  return;
+	}
+
+      auto res = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), field_tyty, arg,
+	argument->get_locus ());
+      if (res->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  return;
+	}
+
+      delete res;
+      i++;
+    }
+
+  if (i != call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  resolved = type.clone ();
+}
+
+void
+TypeCheckCallExpr::visit (FnType &type)
+{
+  type.monomorphize ();
+  if (call.num_params () != type.num_params ())
+    {
+      if (type.is_varadic ())
+	{
+	  if (call.num_params () < type.num_params ())
+	    {
+	      rust_error_at (call.get_locus (),
+			     "unexpected number of arguments %lu expected %lu",
+			     (unsigned long) call.num_params (),
+			     (unsigned long) type.num_params ());
+	      return;
+	    }
+	}
+      else
+	{
+	  rust_error_at (call.get_locus (),
+			 "unexpected number of arguments %lu expected %lu",
+			 (unsigned long) call.num_params (),
+			 (unsigned long) type.num_params ());
+	  return;
+	}
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      // it might be a varadic function
+      if (i < type.num_params ())
+	{
+	  auto fnparam = type.param_at (i);
+	  auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	    argument->get_mappings ().get_hirid (), fnparam.second,
+	    argument_expr_tyty, argument->get_locus ());
+	  if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	    {
+	      rust_error_at (argument->get_locus (),
+			     "Type Resolution failure on parameter");
+	      return;
+	    }
+	}
+
+      i++;
+    }
+
+  if (i < call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  type.monomorphize ();
+  resolved = type.get_return_type ()->clone ();
+}
+
+void
+TypeCheckCallExpr::visit (FnPtr &type)
+{
+  if (call.num_params () != type.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) type.num_params ());
+      return;
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto fnparam = type.param_at (i);
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), fnparam, argument_expr_tyty,
+	argument->get_locus ());
+      if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "Type Resolution failure on parameter");
+	  return;
+	}
+
+      i++;
+    }
+
+  if (i != call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  resolved = type.get_return_type ()->monomorphized_clone ();
+}
+
+// method call checker
+
+void
+TypeCheckMethodCallExpr::visit (FnType &type)
+{
+  type.get_self_type ()->unify (adjusted_self);
+
+  // +1 for the receiver self
+  size_t num_args_to_call = call.num_params () + 1;
+  if (num_args_to_call != type.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) type.num_params ());
+      return;
+    }
+
+  size_t i = 1;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto fnparam = type.param_at (i);
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), fnparam.second,
+	argument_expr_tyty, argument->get_locus ());
+      if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "Type Resolution failure on parameter");
+	  return;
+	}
+
+      i++;
+    }
+
+  if (i != num_args_to_call)
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  type.monomorphize ();
+
+  resolved = type.get_return_type ()->monomorphized_clone ();
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-call.h b/gcc/rust/typecheck/rust-tyty-call.h
new file mode 100644
index 00000000000..51817e6446d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYTY_CALL
+#define RUST_TYTY_CALL
+
+#include "rust-diagnostics.h"
+#include "rust-hir-full.h"
+#include "rust-tyty-visitor.h"
+#include "rust-tyty.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+class TypeCheckCallExpr : private TyVisitor
+{
+public:
+  static BaseType *go (BaseType *ref, HIR::CallExpr &call,
+		       TyTy::VariantDef &variant,
+		       Resolver::TypeCheckContext *context)
+  {
+    TypeCheckCallExpr checker (call, variant, context);
+    ref->accept_vis (checker);
+    return checker.resolved;
+  }
+
+  void visit (InferType &) override { gcc_unreachable (); }
+  void visit (TupleType &) override { gcc_unreachable (); }
+  void visit (ArrayType &) override { gcc_unreachable (); }
+  void visit (SliceType &) override { gcc_unreachable (); }
+  void visit (BoolType &) override { gcc_unreachable (); }
+  void visit (IntType &) override { gcc_unreachable (); }
+  void visit (UintType &) override { gcc_unreachable (); }
+  void visit (FloatType &) override { gcc_unreachable (); }
+  void visit (USizeType &) override { gcc_unreachable (); }
+  void visit (ISizeType &) override { gcc_unreachable (); }
+  void visit (ErrorType &) override { gcc_unreachable (); }
+  void visit (CharType &) override { gcc_unreachable (); }
+  void visit (ReferenceType &) override { gcc_unreachable (); }
+  void visit (PointerType &) override { gcc_unreachable (); }
+  void visit (ParamType &) override { gcc_unreachable (); }
+  void visit (StrType &) override { gcc_unreachable (); }
+  void visit (NeverType &) override { gcc_unreachable (); }
+  void visit (PlaceholderType &) override { gcc_unreachable (); }
+  void visit (ProjectionType &) override { gcc_unreachable (); }
+  void visit (DynamicObjectType &) override { gcc_unreachable (); }
+  void visit (ClosureType &type) override { gcc_unreachable (); }
+
+  // tuple-structs
+  void visit (ADTType &type) override;
+
+  // call fns
+  void visit (FnType &type) override;
+  void visit (FnPtr &type) override;
+
+private:
+  TypeCheckCallExpr (HIR::CallExpr &c, TyTy::VariantDef &variant,
+		     Resolver::TypeCheckContext *context)
+    : resolved (new TyTy::ErrorType (c.get_mappings ().get_hirid ())), call (c),
+      variant (variant), context (context),
+      mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType *resolved;
+  HIR::CallExpr &call;
+  TyTy::VariantDef &variant;
+  Resolver::TypeCheckContext *context;
+  Analysis::Mappings *mappings;
+};
+
+class TypeCheckMethodCallExpr : private TyVisitor
+{
+public:
+  // Resolve the Method parameters and return back the return type
+  static BaseType *go (BaseType *ref, HIR::MethodCallExpr &call,
+		       TyTy::BaseType *adjusted_self,
+		       Resolver::TypeCheckContext *context)
+  {
+    TypeCheckMethodCallExpr checker (call, adjusted_self, context);
+    ref->accept_vis (checker);
+    return checker.resolved;
+  }
+
+  void visit (InferType &) override { gcc_unreachable (); }
+  void visit (TupleType &) override { gcc_unreachable (); }
+  void visit (ArrayType &) override { gcc_unreachable (); }
+  void visit (SliceType &) override { gcc_unreachable (); }
+  void visit (BoolType &) override { gcc_unreachable (); }
+  void visit (IntType &) override { gcc_unreachable (); }
+  void visit (UintType &) override { gcc_unreachable (); }
+  void visit (FloatType &) override { gcc_unreachable (); }
+  void visit (USizeType &) override { gcc_unreachable (); }
+  void visit (ISizeType &) override { gcc_unreachable (); }
+  void visit (ErrorType &) override { gcc_unreachable (); }
+  void visit (ADTType &) override { gcc_unreachable (); };
+  void visit (CharType &) override { gcc_unreachable (); }
+  void visit (ReferenceType &) override { gcc_unreachable (); }
+  void visit (PointerType &) override { gcc_unreachable (); }
+  void visit (ParamType &) override { gcc_unreachable (); }
+  void visit (StrType &) override { gcc_unreachable (); }
+  void visit (NeverType &) override { gcc_unreachable (); }
+  void visit (PlaceholderType &) override { gcc_unreachable (); }
+  void visit (ProjectionType &) override { gcc_unreachable (); }
+  void visit (DynamicObjectType &) override { gcc_unreachable (); }
+
+  // FIXME
+  void visit (FnPtr &type) override { gcc_unreachable (); }
+
+  // call fns
+  void visit (FnType &type) override;
+  void visit (ClosureType &type) override { gcc_unreachable (); }
+
+private:
+  TypeCheckMethodCallExpr (HIR::MethodCallExpr &c,
+			   TyTy::BaseType *adjusted_self,
+			   Resolver::TypeCheckContext *context)
+    : resolved (nullptr), call (c), adjusted_self (adjusted_self),
+      context (context), mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType *resolved;
+  HIR::MethodCallExpr &call;
+  TyTy::BaseType *adjusted_self;
+  Resolver::TypeCheckContext *context;
+  Analysis::Mappings *mappings;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_CALL
diff --git a/gcc/rust/typecheck/rust-tyty-cmp.h b/gcc/rust/typecheck/rust-tyty-cmp.h
new file mode 100644
index 00000000000..07d1dea7464
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-cmp.h
@@ -0,0 +1,1554 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYTY_CMP_H
+#define RUST_TYTY_CMP_H
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+class BaseCmp : public TyConstVisitor
+{
+public:
+  virtual bool can_eq (const BaseType *other)
+  {
+    if (other->get_kind () == TypeKind::PARAM)
+      {
+	const ParamType *p = static_cast<const ParamType *> (other);
+	other = p->resolve ();
+      }
+    if (other->get_kind () == TypeKind::PLACEHOLDER)
+      {
+	const PlaceholderType *p = static_cast<const PlaceholderType *> (other);
+	if (p->can_resolve ())
+	  {
+	    other = p->resolve ();
+	  }
+      }
+    if (other->get_kind () == TypeKind::PROJECTION)
+      {
+	const ProjectionType *p = static_cast<const ProjectionType *> (other);
+	other = p->get ();
+      }
+
+    other->accept_vis (*this);
+    return ok;
+  }
+
+  virtual void visit (const TupleType &type) override
+  {
+    ok = false;
+
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ADTType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const InferType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FnType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FnPtr &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ArrayType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const SliceType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const BoolType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const IntType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const UintType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const USizeType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ISizeType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FloatType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ErrorType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const CharType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ReferenceType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const PointerType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const StrType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const NeverType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ProjectionType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const PlaceholderType &type) override
+  {
+    // it is ok for types to can eq to a placeholder
+    ok = true;
+  }
+
+  virtual void visit (const ParamType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const DynamicObjectType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ClosureType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+protected:
+  BaseCmp (const BaseType *base, bool emit_errors)
+    : mappings (Analysis::Mappings::get ()),
+      context (Resolver::TypeCheckContext::get ()), ok (false),
+      emit_error_flag (emit_errors)
+  {}
+
+  Analysis::Mappings *mappings;
+  Resolver::TypeCheckContext *context;
+
+  bool ok;
+  bool emit_error_flag;
+
+private:
+  /* Returns a pointer to the ty that created this rule. */
+  virtual const BaseType *get_base () const = 0;
+};
+
+class InferCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  InferCmp (const InferType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const BoolType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const IntType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const UintType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const USizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ISizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const FloatType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ArrayType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const SliceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ADTType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const TupleType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const InferType &type) override
+  {
+    switch (base->get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	ok = true;
+	return;
+
+	case InferType::InferTypeKind::INTEGRAL: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	}
+	break;
+
+	case InferType::InferTypeKind::FLOAT: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+	    {
+	      ok = true;
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	}
+	break;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const CharType &type) override
+  {
+    {
+      bool is_valid
+	= (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+      if (is_valid)
+	{
+	  ok = true;
+	  return;
+	}
+
+      BaseCmp::visit (type);
+    }
+  }
+
+  void visit (const ReferenceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const PointerType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const DynamicObjectType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ClosureType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const InferType *base;
+};
+
+class FnCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FnCmp (const FnType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+  void visit (const FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto a = base->param_at (i).second;
+	auto b = type.param_at (i).second;
+
+	if (!a->can_eq (b, emit_error_flag))
+	  {
+	    emit_error_flag = false;
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    if (!base->get_return_type ()->can_eq (type.get_return_type (),
+					   emit_error_flag))
+      {
+	emit_error_flag = false;
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FnType *base;
+};
+
+class FnptrCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FnptrCmp (const FnPtr *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const FnPtr &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i);
+	if (!this_param->can_eq (other_param, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i).second;
+	if (!this_param->can_eq (other_param, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FnPtr *base;
+};
+
+class ClosureCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ClosureCmp (const ClosureType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ClosureType *base;
+};
+
+class ArrayCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ArrayCmp (const ArrayType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ArrayType &type) override
+  {
+    // check base type
+    const BaseType *base_element = base->get_element_type ();
+    const BaseType *other_element = type.get_element_type ();
+    if (!base_element->can_eq (other_element, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ArrayType *base;
+};
+
+class SliceCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  SliceCmp (const SliceType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const SliceType &type) override
+  {
+    // check base type
+    const BaseType *base_element = base->get_element_type ();
+    const BaseType *other_element = type.get_element_type ();
+    if (!base_element->can_eq (other_element, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const SliceType *base;
+};
+
+class BoolCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  BoolCmp (const BoolType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const BoolType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const BoolType *base;
+};
+
+class IntCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  IntCmp (const IntType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const IntType &type) override
+  {
+    ok = type.get_int_kind () == base->get_int_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const IntType *base;
+};
+
+class UintCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  UintCmp (const UintType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const UintType &type) override
+  {
+    ok = type.get_uint_kind () == base->get_uint_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const UintType *base;
+};
+
+class FloatCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FloatCmp (const FloatType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::INTEGRAL;
+  }
+
+  void visit (const FloatType &type) override
+  {
+    ok = type.get_float_kind () == base->get_float_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FloatType *base;
+};
+
+class ADTCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ADTCmp (const ADTType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ADTType &type) override
+  {
+    if (base->get_adt_kind () != type.get_adt_kind ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (base->number_of_variants () != type.number_of_variants ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < type.number_of_variants (); ++i)
+      {
+	TyTy::VariantDef *a = base->get_variants ().at (i);
+	TyTy::VariantDef *b = type.get_variants ().at (i);
+
+	if (a->num_fields () != b->num_fields ())
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+
+	for (size_t j = 0; j < a->num_fields (); j++)
+	  {
+	    TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+	    TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+	    TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+	    TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+	    if (!this_field_ty->can_eq (other_field_ty, emit_error_flag))
+	      {
+		BaseCmp::visit (type);
+		return;
+	      }
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ADTType *base;
+};
+
+class TupleCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  TupleCmp (const TupleType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const TupleType &type) override
+  {
+    if (base->num_fields () != type.num_fields ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_fields (); i++)
+      {
+	BaseType *bo = base->get_field (i);
+	BaseType *fo = type.get_field (i);
+
+	if (!bo->can_eq (fo, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const TupleType *base;
+};
+
+class USizeCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  USizeCmp (const USizeType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const USizeType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const USizeType *base;
+};
+
+class ISizeCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ISizeCmp (const ISizeType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const ISizeType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ISizeType *base;
+};
+
+class CharCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  CharCmp (const CharType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+  void visit (const CharType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const CharType *base;
+};
+
+class ReferenceCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ReferenceCmp (const ReferenceType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ReferenceType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    bool mutability_match = base->is_mutable () == type.is_mutable ();
+    if (!mutability_match)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (!base_type->can_eq (other_base_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ReferenceType *base;
+};
+
+class PointerCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  PointerCmp (const PointerType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const PointerType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (!base_type->can_eq (other_base_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const PointerType *base;
+};
+
+class ParamCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ParamCmp (const ParamType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  // param types are a placeholder we shouldn't have cases where we unify
+  // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+  //
+  // foo<i32>{ a: 123 }.
+  // Then this enforces the i32 type to be referenced on the
+  // field via an hirid.
+  //
+  // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+  // to handle the typing of the struct
+  bool can_eq (const BaseType *other) override
+  {
+    if (!base->can_resolve ())
+      return BaseCmp::can_eq (other);
+
+    auto lookup = base->resolve ();
+    return lookup->can_eq (other, emit_error_flag);
+  }
+
+  // imagine the case where we have:
+  // struct Foo<T>(T);
+  // Then we declare a generic impl block
+  // impl <X>Foo<X> { ... }
+  // both of these types are compatible so we mostly care about the number of
+  // generic arguments
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const InferType &) override { ok = true; }
+
+  void visit (const FnType &) override { ok = true; }
+
+  void visit (const FnPtr &) override { ok = true; }
+
+  void visit (const ADTType &) override { ok = true; }
+
+  void visit (const ArrayType &) override { ok = true; }
+
+  void visit (const SliceType &) override { ok = true; }
+
+  void visit (const BoolType &) override { ok = true; }
+
+  void visit (const IntType &) override { ok = true; }
+
+  void visit (const UintType &) override { ok = true; }
+
+  void visit (const USizeType &) override { ok = true; }
+
+  void visit (const ISizeType &) override { ok = true; }
+
+  void visit (const FloatType &) override { ok = true; }
+
+  void visit (const CharType &) override { ok = true; }
+
+  void visit (const ReferenceType &) override { ok = true; }
+
+  void visit (const PointerType &) override { ok = true; }
+
+  void visit (const StrType &) override { ok = true; }
+
+  void visit (const NeverType &) override { ok = true; }
+
+  void visit (const DynamicObjectType &) override { ok = true; }
+
+  void visit (const PlaceholderType &type) override
+  {
+    ok = base->get_symbol ().compare (type.get_symbol ()) == 0;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ParamType *base;
+};
+
+class StrCmp : public BaseCmp
+{
+  // FIXME we will need a enum for the StrType like ByteBuf etc..
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  StrCmp (const StrType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const StrType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const StrType *base;
+};
+
+class NeverCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  NeverCmp (const NeverType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const NeverType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const NeverType *base;
+};
+
+class PlaceholderCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  PlaceholderCmp (const PlaceholderType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  bool can_eq (const BaseType *other) override
+  {
+    if (!base->can_resolve ())
+      return BaseCmp::can_eq (other);
+
+    BaseType *lookup = base->resolve ();
+    return lookup->can_eq (other, emit_error_flag);
+  }
+
+  void visit (const TupleType &) override { ok = true; }
+
+  void visit (const ADTType &) override { ok = true; }
+
+  void visit (const InferType &) override { ok = true; }
+
+  void visit (const FnType &) override { ok = true; }
+
+  void visit (const FnPtr &) override { ok = true; }
+
+  void visit (const ArrayType &) override { ok = true; }
+
+  void visit (const BoolType &) override { ok = true; }
+
+  void visit (const IntType &) override { ok = true; }
+
+  void visit (const UintType &) override { ok = true; }
+
+  void visit (const USizeType &) override { ok = true; }
+
+  void visit (const ISizeType &) override { ok = true; }
+
+  void visit (const FloatType &) override { ok = true; }
+
+  void visit (const ErrorType &) override { ok = true; }
+
+  void visit (const CharType &) override { ok = true; }
+
+  void visit (const ReferenceType &) override { ok = true; }
+
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const StrType &) override { ok = true; }
+
+  void visit (const NeverType &) override { ok = true; }
+
+  void visit (const SliceType &) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+
+  const PlaceholderType *base;
+};
+
+class DynamicCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  DynamicCmp (const DynamicObjectType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const DynamicObjectType &type) override
+  {
+    if (base->num_specified_bounds () != type.num_specified_bounds ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    ok = base->bounds_compatible (type, ref_locus, false);
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+
+  const DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_CMP_H
diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h
new file mode 100644
index 00000000000..77d912a5921
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-rules.h
@@ -0,0 +1,1584 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYTY_RULES
+#define RUST_TYTY_RULES
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+/* Rules specify how to unify two Ty. For example, the result of unifying the
+   two tuples (u64, A) and (B, i64) would be (u64, i64).
+
+   Performing a unification requires a double dispatch. To illustrate, suppose
+   we want to unify `ty1` and `ty2`. Here's what it looks like:
+     1. The caller calls `ty1.unify(ty2)`. This is the first dispatch.
+     2. `ty1` creates a rule specific to its type(e.g. TupleRules).
+     3. The rule calls `ty2.accept_vis(rule)`. This is the second dispatch.
+     4. `ty2` calls `rule.visit(*this)`, which will method-overload to the
+	      correct implementation at compile time.
+
+   The nice thing about Rules is that they seperate unification logic from the
+   representation of Ty. To support unifying a new Ty, implement its
+   `accept_vis` and `unify` method to pass the unification request to Rules.
+   Then, create a new `XXXRules` class and implement one `visit` method for
+   every Ty it can unify with. */
+class BaseRules : public TyVisitor
+{
+public:
+  virtual ~BaseRules () {}
+
+  /* Unify two ty. Returns a pointer to the newly-created unified ty, or nullptr
+     if the two types cannot be unified. The caller is responsible for releasing
+     the memory of the returned ty.
+
+     This method is meant to be used internally by Ty. If you're trying to unify
+     two ty, you can simply call `unify` on ty themselves. */
+  virtual BaseType *unify (BaseType *other)
+  {
+    if (other->get_kind () == TypeKind::PARAM)
+      {
+	ParamType *p = static_cast<ParamType *> (other);
+	other = p->resolve ();
+      }
+    else if (other->get_kind () == TypeKind::PLACEHOLDER)
+      {
+	PlaceholderType *p = static_cast<PlaceholderType *> (other);
+	if (p->can_resolve ())
+	  {
+	    other = p->resolve ();
+	    return get_base ()->unify (other);
+	  }
+      }
+    else if (other->get_kind () == TypeKind::PROJECTION)
+      {
+	ProjectionType *p = static_cast<ProjectionType *> (other);
+	other = p->get ();
+	return get_base ()->unify (other);
+      }
+
+    other->accept_vis (*this);
+    if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+      return resolved;
+
+    resolved->append_reference (get_base ()->get_ref ());
+    resolved->append_reference (other->get_ref ());
+    for (auto ref : get_base ()->get_combined_refs ())
+      resolved->append_reference (ref);
+    for (auto ref : other->get_combined_refs ())
+      resolved->append_reference (ref);
+
+    other->append_reference (resolved->get_ref ());
+    other->append_reference (get_base ()->get_ref ());
+    get_base ()->append_reference (resolved->get_ref ());
+    get_base ()->append_reference (other->get_ref ());
+
+    bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER;
+    bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER;
+    bool results_is_non_general_infer_var
+      = (result_is_infer_var
+	 && (static_cast<InferType *> (resolved))->get_infer_kind ()
+	      != TyTy::InferType::GENERAL);
+    if (result_resolved || results_is_non_general_infer_var)
+      {
+	for (auto &ref : resolved->get_combined_refs ())
+	  {
+	    TyTy::BaseType *ref_tyty = nullptr;
+	    bool ok = context->lookup_type (ref, &ref_tyty);
+	    if (!ok)
+	      continue;
+
+	    // if any of the types are inference variables lets fix them
+	    if (ref_tyty->get_kind () == TyTy::TypeKind::INFER)
+	      {
+		context->insert_type (
+		  Analysis::NodeMapping (mappings->get_current_crate (),
+					 UNKNOWN_NODEID, ref,
+					 UNKNOWN_LOCAL_DEFID),
+		  resolved->clone ());
+	      }
+	  }
+      }
+    return resolved;
+  }
+
+  virtual void visit (TupleType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ADTType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (InferType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FnType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FnPtr &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ArrayType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (SliceType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (BoolType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (IntType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (UintType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (USizeType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ISizeType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FloatType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ErrorType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (CharType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ReferenceType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (PointerType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ParamType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (StrType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (NeverType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (PlaceholderType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ProjectionType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (DynamicObjectType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ClosureType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+protected:
+  BaseRules (BaseType *base)
+    : mappings (Analysis::Mappings::get ()),
+      context (Resolver::TypeCheckContext::get ()),
+      resolved (new ErrorType (base->get_ref (), base->get_ref ()))
+  {}
+
+  Analysis::Mappings *mappings;
+  Resolver::TypeCheckContext *context;
+
+  /* Temporary storage for the result of a unification.
+     We could return the result directly instead of storing it in the rule
+     object, but that involves modifying the visitor pattern to accommodate
+     the return value, which is too complex. */
+  BaseType *resolved;
+
+private:
+  /* Returns a pointer to the ty that created this rule. */
+  virtual BaseType *get_base () = 0;
+};
+
+class InferRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  InferRules (InferType *base) : BaseRules (base), base (base) {}
+
+  void visit (BoolType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (IntType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (UintType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (USizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ISizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (FloatType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ArrayType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (SliceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ADTType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (TupleType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (InferType &type) override
+  {
+    switch (base->get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	resolved = type.clone ();
+	return;
+
+	case InferType::InferTypeKind::INTEGRAL: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+	    {
+	      resolved = type.clone ();
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      resolved = base->clone ();
+	      return;
+	    }
+	}
+	break;
+
+	case InferType::InferTypeKind::FLOAT: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+	    {
+	      resolved = type.clone ();
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      resolved = base->clone ();
+	      return;
+	    }
+	}
+	break;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (CharType &type) override
+  {
+    {
+      bool is_valid
+	= (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+      if (is_valid)
+	{
+	  resolved = type.clone ();
+	  return;
+	}
+
+      BaseRules::visit (type);
+    }
+  }
+
+  void visit (ReferenceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (PointerType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ParamType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (DynamicObjectType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ClosureType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  InferType *base;
+};
+
+class FnRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FnRules (FnType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto a = base->param_at (i).second;
+	auto b = type.param_at (i).second;
+
+	auto unified_param = a->unify (b);
+	if (unified_param == nullptr)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    auto unified_return
+      = base->get_return_type ()->unify (type.get_return_type ());
+    if (unified_return == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FnType *base;
+};
+
+class FnptrRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FnptrRules (FnPtr *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnPtr &type) override
+  {
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    auto unified_result = this_ret_type->unify (other_ret_type);
+    if (unified_result == nullptr
+	|| unified_result->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i);
+	auto unified_param = this_param->unify (other_param);
+	if (unified_param == nullptr
+	    || unified_param->get_kind () == TypeKind::ERROR)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnType &type) override
+  {
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    auto unified_result = this_ret_type->unify (other_ret_type);
+    if (unified_result == nullptr
+	|| unified_result->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i).second;
+	auto unified_param = this_param->unify (other_param);
+	if (unified_param == nullptr
+	    || unified_param->get_kind () == TypeKind::ERROR)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FnPtr *base;
+};
+
+class ClosureRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ClosureRules (ClosureType *base) : BaseRules (base), base (base) {}
+
+  // TODO
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ClosureType *base;
+};
+
+class ArrayRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ArrayRules (ArrayType *base) : BaseRules (base), base (base) {}
+
+  void visit (ArrayType &type) override
+  {
+    // check base type
+    auto base_resolved
+      = base->get_element_type ()->unify (type.get_element_type ());
+    if (base_resolved == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved
+      = new ArrayType (type.get_ref (), type.get_ty_ref (),
+		       type.get_ident ().locus, type.get_capacity_expr (),
+		       TyVar (base_resolved->get_ref ()));
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ArrayType *base;
+};
+
+class SliceRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  SliceRules (SliceType *base) : BaseRules (base), base (base) {}
+
+  void visit (SliceType &type) override
+  {
+    // check base type
+    auto base_resolved
+      = base->get_element_type ()->unify (type.get_element_type ());
+    if (base_resolved == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new SliceType (type.get_ref (), type.get_ty_ref (),
+			      type.get_ident ().locus,
+			      TyVar (base_resolved->get_ref ()));
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  SliceType *base;
+};
+
+class BoolRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  BoolRules (BoolType *base) : BaseRules (base), base (base) {}
+
+  void visit (BoolType &type) override
+  {
+    resolved = new BoolType (type.get_ref (), type.get_ty_ref ());
+  }
+
+  void visit (InferType &type) override
+  {
+    switch (type.get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	resolved = base->clone ();
+	break;
+
+      default:
+	BaseRules::visit (type);
+	break;
+      }
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  BoolType *base;
+};
+
+class IntRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  IntRules (IntType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (IntType &type) override
+  {
+    if (type.get_int_kind () != base->get_int_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved
+      = new IntType (type.get_ref (), type.get_ty_ref (), type.get_int_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  IntType *base;
+};
+
+class UintRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  UintRules (UintType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (UintType &type) override
+  {
+    if (type.get_uint_kind () != base->get_uint_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new UintType (type.get_ref (), type.get_ty_ref (),
+			     type.get_uint_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  UintType *base;
+};
+
+class FloatRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FloatRules (FloatType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FloatType &type) override
+  {
+    if (type.get_float_kind () != base->get_float_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new FloatType (type.get_ref (), type.get_ty_ref (),
+			      type.get_float_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FloatType *base;
+};
+
+class ADTRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ADTRules (ADTType *base) : BaseRules (base), base (base) {}
+
+  void visit (ADTType &type) override
+  {
+    if (base->get_adt_kind () != type.get_adt_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->number_of_variants () != type.number_of_variants ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < type.number_of_variants (); ++i)
+      {
+	TyTy::VariantDef *a = base->get_variants ().at (i);
+	TyTy::VariantDef *b = type.get_variants ().at (i);
+
+	if (a->num_fields () != b->num_fields ())
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+
+	for (size_t j = 0; j < a->num_fields (); j++)
+	  {
+	    TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+	    TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+	    TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+	    TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+	    BaseType *unified_ty = this_field_ty->unify (other_field_ty);
+	    if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+	      return;
+	  }
+      }
+
+    // generic args for the unit-struct case
+    if (type.is_unit () && base->is_unit ())
+      {
+	rust_assert (type.get_num_substitutions ()
+		     == base->get_num_substitutions ());
+
+	for (size_t i = 0; i < type.get_num_substitutions (); i++)
+	  {
+	    auto &a = base->get_substs ().at (i);
+	    auto &b = type.get_substs ().at (i);
+
+	    auto pa = a.get_param_ty ();
+	    auto pb = b.get_param_ty ();
+
+	    auto res = pa->unify (pb);
+	    if (res->get_kind () == TyTy::TypeKind::ERROR)
+	      {
+		return;
+	      }
+	  }
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ADTType *base;
+};
+
+class TupleRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  TupleRules (TupleType *base) : BaseRules (base), base (base) {}
+
+  void visit (TupleType &type) override
+  {
+    if (base->num_fields () != type.num_fields ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    std::vector<TyVar> fields;
+    for (size_t i = 0; i < base->num_fields (); i++)
+      {
+	BaseType *bo = base->get_field (i);
+	BaseType *fo = type.get_field (i);
+
+	BaseType *unified_ty = bo->unify (fo);
+	if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+	  return;
+
+	fields.push_back (TyVar (unified_ty->get_ref ()));
+      }
+
+    resolved = new TyTy::TupleType (type.get_ref (), type.get_ty_ref (),
+				    type.get_ident ().locus, fields);
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  TupleType *base;
+};
+
+class USizeRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  USizeRules (USizeType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (USizeType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  USizeType *base;
+};
+
+class ISizeRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ISizeRules (ISizeType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (ISizeType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ISizeType *base;
+};
+
+class CharRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  CharRules (CharType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (CharType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  CharType *base;
+};
+
+class ReferenceRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ReferenceRules (ReferenceType *base) : BaseRules (base), base (base) {}
+
+  void visit (ReferenceType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+    if (base_resolved == nullptr
+	|| base_resolved->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new ReferenceType (base->get_ref (), base->get_ty_ref (),
+				  TyVar (base_resolved->get_ref ()),
+				  base->mutability ());
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ReferenceType *base;
+};
+
+class PointerRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  PointerRules (PointerType *base) : BaseRules (base), base (base) {}
+
+  void visit (PointerType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+    if (base_resolved == nullptr
+	|| base_resolved->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new PointerType (base->get_ref (), base->get_ty_ref (),
+				TyVar (base_resolved->get_ref ()),
+				base->mutability ());
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  PointerType *base;
+};
+
+class ParamRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ParamRules (ParamType *base) : BaseRules (base), base (base) {}
+
+  // param types are a placeholder we shouldn't have cases where we unify
+  // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+  //
+  // foo<i32>{ a: 123 }.
+  // Then this enforces the i32 type to be referenced on the
+  // field via an hirid.
+  //
+  // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+  // to handle the typing of the struct
+  BaseType *unify (BaseType *other) override final
+  {
+    if (!base->can_resolve ())
+      return BaseRules::unify (other);
+
+    auto lookup = base->resolve ();
+    return lookup->unify (other);
+  }
+
+  void visit (ParamType &type) override
+  {
+    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ParamType *base;
+};
+
+class StrRules : public BaseRules
+{
+  // FIXME we will need a enum for the StrType like ByteBuf etc..
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  StrRules (StrType *base) : BaseRules (base), base (base) {}
+
+  void visit (StrType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  StrType *base;
+};
+
+class NeverRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  NeverRules (NeverType *base) : BaseRules (base), base (base) {}
+
+  void visit (NeverType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  NeverType *base;
+};
+
+class PlaceholderRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  PlaceholderRules (PlaceholderType *base) : BaseRules (base), base (base) {}
+
+  BaseType *unify (BaseType *other) override final
+  {
+    if (!base->can_resolve ())
+      return BaseRules::unify (other);
+
+    BaseType *lookup = base->resolve ();
+    return lookup->unify (other);
+  }
+
+  void visit (PlaceholderType &type) override
+  {
+    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  PlaceholderType *base;
+};
+
+class DynamicRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  DynamicRules (DynamicObjectType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+  void visit (DynamicObjectType &type) override
+  {
+    if (base->num_specified_bounds () != type.num_specified_bounds ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    if (!base->bounds_compatible (type, ref_locus, true))
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_RULES
diff --git a/gcc/rust/typecheck/rust-tyty-visitor.h b/gcc/rust/typecheck/rust-tyty-visitor.h
new file mode 100644
index 00000000000..464e70d39d7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-visitor.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYTY_VISITOR
+#define RUST_TYTY_VISITOR
+
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace TyTy {
+
+class TyVisitor
+{
+public:
+  virtual void visit (InferType &type) = 0;
+  virtual void visit (ADTType &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (FnType &type) = 0;
+  virtual void visit (FnPtr &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (BoolType &type) = 0;
+  virtual void visit (IntType &type) = 0;
+  virtual void visit (UintType &type) = 0;
+  virtual void visit (FloatType &type) = 0;
+  virtual void visit (USizeType &type) = 0;
+  virtual void visit (ISizeType &type) = 0;
+  virtual void visit (ErrorType &type) = 0;
+  virtual void visit (CharType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (PointerType &type) = 0;
+  virtual void visit (ParamType &type) = 0;
+  virtual void visit (StrType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (PlaceholderType &type) = 0;
+  virtual void visit (ProjectionType &type) = 0;
+  virtual void visit (DynamicObjectType &type) = 0;
+  virtual void visit (ClosureType &type) = 0;
+};
+
+class TyConstVisitor
+{
+public:
+  virtual void visit (const InferType &type) = 0;
+  virtual void visit (const ADTType &type) = 0;
+  virtual void visit (const TupleType &type) = 0;
+  virtual void visit (const FnType &type) = 0;
+  virtual void visit (const FnPtr &type) = 0;
+  virtual void visit (const ArrayType &type) = 0;
+  virtual void visit (const SliceType &type) = 0;
+  virtual void visit (const BoolType &type) = 0;
+  virtual void visit (const IntType &type) = 0;
+  virtual void visit (const UintType &type) = 0;
+  virtual void visit (const FloatType &type) = 0;
+  virtual void visit (const USizeType &type) = 0;
+  virtual void visit (const ISizeType &type) = 0;
+  virtual void visit (const ErrorType &type) = 0;
+  virtual void visit (const CharType &type) = 0;
+  virtual void visit (const ReferenceType &type) = 0;
+  virtual void visit (const PointerType &type) = 0;
+  virtual void visit (const ParamType &type) = 0;
+  virtual void visit (const StrType &type) = 0;
+  virtual void visit (const NeverType &type) = 0;
+  virtual void visit (const PlaceholderType &type) = 0;
+  virtual void visit (const ProjectionType &type) = 0;
+  virtual void visit (const DynamicObjectType &type) = 0;
+  virtual void visit (const ClosureType &type) = 0;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_VISITOR
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
new file mode 100644
index 00000000000..3c2c6786940
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -0,0 +1,2885 @@
+// 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-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-tyty-call.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-tyty-rules.h"
+#include "rust-tyty-cmp.h"
+#include "rust-hir-map.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-trait-ref.h"
+#include "rust-hir-type-bounds.h"
+
+namespace Rust {
+namespace TyTy {
+
+std::string
+TypeKindFormat::to_string (TypeKind kind)
+{
+  switch (kind)
+    {
+    case TypeKind::INFER:
+      return "Infer";
+
+    case TypeKind::ADT:
+      return "ADT";
+
+    case TypeKind::STR:
+      return "STR";
+
+    case TypeKind::REF:
+      return "REF";
+
+    case TypeKind::POINTER:
+      return "POINTER";
+
+    case TypeKind::PARAM:
+      return "PARAM";
+
+    case TypeKind::ARRAY:
+      return "ARRAY";
+
+    case TypeKind::SLICE:
+      return "SLICE";
+
+    case TypeKind::FNDEF:
+      return "FnDef";
+
+    case TypeKind::FNPTR:
+      return "FnPtr";
+
+    case TypeKind::TUPLE:
+      return "Tuple";
+
+    case TypeKind::BOOL:
+      return "Bool";
+
+    case TypeKind::CHAR:
+      return "Char";
+
+    case TypeKind::INT:
+      return "Int";
+
+    case TypeKind::UINT:
+      return "Uint";
+
+    case TypeKind::FLOAT:
+      return "Float";
+
+    case TypeKind::USIZE:
+      return "Usize";
+
+    case TypeKind::ISIZE:
+      return "Isize";
+
+    case TypeKind::NEVER:
+      return "Never";
+
+    case TypeKind::PLACEHOLDER:
+      return "Placeholder";
+
+    case TypeKind::PROJECTION:
+      return "Projection";
+
+    case TypeKind::DYNAMIC:
+      return "Dynamic";
+
+    case TypeKind::CLOSURE:
+      return "Closure";
+
+    case TypeKind::ERROR:
+      return "ERROR";
+    }
+  gcc_unreachable ();
+}
+
+bool
+is_primitive_type_kind (TypeKind kind)
+{
+  switch (kind)
+    {
+    case TypeKind::BOOL:
+    case TypeKind::CHAR:
+    case TypeKind::INT:
+    case TypeKind::UINT:
+    case TypeKind::ISIZE:
+    case TypeKind::USIZE:
+    case TypeKind::FLOAT:
+    case TypeKind::NEVER:
+    case TypeKind::STR:
+      return true;
+    default:
+      return false;
+    }
+}
+
+bool
+BaseType::satisfies_bound (const TypeBoundPredicate &predicate) const
+{
+  const Resolver::TraitReference *query = predicate.get ();
+  for (auto &bound : specified_bounds)
+    {
+      const Resolver::TraitReference *item = bound.get ();
+      bool found = item->get_mappings ().get_defid ()
+		   == query->get_mappings ().get_defid ();
+      if (found)
+	return true;
+    }
+
+  auto probed = Resolver::TypeBoundsProbe::Probe (this);
+  for (auto &b : probed)
+    {
+      const Resolver::TraitReference *bound = b.first;
+      bool found = bound->get_mappings ().get_defid ()
+		   == query->get_mappings ().get_defid ();
+      if (found)
+	return true;
+    }
+
+  return false;
+}
+
+bool
+BaseType::bounds_compatible (const BaseType &other, Location locus,
+			     bool emit_error) const
+{
+  std::vector<std::reference_wrapper<const TypeBoundPredicate>>
+    unsatisfied_bounds;
+  for (auto &bound : get_specified_bounds ())
+    {
+      if (!other.satisfies_bound (bound))
+	unsatisfied_bounds.push_back (bound);
+    }
+
+  // lets emit a single error for this
+  if (unsatisfied_bounds.size () > 0)
+    {
+      RichLocation r (locus);
+      std::string missing_preds;
+      for (size_t i = 0; i < unsatisfied_bounds.size (); i++)
+	{
+	  const TypeBoundPredicate &pred = unsatisfied_bounds.at (i);
+	  r.add_range (pred.get_locus ());
+	  missing_preds += pred.get_name ();
+
+	  bool have_next = (i + 1) < unsatisfied_bounds.size ();
+	  if (have_next)
+	    missing_preds += ", ";
+	}
+
+      if (emit_error)
+	{
+	  rust_error_at (r,
+			 "bounds not satisfied for %s %<%s%> is not satisfied",
+			 other.get_name ().c_str (), missing_preds.c_str ());
+	  // rust_assert (!emit_error);
+	}
+    }
+
+  return unsatisfied_bounds.size () == 0;
+}
+
+void
+BaseType::inherit_bounds (const BaseType &other)
+{
+  inherit_bounds (other.get_specified_bounds ());
+}
+
+void
+BaseType::inherit_bounds (
+  const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
+{
+  // FIXME
+  // 1. This needs to union the bounds
+  // 2. Do some checking for trait polarity to ensure compatibility
+  for (auto &bound : specified_bounds)
+    {
+      add_bound (bound);
+    }
+}
+
+const BaseType *
+BaseType::get_root () const
+{
+  // FIXME this needs to be it its own visitor class with a vector adjustments
+  const TyTy::BaseType *root = this;
+  if (get_kind () == TyTy::REF)
+    {
+      const ReferenceType *r = static_cast<const ReferenceType *> (root);
+      root = r->get_base ()->get_root ();
+    }
+  else if (get_kind () == TyTy::POINTER)
+    {
+      const PointerType *r = static_cast<const PointerType *> (root);
+      root = r->get_base ()->get_root ();
+    }
+
+  // these are an unsize
+  else if (get_kind () == TyTy::SLICE)
+    {
+      const SliceType *r = static_cast<const SliceType *> (root);
+      root = r->get_element_type ()->get_root ();
+    }
+  // else if (get_kind () == TyTy::ARRAY)
+  //   {
+  //     const ArrayType *r = static_cast<const ArrayType *> (root);
+  //     root = r->get_element_type ()->get_root ();
+  //   }
+
+  return root;
+}
+
+const BaseType *
+BaseType::destructure () const
+{
+  int recurisve_ops = 0;
+  const BaseType *x = this;
+  while (true)
+    {
+      if (recurisve_ops++ >= rust_max_recursion_depth)
+	{
+	  rust_error_at (
+	    Location (),
+	    "%<recursion depth%> count exceeds limit of %i (use "
+	    "%<frust-max-recursion-depth=%> to increase the limit)",
+	    rust_max_recursion_depth);
+	  return new ErrorType (get_ref ());
+	}
+
+      switch (x->get_kind ())
+	{
+	  case TyTy::TypeKind::PARAM: {
+	    const TyTy::ParamType *p = static_cast<const TyTy::ParamType *> (x);
+	    x = p->resolve ();
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PLACEHOLDER: {
+	    const TyTy::PlaceholderType *p
+	      = static_cast<const TyTy::PlaceholderType *> (x);
+	    rust_assert (p->can_resolve ());
+	    x = p->resolve ();
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PROJECTION: {
+	    const TyTy::ProjectionType *p
+	      = static_cast<const TyTy::ProjectionType *> (x);
+	    x = p->get ();
+	  }
+	  break;
+
+	default:
+	  return x;
+	}
+    }
+
+  return x;
+}
+
+TyVar::TyVar (HirId ref) : ref (ref)
+{
+  // ensure this reference is defined within the context
+  auto context = Resolver::TypeCheckContext::get ();
+  BaseType *lookup = nullptr;
+  bool ok = context->lookup_type (ref, &lookup);
+  rust_assert (ok);
+}
+
+BaseType *
+TyVar::get_tyty () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  BaseType *lookup = nullptr;
+  bool ok = context->lookup_type (ref, &lookup);
+  rust_assert (ok);
+  return lookup;
+}
+
+TyVar
+TyVar::get_implicit_infer_var (Location locus)
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  InferType *infer = new InferType (mappings->get_next_hir_id (),
+				    InferType::InferTypeKind::GENERAL, locus);
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       infer->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			infer);
+  mappings->insert_location (infer->get_ref (), locus);
+
+  return TyVar (infer->get_ref ());
+}
+
+TyVar
+TyVar::subst_covariant_var (TyTy::BaseType *orig, TyTy::BaseType *subst)
+{
+  if (orig->get_kind () != TyTy::TypeKind::PARAM)
+    return TyVar (subst->get_ty_ref ());
+  else if (subst->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      TyTy::ParamType *p = static_cast<TyTy::ParamType *> (subst);
+      if (p->resolve ()->get_kind () == TyTy::TypeKind::PARAM)
+	{
+	  return TyVar (subst->get_ty_ref ());
+	}
+    }
+
+  return TyVar (subst->get_ref ());
+}
+
+TyVar
+TyVar::clone () const
+{
+  TyTy::BaseType *c = get_tyty ()->clone ();
+  return TyVar (c->get_ref ());
+}
+
+TyVar
+TyVar::monomorphized_clone () const
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  // this needs a new hirid
+  TyTy::BaseType *c = get_tyty ()->monomorphized_clone ();
+  c->set_ref (mappings->get_next_hir_id ());
+
+  // insert it
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID, c->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			c);
+
+  return TyVar (c->get_ref ());
+}
+
+TyWithLocation::TyWithLocation (BaseType *ty, Location locus)
+  : ty (ty), locus (locus)
+{}
+
+TyWithLocation::TyWithLocation (BaseType *ty) : ty (ty)
+{
+  auto mappings = Analysis::Mappings::get ();
+  locus = mappings->lookup_location (ty->get_ref ());
+}
+
+void
+InferType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+InferType::as_string () const
+{
+  switch (infer_kind)
+    {
+    case GENERAL:
+      return "T?";
+    case INTEGRAL:
+      return "<integer>";
+    case FLOAT:
+      return "<float>";
+    }
+  return "<infer::error>";
+}
+
+BaseType *
+InferType::unify (BaseType *other)
+{
+  InferRules r (this);
+  return r.unify (other);
+}
+
+bool
+InferType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  InferCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+InferType::clone () const
+{
+  // clones for inference variables are special in that they _must_ exist within
+  // the type check context and we must ensure we don't loose the chain
+  // otherwise we will end up in the missing type annotations case
+  //
+  // This means we cannot simply take over the same reference we must generate a
+  // new ref just like the get_implicit_infer_var code then we can setup the
+  // chain of references accordingly to ensure we don't loose the ability to
+  // update the inference variables when we solve the type
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  InferType *clone
+    = new InferType (mappings->get_next_hir_id (), get_infer_kind (),
+		     get_ident ().locus, get_combined_refs ());
+
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       clone->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			clone);
+  mappings->insert_location (clone->get_ref (),
+			     mappings->lookup_location (get_ref ()));
+
+  // setup the chain to reference this
+  clone->append_reference (get_ref ());
+
+  return clone;
+}
+
+BaseType *
+InferType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+InferType::default_type (BaseType **type) const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  bool ok = false;
+  switch (infer_kind)
+    {
+    case GENERAL:
+      return false;
+
+      case INTEGRAL: {
+	ok = context->lookup_builtin ("i32", type);
+	rust_assert (ok);
+	return ok;
+      }
+
+      case FLOAT: {
+	ok = context->lookup_builtin ("f64", type);
+	rust_assert (ok);
+	return ok;
+      }
+    }
+  return false;
+}
+
+void
+ErrorType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ErrorType::as_string () const
+{
+  return "<tyty::error>";
+}
+
+BaseType *
+ErrorType::unify (BaseType *other)
+{
+  return this;
+}
+
+bool
+ErrorType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  return get_kind () == other->get_kind ();
+}
+
+BaseType *
+ErrorType::clone () const
+{
+  return new ErrorType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+ErrorType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+std::string
+StructFieldType::as_string () const
+{
+  return name + ":" + get_field_type ()->debug_str ();
+}
+
+bool
+StructFieldType::is_equal (const StructFieldType &other) const
+{
+  bool names_eq = get_name ().compare (other.get_name ()) == 0;
+
+  TyTy::BaseType *o = other.get_field_type ();
+  if (o->get_kind () == TypeKind::PARAM)
+    {
+      ParamType *op = static_cast<ParamType *> (o);
+      o = op->resolve ();
+    }
+
+  bool types_eq = get_field_type ()->is_equal (*o);
+
+  return names_eq && types_eq;
+}
+
+StructFieldType *
+StructFieldType::clone () const
+{
+  return new StructFieldType (get_ref (), get_name (),
+			      get_field_type ()->clone ());
+}
+
+StructFieldType *
+StructFieldType::monomorphized_clone () const
+{
+  return new StructFieldType (get_ref (), get_name (),
+			      get_field_type ()->monomorphized_clone ());
+}
+
+bool
+SubstitutionParamMapping::need_substitution () const
+{
+  if (!param->can_resolve ())
+    return true;
+
+  auto resolved = param->resolve ();
+  return !resolved->is_concrete ();
+}
+
+bool
+SubstitutionParamMapping::fill_param_ty (
+  SubstitutionArgumentMappings &subst_mappings, Location locus)
+{
+  SubstitutionArg arg = SubstitutionArg::error ();
+  bool ok = subst_mappings.get_argument_for_symbol (get_param_ty (), &arg);
+  if (!ok)
+    return true;
+
+  TyTy::BaseType &type = *arg.get_tyty ();
+  if (type.get_kind () == TyTy::TypeKind::INFER)
+    {
+      type.inherit_bounds (*param);
+    }
+  else
+    {
+      if (!param->bounds_compatible (type, locus, true))
+	return false;
+    }
+
+  if (type.get_kind () == TypeKind::PARAM)
+    {
+      // delete param;
+      param = static_cast<ParamType *> (type.clone ());
+    }
+  else
+    {
+      // check the substitution is compatible with bounds
+      if (!param->bounds_compatible (type, locus, true))
+	return false;
+
+      // recursively pass this down to all HRTB's
+      for (auto &bound : param->get_specified_bounds ())
+	bound.handle_substitions (subst_mappings);
+
+      param->set_ty_ref (type.get_ref ());
+    }
+
+  return true;
+}
+
+void
+SubstitutionParamMapping::override_context ()
+{
+  if (!param->can_resolve ())
+    return;
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       param->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			param->resolve ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::get_mappings_from_generic_args (HIR::GenericArgs &args)
+{
+  if (args.get_binding_args ().size () > 0)
+    {
+      RichLocation r (args.get_locus ());
+      for (auto &binding : args.get_binding_args ())
+	r.add_range (binding.get_locus ());
+
+      rust_error_at (r, "associated type bindings are not allowed here");
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  // for inherited arguments
+  size_t offs = used_arguments.size ();
+  if (args.get_type_args ().size () + offs > substitutions.size ())
+    {
+      RichLocation r (args.get_locus ());
+      r.add_range (substitutions.front ().get_param_locus ());
+
+      rust_error_at (
+	r,
+	"generic item takes at most %lu type arguments but %lu were supplied",
+	(unsigned long) substitutions.size (),
+	(unsigned long) args.get_type_args ().size ());
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  if (args.get_type_args ().size () + offs < min_required_substitutions ())
+    {
+      RichLocation r (args.get_locus ());
+      r.add_range (substitutions.front ().get_param_locus ());
+
+      rust_error_at (
+	r,
+	"generic item takes at least %lu type arguments but %lu were supplied",
+	(unsigned long) (min_required_substitutions () - offs),
+	(unsigned long) args.get_type_args ().size ());
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  std::vector<SubstitutionArg> mappings = used_arguments.get_mappings ();
+  for (auto &arg : args.get_type_args ())
+    {
+      BaseType *resolved = Resolver::TypeCheckType::Resolve (arg.get ());
+      if (resolved == nullptr || resolved->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (args.get_locus (), "failed to resolve type arguments");
+	  return SubstitutionArgumentMappings::error ();
+	}
+
+      SubstitutionArg subst_arg (&substitutions.at (offs), resolved);
+      offs++;
+      mappings.push_back (std::move (subst_arg));
+    }
+
+  // we must need to fill out defaults
+  size_t left_over
+    = num_required_substitutions () - min_required_substitutions ();
+  if (left_over > 0)
+    {
+      for (size_t offs = mappings.size (); offs < substitutions.size (); offs++)
+	{
+	  SubstitutionParamMapping &param = substitutions.at (offs);
+	  rust_assert (param.param_has_default_ty ());
+
+	  BaseType *resolved = param.get_default_ty ();
+	  if (resolved->get_kind () == TypeKind::ERROR)
+	    return SubstitutionArgumentMappings::error ();
+
+	  // this resolved default might already contain default parameters
+	  if (resolved->contains_type_parameters ())
+	    {
+	      SubstitutionArgumentMappings intermediate (mappings,
+							 args.get_locus ());
+	      resolved = Resolver::SubstMapperInternal::Resolve (resolved,
+								 intermediate);
+
+	      if (resolved->get_kind () == TypeKind::ERROR)
+		return SubstitutionArgumentMappings::error ();
+	    }
+
+	  SubstitutionArg subst_arg (&param, resolved);
+	  mappings.push_back (std::move (subst_arg));
+	}
+    }
+
+  return SubstitutionArgumentMappings (mappings, args.get_locus ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::adjust_mappings_for_this (
+  SubstitutionArgumentMappings &mappings)
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+  for (size_t i = 0; i < substitutions.size (); i++)
+    {
+      auto &subst = substitutions.at (i);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      if (mappings.size () == substitutions.size ())
+	{
+	  mappings.get_argument_at (i, &arg);
+	}
+      else
+	{
+	  if (subst.needs_substitution ())
+	    {
+	      // get from passed in mappings
+	      mappings.get_argument_for_symbol (subst.get_param_ty (), &arg);
+	    }
+	  else
+	    {
+	      // we should already have this somewhere
+	      used_arguments.get_argument_for_symbol (subst.get_param_ty (),
+						      &arg);
+	    }
+	}
+
+      bool ok = !arg.is_error ();
+      if (ok)
+	{
+	  SubstitutionArg adjusted (&subst, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  if (resolved_mappings.empty ())
+    return SubstitutionArgumentMappings::error ();
+
+  return SubstitutionArgumentMappings (resolved_mappings, mappings.get_locus (),
+				       mappings.get_subst_cb (),
+				       mappings.trait_item_mode ());
+}
+
+bool
+SubstitutionRef::are_mappings_bound (SubstitutionArgumentMappings &mappings)
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+  for (size_t i = 0; i < substitutions.size (); i++)
+    {
+      auto &subst = substitutions.at (i);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      if (mappings.size () == substitutions.size ())
+	{
+	  mappings.get_argument_at (i, &arg);
+	}
+      else
+	{
+	  if (subst.needs_substitution ())
+	    {
+	      // get from passed in mappings
+	      mappings.get_argument_for_symbol (subst.get_param_ty (), &arg);
+	    }
+	  else
+	    {
+	      // we should already have this somewhere
+	      used_arguments.get_argument_for_symbol (subst.get_param_ty (),
+						      &arg);
+	    }
+	}
+
+      bool ok = !arg.is_error ();
+      if (ok)
+	{
+	  SubstitutionArg adjusted (&subst, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  return !resolved_mappings.empty ();
+}
+
+// this function assumes that the mappings being passed are for the same type as
+// this new substitution reference so ordering matters here
+SubstitutionArgumentMappings
+SubstitutionRef::solve_mappings_from_receiver_for_self (
+  SubstitutionArgumentMappings &mappings) const
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+
+  rust_assert (mappings.size () == get_num_substitutions ());
+  for (size_t i = 0; i < get_num_substitutions (); i++)
+    {
+      const SubstitutionParamMapping &param_mapping = substitutions.at (i);
+      SubstitutionArg &arg = mappings.get_mappings ().at (i);
+
+      if (param_mapping.needs_substitution ())
+	{
+	  SubstitutionArg adjusted (&param_mapping, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  return SubstitutionArgumentMappings (resolved_mappings,
+				       mappings.get_locus ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::solve_missing_mappings_from_this (SubstitutionRef &ref,
+						   SubstitutionRef &to)
+{
+  rust_assert (!ref.needs_substitution ());
+  rust_assert (needs_substitution ());
+  rust_assert (get_num_substitutions () == ref.get_num_substitutions ());
+
+  Location locus = used_arguments.get_locus ();
+  std::vector<SubstitutionArg> resolved_mappings;
+
+  std::map<HirId, std::pair<ParamType *, BaseType *>> substs;
+  for (size_t i = 0; i < get_num_substitutions (); i++)
+    {
+      SubstitutionParamMapping &a = substitutions.at (i);
+      SubstitutionParamMapping &b = ref.substitutions.at (i);
+
+      if (a.need_substitution ())
+	{
+	  const BaseType *root = a.get_param_ty ()->resolve ()->get_root ();
+	  rust_assert (root->get_kind () == TyTy::TypeKind::PARAM);
+	  const ParamType *p = static_cast<const TyTy::ParamType *> (root);
+
+	  substs[p->get_ty_ref ()] = {static_cast<ParamType *> (p->clone ()),
+				      b.get_param_ty ()->resolve ()};
+	}
+    }
+
+  for (auto it = substs.begin (); it != substs.end (); it++)
+    {
+      HirId param_id = it->first;
+      BaseType *arg = it->second.second;
+
+      const SubstitutionParamMapping *associate_param = nullptr;
+      for (SubstitutionParamMapping &p : to.substitutions)
+	{
+	  if (p.get_param_ty ()->get_ty_ref () == param_id)
+	    {
+	      associate_param = &p;
+	      break;
+	    }
+	}
+
+      rust_assert (associate_param != nullptr);
+      SubstitutionArg argument (associate_param, arg);
+      resolved_mappings.push_back (std::move (argument));
+    }
+
+  return SubstitutionArgumentMappings (resolved_mappings, locus);
+}
+
+bool
+SubstitutionRef::monomorphize ()
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  for (const auto &subst : get_substs ())
+    {
+      const TyTy::ParamType *pty = subst.get_param_ty ();
+
+      if (!pty->can_resolve ())
+	continue;
+
+      const TyTy::BaseType *binding = pty->resolve ();
+      if (binding->get_kind () == TyTy::TypeKind::PARAM)
+	continue;
+
+      for (const auto &bound : pty->get_specified_bounds ())
+	{
+	  const Resolver::TraitReference *specified_bound_ref = bound.get ();
+
+	  // setup any associated type mappings for the specified bonds and this
+	  // type
+	  auto candidates = Resolver::TypeBoundsProbe::Probe (binding);
+
+	  Resolver::AssociatedImplTrait *associated_impl_trait = nullptr;
+	  for (auto &probed_bound : candidates)
+	    {
+	      const Resolver::TraitReference *bound_trait_ref
+		= probed_bound.first;
+	      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+	      HirId impl_block_id
+		= associated_impl->get_mappings ().get_hirid ();
+	      Resolver::AssociatedImplTrait *associated = nullptr;
+	      bool found_impl_trait
+		= context->lookup_associated_trait_impl (impl_block_id,
+							 &associated);
+	      if (found_impl_trait)
+		{
+		  bool found_trait
+		    = specified_bound_ref->is_equal (*bound_trait_ref);
+		  bool found_self
+		    = associated->get_self ()->can_eq (binding, false);
+		  if (found_trait && found_self)
+		    {
+		      associated_impl_trait = associated;
+		      break;
+		    }
+		}
+	    }
+
+	  if (associated_impl_trait != nullptr)
+	    {
+	      associated_impl_trait->setup_associated_types (binding, bound);
+	    }
+	}
+    }
+
+  return true;
+}
+
+void
+ADTType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ADTType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ADTType::as_string () const
+{
+  std::string variants_buffer;
+  for (size_t i = 0; i < number_of_variants (); ++i)
+    {
+      TyTy::VariantDef *variant = variants.at (i);
+      variants_buffer += variant->as_string ();
+      if ((i + 1) < number_of_variants ())
+	variants_buffer += ", ";
+    }
+
+  return identifier + subst_as_string () + "{" + variants_buffer + "}";
+}
+
+BaseType *
+ADTType::unify (BaseType *other)
+{
+  ADTRules r (this);
+  return r.unify (other);
+}
+
+bool
+ADTType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ADTCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ADTType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ADTType &> (other);
+  if (get_adt_kind () != other2.get_adt_kind ())
+    return false;
+
+  if (number_of_variants () != other2.number_of_variants ())
+    return false;
+
+  if (has_subsititions_defined () != other2.has_subsititions_defined ())
+    return false;
+
+  if (has_subsititions_defined ())
+    {
+      if (get_num_substitutions () != other2.get_num_substitutions ())
+	return false;
+
+      for (size_t i = 0; i < get_num_substitutions (); i++)
+	{
+	  const SubstitutionParamMapping &a = substitutions.at (i);
+	  const SubstitutionParamMapping &b = other2.substitutions.at (i);
+
+	  const ParamType *aa = a.get_param_ty ();
+	  const ParamType *bb = b.get_param_ty ();
+	  BaseType *aaa = aa->resolve ();
+	  BaseType *bbb = bb->resolve ();
+	  if (!aaa->is_equal (*bbb))
+	    return false;
+	}
+    }
+
+  for (size_t i = 0; i < number_of_variants (); i++)
+    {
+      const TyTy::VariantDef *a = get_variants ().at (i);
+      const TyTy::VariantDef *b = other2.get_variants ().at (i);
+
+      if (!a->is_equal (*b))
+	return false;
+    }
+
+  return true;
+}
+
+BaseType *
+ADTType::clone () const
+{
+  std::vector<VariantDef *> cloned_variants;
+  for (auto &variant : variants)
+    cloned_variants.push_back (variant->clone ());
+
+  return new ADTType (get_ref (), get_ty_ref (), identifier, ident,
+		      get_adt_kind (), cloned_variants, clone_substs (),
+		      get_repr_options (), used_arguments,
+		      get_combined_refs ());
+}
+
+BaseType *
+ADTType::monomorphized_clone () const
+{
+  std::vector<VariantDef *> cloned_variants;
+  for (auto &variant : variants)
+    cloned_variants.push_back (variant->monomorphized_clone ());
+
+  return new ADTType (get_ref (), get_ty_ref (), identifier, ident,
+		      get_adt_kind (), cloned_variants, clone_substs (),
+		      get_repr_options (), used_arguments,
+		      get_combined_refs ());
+}
+
+static bool
+handle_substitions (SubstitutionArgumentMappings &subst_mappings,
+		    StructFieldType *field)
+{
+  auto fty = field->get_field_type ();
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      field->set_field_type (new_field);
+	    }
+	  else
+	    {
+	      field->get_field_type ()->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->has_subsititions_defined () || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return false;
+	}
+
+      auto new_field = concrete->clone ();
+      new_field->set_ref (fty->get_ref ());
+      field->set_field_type (new_field);
+    }
+
+  return true;
+}
+
+ADTType *
+ADTType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  ADTType *adt = static_cast<ADTType *> (clone ());
+  adt->set_ty_ref (mappings->get_next_hir_id ());
+  adt->used_arguments = subst_mappings;
+
+  for (auto &sub : adt->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+
+  for (auto &variant : adt->get_variants ())
+    {
+      if (variant->is_dataless_variant ())
+	continue;
+
+      for (auto &field : variant->get_fields ())
+	{
+	  bool ok = ::Rust::TyTy::handle_substitions (subst_mappings, field);
+	  if (!ok)
+	    return adt;
+	}
+    }
+
+  return adt;
+}
+
+void
+TupleType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+TupleType::as_string () const
+{
+  std::string fields_buffer;
+  for (const TyVar &field : get_fields ())
+    {
+      fields_buffer += field.get_tyty ()->as_string ();
+      fields_buffer += ", ";
+    }
+  return "(" + fields_buffer + ")";
+}
+
+BaseType *
+TupleType::get_field (size_t index) const
+{
+  return fields.at (index).get_tyty ();
+}
+
+BaseType *
+TupleType::unify (BaseType *other)
+{
+  TupleRules r (this);
+  return r.unify (other);
+}
+
+bool
+TupleType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  TupleCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+TupleType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const TupleType &> (other);
+  if (num_fields () != other2.num_fields ())
+    return false;
+
+  for (size_t i = 0; i < num_fields (); i++)
+    {
+      if (!get_field (i)->is_equal (*other2.get_field (i)))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+TupleType::clone () const
+{
+  std::vector<TyVar> cloned_fields;
+  for (const auto &f : fields)
+    cloned_fields.push_back (f.clone ());
+
+  return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus,
+			cloned_fields, get_combined_refs ());
+}
+
+BaseType *
+TupleType::monomorphized_clone () const
+{
+  std::vector<TyVar> cloned_fields;
+  for (const auto &f : fields)
+    cloned_fields.push_back (f.monomorphized_clone ());
+
+  return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus,
+			cloned_fields, get_combined_refs ());
+}
+
+TupleType *
+TupleType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  TupleType *tuple = static_cast<TupleType *> (clone ());
+  tuple->set_ref (mappings_table->get_next_hir_id ());
+  tuple->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  for (size_t i = 0; i < tuple->fields.size (); i++)
+    {
+      TyVar &field = fields.at (i);
+      if (field.get_tyty ()->contains_type_parameters ())
+	{
+	  BaseType *concrete
+	    = Resolver::SubstMapperInternal::Resolve (field.get_tyty (),
+						      mappings);
+	  tuple->fields[i]
+	    = TyVar::subst_covariant_var (field.get_tyty (), concrete);
+	}
+    }
+
+  return tuple;
+}
+
+void
+FnType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FnType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FnType::as_string () const
+{
+  std::string params_str = "";
+  for (auto &param : params)
+    {
+      auto pattern = param.first;
+      auto ty = param.second;
+      params_str += pattern->as_string () + " " + ty->as_string ();
+      params_str += ",";
+    }
+
+  std::string ret_str = type->as_string ();
+  return "fn" + subst_as_string () + " (" + params_str + ") -> " + ret_str;
+}
+
+BaseType *
+FnType::unify (BaseType *other)
+{
+  FnRules r (this);
+  return r.unify (other);
+}
+
+bool
+FnType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FnCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+FnType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const FnType &> (other);
+  if (get_identifier ().compare (other2.get_identifier ()) != 0)
+    return false;
+
+  if (!get_return_type ()->is_equal (*other2.get_return_type ()))
+    return false;
+
+  if (has_subsititions_defined () != other2.has_subsititions_defined ())
+    return false;
+
+  if (has_subsititions_defined ())
+    {
+      if (get_num_substitutions () != other2.get_num_substitutions ())
+	return false;
+
+      const FnType &ofn = static_cast<const FnType &> (other);
+      for (size_t i = 0; i < get_num_substitutions (); i++)
+	{
+	  const SubstitutionParamMapping &a = get_substs ().at (i);
+	  const SubstitutionParamMapping &b = ofn.get_substs ().at (i);
+
+	  const ParamType *pa = a.get_param_ty ();
+	  const ParamType *pb = b.get_param_ty ();
+
+	  if (!pa->is_equal (*pb))
+	    return false;
+	}
+    }
+
+  if (num_params () != other2.num_params ())
+    return false;
+
+  for (size_t i = 0; i < num_params (); i++)
+    {
+      auto lhs = param_at (i).second;
+      auto rhs = other2.param_at (i).second;
+      if (!lhs->is_equal (*rhs))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+FnType::clone () const
+{
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back ({p.first, p.second->clone ()});
+
+  return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (),
+		     ident, flags, abi, std::move (cloned_params),
+		     get_return_type ()->clone (), clone_substs (),
+		     get_combined_refs ());
+}
+
+BaseType *
+FnType::monomorphized_clone () const
+{
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back ({p.first, p.second->monomorphized_clone ()});
+
+  return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (),
+		     ident, flags, abi, std::move (cloned_params),
+		     get_return_type ()->clone (), clone_substs (),
+		     get_combined_refs ());
+}
+
+FnType *
+FnType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  FnType *fn = static_cast<FnType *> (clone ());
+  fn->set_ty_ref (mappings->get_next_hir_id ());
+  fn->used_arguments = subst_mappings;
+
+  for (auto &sub : fn->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	{
+	  sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+	}
+    }
+
+  auto fty = fn->get_return_type ();
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      fn->type = new_field;
+	    }
+	  else
+	    {
+	      fty->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->needs_generic_substitutions ()
+	   || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return nullptr;
+	}
+
+      auto new_field = concrete->clone ();
+      new_field->set_ref (fty->get_ref ());
+      fn->type = new_field;
+    }
+
+  for (auto &param : fn->get_params ())
+    {
+      auto fty = param.second;
+
+      bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+      if (is_param_ty)
+	{
+	  ParamType *p = static_cast<ParamType *> (fty);
+
+	  SubstitutionArg arg = SubstitutionArg::error ();
+	  bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+	  if (ok)
+	    {
+	      auto argt = arg.get_tyty ();
+	      bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	      bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	      if (arg_is_param || arg_is_concrete)
+		{
+		  auto new_field = argt->clone ();
+		  new_field->set_ref (fty->get_ref ());
+		  param.second = new_field;
+		}
+	      else
+		{
+		  fty->set_ty_ref (argt->get_ref ());
+		}
+	    }
+	}
+      else if (fty->has_subsititions_defined ()
+	       || fty->contains_type_parameters ())
+	{
+	  BaseType *concrete
+	    = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+	  if (concrete == nullptr
+	      || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	    {
+	      rust_error_at (subst_mappings.get_locus (),
+			     "Failed to resolve field substitution type: %s",
+			     fty->as_string ().c_str ());
+	      return nullptr;
+	    }
+
+	  auto new_field = concrete->clone ();
+	  new_field->set_ref (fty->get_ref ());
+	  param.second = new_field;
+	}
+    }
+
+  return fn;
+}
+
+void
+FnPtr::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FnPtr::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FnPtr::as_string () const
+{
+  std::string params_str;
+
+  auto &params = get_params ();
+  for (auto &p : params)
+    {
+      params_str += p.get_tyty ()->as_string () + " ,";
+    }
+
+  return "fnptr (" + params_str + ") -> " + get_return_type ()->as_string ();
+}
+
+BaseType *
+FnPtr::unify (BaseType *other)
+{
+  FnptrRules r (this);
+  return r.unify (other);
+}
+
+bool
+FnPtr::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FnptrCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+FnPtr::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const FnPtr &> (other);
+  auto this_ret_type = get_return_type ();
+  auto other_ret_type = other2.get_return_type ();
+  if (this_ret_type->is_equal (*other_ret_type))
+    return false;
+
+  if (num_params () != other2.num_params ())
+    return false;
+
+  for (size_t i = 0; i < num_params (); i++)
+    {
+      if (!param_at (i)->is_equal (*other2.param_at (i)))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+FnPtr::clone () const
+{
+  std::vector<TyVar> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back (TyVar (p.get_ref ()));
+
+  return new FnPtr (get_ref (), get_ty_ref (), ident.locus,
+		    std::move (cloned_params), result_type,
+		    get_combined_refs ());
+}
+
+BaseType *
+FnPtr::monomorphized_clone () const
+{
+  std::vector<TyVar> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back (p.monomorphized_clone ());
+
+  return new FnPtr (get_ref (), get_ty_ref (), ident.locus,
+		    std::move (cloned_params), result_type,
+		    get_combined_refs ());
+}
+
+void
+ClosureType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ClosureType::as_string () const
+{
+  return "TODO";
+}
+
+BaseType *
+ClosureType::unify (BaseType *other)
+{
+  ClosureRules r (this);
+  return r.unify (other);
+}
+
+bool
+ClosureType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ClosureCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ClosureType::is_equal (const BaseType &other) const
+{
+  gcc_unreachable ();
+  return false;
+}
+
+BaseType *
+ClosureType::clone () const
+{
+  return new ClosureType (get_ref (), get_ty_ref (), ident, id, parameter_types,
+			  result_type, clone_substs (), get_combined_refs ());
+}
+
+BaseType *
+ClosureType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+ClosureType *
+ClosureType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  gcc_unreachable ();
+  return nullptr;
+}
+
+void
+ArrayType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ArrayType::as_string () const
+{
+  return "[" + get_element_type ()->as_string () + ":" + "CAPACITY" + "]";
+}
+
+BaseType *
+ArrayType::unify (BaseType *other)
+{
+  ArrayRules r (this);
+  return r.unify (other);
+}
+
+bool
+ArrayType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ArrayCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ArrayType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ArrayType &> (other);
+
+  auto this_element_type = get_element_type ();
+  auto other_element_type = other2.get_element_type ();
+
+  return this_element_type->is_equal (*other_element_type);
+}
+
+BaseType *
+ArrayType::get_element_type () const
+{
+  return element_type.get_tyty ();
+}
+
+BaseType *
+ArrayType::clone () const
+{
+  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr,
+			element_type, get_combined_refs ());
+}
+
+BaseType *
+ArrayType::monomorphized_clone () const
+{
+  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr,
+			element_type.monomorphized_clone (),
+			get_combined_refs ());
+}
+
+ArrayType *
+ArrayType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  ArrayType *ref = static_cast<ArrayType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_element_type ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->element_type = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+SliceType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+SliceType::as_string () const
+{
+  return "[" + get_element_type ()->as_string () + "]";
+}
+
+BaseType *
+SliceType::unify (BaseType *other)
+{
+  SliceRules r (this);
+  return r.unify (other);
+}
+
+bool
+SliceType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  SliceCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+SliceType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const SliceType &> (other);
+
+  auto this_element_type = get_element_type ();
+  auto other_element_type = other2.get_element_type ();
+
+  return this_element_type->is_equal (*other_element_type);
+}
+
+BaseType *
+SliceType::get_element_type () const
+{
+  return element_type.get_tyty ();
+}
+
+BaseType *
+SliceType::clone () const
+{
+  return new SliceType (get_ref (), get_ty_ref (), ident.locus,
+			element_type.clone (), get_combined_refs ());
+}
+
+BaseType *
+SliceType::monomorphized_clone () const
+{
+  return new SliceType (get_ref (), get_ty_ref (), ident.locus,
+			element_type.monomorphized_clone (),
+			get_combined_refs ());
+}
+
+SliceType *
+SliceType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  SliceType *ref = static_cast<SliceType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_element_type ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->element_type = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+BoolType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BoolType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+BoolType::as_string () const
+{
+  return "bool";
+}
+
+BaseType *
+BoolType::unify (BaseType *other)
+{
+  BoolRules r (this);
+  return r.unify (other);
+}
+
+bool
+BoolType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  BoolCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+BoolType::clone () const
+{
+  return new BoolType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+BoolType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+IntType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IntType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+IntType::as_string () const
+{
+  switch (int_kind)
+    {
+    case I8:
+      return "i8";
+    case I16:
+      return "i16";
+    case I32:
+      return "i32";
+    case I64:
+      return "i64";
+    case I128:
+      return "i128";
+    }
+  gcc_unreachable ();
+  return "__unknown_int_type";
+}
+
+BaseType *
+IntType::unify (BaseType *other)
+{
+  IntRules r (this);
+  return r.unify (other);
+}
+
+bool
+IntType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  IntCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+IntType::clone () const
+{
+  return new IntType (get_ref (), get_ty_ref (), get_int_kind (),
+		      get_combined_refs ());
+}
+
+BaseType *
+IntType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+IntType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const IntType &o = static_cast<const IntType &> (other);
+  return get_int_kind () == o.get_int_kind ();
+}
+
+void
+UintType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UintType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+UintType::as_string () const
+{
+  switch (uint_kind)
+    {
+    case U8:
+      return "u8";
+    case U16:
+      return "u16";
+    case U32:
+      return "u32";
+    case U64:
+      return "u64";
+    case U128:
+      return "u128";
+    }
+  gcc_unreachable ();
+  return "__unknown_uint_type";
+}
+
+BaseType *
+UintType::unify (BaseType *other)
+{
+  UintRules r (this);
+  return r.unify (other);
+}
+
+bool
+UintType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  UintCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+UintType::clone () const
+{
+  return new UintType (get_ref (), get_ty_ref (), get_uint_kind (),
+		       get_combined_refs ());
+}
+
+BaseType *
+UintType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+UintType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const UintType &o = static_cast<const UintType &> (other);
+  return get_uint_kind () == o.get_uint_kind ();
+}
+
+void
+FloatType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FloatType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FloatType::as_string () const
+{
+  switch (float_kind)
+    {
+    case F32:
+      return "f32";
+    case F64:
+      return "f64";
+    }
+  gcc_unreachable ();
+  return "__unknown_float_type";
+}
+
+BaseType *
+FloatType::unify (BaseType *other)
+{
+  FloatRules r (this);
+  return r.unify (other);
+}
+
+bool
+FloatType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FloatCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+FloatType::clone () const
+{
+  return new FloatType (get_ref (), get_ty_ref (), get_float_kind (),
+			get_combined_refs ());
+}
+
+BaseType *
+FloatType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+FloatType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const FloatType &o = static_cast<const FloatType &> (other);
+  return get_float_kind () == o.get_float_kind ();
+}
+
+void
+USizeType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+USizeType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+USizeType::as_string () const
+{
+  return "usize";
+}
+
+BaseType *
+USizeType::unify (BaseType *other)
+{
+  USizeRules r (this);
+  return r.unify (other);
+}
+
+bool
+USizeType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  USizeCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+USizeType::clone () const
+{
+  return new USizeType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+USizeType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+ISizeType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ISizeType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ISizeType::as_string () const
+{
+  return "isize";
+}
+
+BaseType *
+ISizeType::unify (BaseType *other)
+{
+  ISizeRules r (this);
+  return r.unify (other);
+}
+
+bool
+ISizeType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ISizeCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+ISizeType::clone () const
+{
+  return new ISizeType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+ISizeType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+CharType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CharType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+CharType::as_string () const
+{
+  return "char";
+}
+
+BaseType *
+CharType::unify (BaseType *other)
+{
+  CharRules r (this);
+  return r.unify (other);
+}
+
+bool
+CharType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  CharCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+CharType::clone () const
+{
+  return new CharType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+CharType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+ReferenceType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ReferenceType::as_string () const
+{
+  return std::string ("&") + (is_mutable () ? "mut" : "") + " "
+	 + get_base ()->as_string ();
+}
+
+BaseType *
+ReferenceType::unify (BaseType *other)
+{
+  ReferenceRules r (this);
+  return r.unify (other);
+}
+
+bool
+ReferenceType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ReferenceCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ReferenceType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ReferenceType &> (other);
+  if (mutability () != other2.mutability ())
+    return false;
+
+  return get_base ()->is_equal (*other2.get_base ());
+}
+
+BaseType *
+ReferenceType::get_base () const
+{
+  return base.get_tyty ();
+}
+
+BaseType *
+ReferenceType::clone () const
+{
+  return new ReferenceType (get_ref (), get_ty_ref (), base, mutability (),
+			    get_combined_refs ());
+}
+
+BaseType *
+ReferenceType::monomorphized_clone () const
+{
+  return new ReferenceType (get_ref (), get_ty_ref (),
+			    base.monomorphized_clone (), mutability (),
+			    get_combined_refs ());
+}
+
+ReferenceType *
+ReferenceType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  ReferenceType *ref = static_cast<ReferenceType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_base ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->base = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+PointerType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PointerType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+PointerType::as_string () const
+{
+  return std::string ("* ") + (is_mutable () ? "mut" : "const") + " "
+	 + get_base ()->as_string ();
+}
+
+BaseType *
+PointerType::unify (BaseType *other)
+{
+  PointerRules r (this);
+  return r.unify (other);
+}
+
+bool
+PointerType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  PointerCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+PointerType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const PointerType &> (other);
+  if (mutability () != other2.mutability ())
+    return false;
+
+  return get_base ()->is_equal (*other2.get_base ());
+}
+
+BaseType *
+PointerType::get_base () const
+{
+  return base.get_tyty ();
+}
+
+BaseType *
+PointerType::clone () const
+{
+  return new PointerType (get_ref (), get_ty_ref (), base, mutability (),
+			  get_combined_refs ());
+}
+
+BaseType *
+PointerType::monomorphized_clone () const
+{
+  return new PointerType (get_ref (), get_ty_ref (),
+			  base.monomorphized_clone (), mutability (),
+			  get_combined_refs ());
+}
+
+PointerType *
+PointerType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  PointerType *ref = static_cast<PointerType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_base ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->base = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+ParamType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParamType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ParamType::as_string () const
+{
+  if (!can_resolve ())
+    {
+      return get_symbol () + " REF: " + std::to_string (get_ref ());
+    }
+
+  BaseType *lookup = resolve ();
+  return get_symbol () + "=" + lookup->as_string ();
+}
+
+std::string
+ParamType::get_name () const
+{
+  if (!can_resolve ())
+    return get_symbol ();
+
+  return resolve ()->get_name ();
+}
+
+BaseType *
+ParamType::unify (BaseType *other)
+{
+  ParamRules r (this);
+  return r.unify (other);
+}
+
+bool
+ParamType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ParamCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+ParamType::clone () const
+{
+  return new ParamType (get_symbol (), ident.locus, get_ref (), get_ty_ref (),
+			param, get_specified_bounds (), get_combined_refs ());
+}
+
+BaseType *
+ParamType::monomorphized_clone () const
+{
+  return resolve ()->clone ();
+}
+
+std::string
+ParamType::get_symbol () const
+{
+  return symbol;
+}
+
+BaseType *
+ParamType::resolve () const
+{
+  TyVar var (get_ty_ref ());
+  BaseType *r = var.get_tyty ();
+
+  while (r->get_kind () == TypeKind::PARAM)
+    {
+      ParamType *rr = static_cast<ParamType *> (r);
+      if (!rr->can_resolve ())
+	break;
+
+      TyVar v (rr->get_ty_ref ());
+      r = v.get_tyty ();
+    }
+
+  if (r->get_kind () == TypeKind::PARAM && (r->get_ref () == r->get_ty_ref ()))
+    return TyVar (r->get_ty_ref ()).get_tyty ();
+
+  return r;
+}
+
+bool
+ParamType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    {
+      if (!can_resolve ())
+	return false;
+
+      return resolve ()->is_equal (other);
+    }
+
+  auto other2 = static_cast<const ParamType &> (other);
+  if (can_resolve () != other2.can_resolve ())
+    return false;
+
+  if (can_resolve ())
+    return resolve ()->can_eq (other2.resolve (), false);
+
+  return get_symbol ().compare (other2.get_symbol ()) == 0;
+}
+
+ParamType *
+ParamType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  SubstitutionArg arg = SubstitutionArg::error ();
+  bool ok = subst_mappings.get_argument_for_symbol (this, &arg);
+  if (!ok || arg.is_error ())
+    return this;
+
+  ParamType *p = static_cast<ParamType *> (clone ());
+  subst_mappings.on_param_subst (*p, arg);
+
+  // there are two cases one where we substitute directly to a new PARAM and
+  // otherwise
+  if (arg.get_tyty ()->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      p->set_ty_ref (arg.get_tyty ()->get_ref ());
+      return p;
+    }
+
+  // this is the new subst that this needs to pass
+  p->set_ref (mappings->get_next_hir_id ());
+  p->set_ty_ref (arg.get_tyty ()->get_ref ());
+
+  return p;
+}
+
+BaseType *
+StrType::clone () const
+{
+  return new StrType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+StrType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+StrType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StrType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+StrType::as_string () const
+{
+  return "str";
+}
+
+BaseType *
+StrType::unify (BaseType *other)
+{
+  StrRules r (this);
+  return r.unify (other);
+}
+
+bool
+StrType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  StrCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+StrType::is_equal (const BaseType &other) const
+{
+  return get_kind () == other.get_kind ();
+}
+
+void
+NeverType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+NeverType::as_string () const
+{
+  return "!";
+}
+
+BaseType *
+NeverType::unify (BaseType *other)
+{
+  NeverRules r (this);
+  return r.unify (other);
+}
+
+bool
+NeverType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  NeverCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+NeverType::clone () const
+{
+  return new NeverType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+NeverType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+// placeholder type
+
+void
+PlaceholderType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PlaceholderType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+PlaceholderType::as_string () const
+{
+  return "<placeholder:" + (can_resolve () ? resolve ()->as_string () : "")
+	 + ">";
+}
+
+BaseType *
+PlaceholderType::unify (BaseType *other)
+{
+  PlaceholderRules r (this);
+  return r.unify (other);
+}
+
+bool
+PlaceholderType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  PlaceholderCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+PlaceholderType::clone () const
+{
+  return new PlaceholderType (get_symbol (), get_ref (), get_ty_ref (),
+			      get_combined_refs ());
+}
+
+BaseType *
+PlaceholderType::monomorphized_clone () const
+{
+  if (can_resolve ())
+    return resolve ()->monomorphized_clone ();
+
+  return clone ();
+}
+
+void
+PlaceholderType::set_associated_type (HirId ref)
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  context->insert_associated_type_mapping (get_ty_ref (), ref);
+}
+
+void
+PlaceholderType::clear_associated_type ()
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  context->clear_associated_type_mapping (get_ty_ref ());
+}
+
+bool
+PlaceholderType::can_resolve () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  return context->lookup_associated_type_mapping (get_ty_ref (), nullptr);
+}
+
+BaseType *
+PlaceholderType::resolve () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  HirId mapping;
+  bool ok = context->lookup_associated_type_mapping (get_ty_ref (), &mapping);
+  rust_assert (ok);
+
+  return TyVar (mapping).get_tyty ();
+}
+
+bool
+PlaceholderType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    {
+      if (!can_resolve ())
+	return false;
+
+      return resolve ()->is_equal (other);
+    }
+
+  auto other2 = static_cast<const PlaceholderType &> (other);
+  return get_symbol ().compare (other2.get_symbol ()) == 0;
+}
+
+// Projection type
+
+void
+ProjectionType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ProjectionType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ProjectionType::as_string () const
+{
+  return "<Projection=" + subst_as_string () + "::" + base->as_string () + ">";
+}
+
+BaseType *
+ProjectionType::unify (BaseType *other)
+{
+  return base->unify (other);
+}
+
+bool
+ProjectionType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  return base->can_eq (other, emit_errors);
+}
+
+BaseType *
+ProjectionType::clone () const
+{
+  return new ProjectionType (get_ref (), get_ty_ref (), base->clone (), trait,
+			     item, clone_substs (), used_arguments,
+			     get_combined_refs ());
+}
+
+BaseType *
+ProjectionType::monomorphized_clone () const
+{
+  return get ()->monomorphized_clone ();
+}
+
+ProjectionType *
+ProjectionType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  // // do we really need to substitute this?
+  // if (base->needs_generic_substitutions () || base->contains_type_parameters
+  // ())
+  //   {
+  //     return this;
+  //   }
+
+  ProjectionType *projection = static_cast<ProjectionType *> (clone ());
+  projection->set_ty_ref (mappings->get_next_hir_id ());
+  projection->used_arguments = subst_mappings;
+
+  auto context = Resolver::TypeCheckContext::get ();
+  context->insert_implicit_type (projection->get_ty_ref (), projection);
+
+  for (auto &sub : projection->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+
+  auto fty = projection->base;
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      projection->base = new_field;
+	    }
+	  else
+	    {
+	      fty->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->needs_generic_substitutions ()
+	   || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return nullptr;
+	}
+
+      projection->base = concrete;
+    }
+
+  return projection;
+}
+
+void
+DynamicObjectType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DynamicObjectType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+DynamicObjectType::as_string () const
+{
+  return "dyn [" + raw_bounds_as_string () + "]";
+}
+
+BaseType *
+DynamicObjectType::unify (BaseType *other)
+{
+  DynamicRules r (this);
+  return r.unify (other);
+}
+
+bool
+DynamicObjectType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  DynamicCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+DynamicObjectType::clone () const
+{
+  return new DynamicObjectType (get_ref (), get_ty_ref (), ident,
+				specified_bounds, get_combined_refs ());
+}
+
+BaseType *
+DynamicObjectType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+std::string
+DynamicObjectType::get_name () const
+{
+  return "dyn [" + raw_bounds_as_name () + "]";
+}
+
+bool
+DynamicObjectType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  if (num_specified_bounds () != other.num_specified_bounds ())
+    return false;
+
+  return bounds_compatible (other, Location (), false);
+}
+
+const std::vector<
+  std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+DynamicObjectType::get_object_items () const
+{
+  std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+    items;
+  for (auto &bound : get_specified_bounds ())
+    {
+      const Resolver::TraitReference *trait = bound.get ();
+      for (auto &item : trait->get_trait_items ())
+	{
+	  if (item.get_trait_item_type ()
+		== Resolver::TraitItemReference::TraitItemType::FN
+	      && item.is_object_safe ())
+	    items.push_back ({&item, &bound});
+	}
+
+      for (auto &super_trait : trait->get_super_traits ())
+	{
+	  for (auto &item : super_trait->get_trait_items ())
+	    {
+	      if (item.get_trait_item_type ()
+		    == Resolver::TraitItemReference::TraitItemType::FN
+		  && item.is_object_safe ())
+		items.push_back ({&item, &bound});
+	    }
+	}
+    }
+  return items;
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
new file mode 100644
index 00000000000..c47921d44d7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -0,0 +1,2533 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_TYTY
+#define RUST_TYTY
+
+#include "rust-hir-map.h"
+#include "rust-hir-full.h"
+#include "rust-diagnostics.h"
+#include "rust-abi.h"
+#include "rust-common.h"
+#include "rust-identifier.h"
+
+namespace Rust {
+
+namespace Resolver {
+class TraitReference;
+class TraitItemReference;
+class AssociatedImplTrait;
+} // namespace Resolver
+
+namespace TyTy {
+
+// https://rustc-dev-guide.rust-lang.org/type-inference.html#inference-variables
+// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variants
+enum TypeKind
+{
+  INFER,
+  ADT,
+  STR,
+  REF,
+  POINTER,
+  PARAM,
+  ARRAY,
+  SLICE,
+  FNDEF,
+  FNPTR,
+  TUPLE,
+  BOOL,
+  CHAR,
+  INT,
+  UINT,
+  FLOAT,
+  USIZE,
+  ISIZE,
+  NEVER,
+  PLACEHOLDER,
+  PROJECTION,
+  DYNAMIC,
+  CLOSURE,
+  // there are more to add...
+  ERROR
+};
+
+extern bool
+is_primitive_type_kind (TypeKind kind);
+
+class TypeKindFormat
+{
+public:
+  static std::string to_string (TypeKind kind);
+};
+
+class BaseType;
+class TypeBoundPredicate;
+class TypeBoundPredicateItem
+{
+public:
+  TypeBoundPredicateItem (const TypeBoundPredicate *parent,
+			  const Resolver::TraitItemReference *trait_item_ref)
+    : parent (parent), trait_item_ref (trait_item_ref)
+  {}
+
+  static TypeBoundPredicateItem error ()
+  {
+    return TypeBoundPredicateItem (nullptr, nullptr);
+  }
+
+  bool is_error () const
+  {
+    return parent == nullptr || trait_item_ref == nullptr;
+  }
+
+  BaseType *get_tyty_for_receiver (const TyTy::BaseType *receiver);
+
+  const Resolver::TraitItemReference *get_raw_item () const;
+
+  bool needs_implementation () const;
+
+  const TypeBoundPredicate *get_parent () const { return parent; }
+
+  Location get_locus () const;
+
+private:
+  const TypeBoundPredicate *parent;
+  const Resolver::TraitItemReference *trait_item_ref;
+};
+
+class TypeBoundsMappings
+{
+protected:
+  TypeBoundsMappings (std::vector<TypeBoundPredicate> specified_bounds);
+
+public:
+  std::vector<TypeBoundPredicate> &get_specified_bounds ();
+
+  const std::vector<TypeBoundPredicate> &get_specified_bounds () const;
+
+  size_t num_specified_bounds () const;
+
+  std::string raw_bounds_as_string () const;
+
+  std::string bounds_as_string () const;
+
+  std::string raw_bounds_as_name () const;
+
+protected:
+  void add_bound (TypeBoundPredicate predicate);
+
+  std::vector<TypeBoundPredicate> specified_bounds;
+};
+
+class TyVisitor;
+class TyConstVisitor;
+class BaseType : public TypeBoundsMappings
+{
+public:
+  virtual ~BaseType () {}
+
+  HirId get_ref () const { return ref; }
+
+  void set_ref (HirId id)
+  {
+    if (id != ref)
+      append_reference (ref);
+    ref = id;
+  }
+
+  HirId get_ty_ref () const { return ty_ref; }
+
+  void set_ty_ref (HirId id) { ty_ref = id; }
+
+  virtual void accept_vis (TyVisitor &vis) = 0;
+
+  virtual void accept_vis (TyConstVisitor &vis) const = 0;
+
+  virtual std::string as_string () const = 0;
+
+  virtual std::string get_name () const = 0;
+
+  // Unify two types. Returns a pointer to the newly-created unified ty, or
+  // nullptr if the two ty cannot be unified. The caller is responsible for
+  // releasing the memory of the returned ty.
+  virtual BaseType *unify (BaseType *other) = 0;
+
+  // similar to unify but does not actually perform type unification but
+  // determines whether they are compatible. Consider the following
+  //
+  // fn foo<T>() -> T { ... }
+  // fn foo() -> i32 { ... }
+  //
+  // when the function has been substituted they can be considered equal.
+  //
+  // It can also be used to optional emit errors for trait item compatibility
+  // checks
+  virtual bool can_eq (const BaseType *other, bool emit_errors) const = 0;
+
+  // Check value equality between two ty. Type inference rules are ignored. Two
+  //   ty are considered equal if they're of the same kind, and
+  //     1. (For ADTs, arrays, tuples, refs) have the same underlying ty
+  //     2. (For functions) have the same signature
+  virtual bool is_equal (const BaseType &other) const
+  {
+    return get_kind () == other.get_kind ();
+  }
+
+  bool satisfies_bound (const TypeBoundPredicate &predicate) const;
+
+  bool bounds_compatible (const BaseType &other, Location locus,
+			  bool emit_error) const;
+
+  void inherit_bounds (const BaseType &other);
+
+  void inherit_bounds (
+    const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);
+
+  virtual bool is_unit () const { return false; }
+
+  virtual bool is_concrete () const = 0;
+
+  TypeKind get_kind () const { return kind; }
+
+  /* Returns a pointer to a clone of this. The caller is responsible for
+   * releasing the memory of the returned ty. */
+  virtual BaseType *clone () const = 0;
+
+  // TODO
+  virtual BaseType *monomorphized_clone () const = 0;
+
+  // get_combined_refs returns the chain of node refs involved in unification
+  std::set<HirId> get_combined_refs () const { return combined; }
+
+  void append_reference (HirId id) { combined.insert (id); }
+
+  virtual bool supports_substitutions () const { return false; }
+
+  virtual bool has_subsititions_defined () const { return false; }
+
+  virtual bool can_substitute () const
+  {
+    return supports_substitutions () && has_subsititions_defined ();
+  }
+
+  virtual bool needs_generic_substitutions () const { return false; }
+
+  bool contains_type_parameters () const { return !is_concrete (); }
+
+  std::string mappings_str () const
+  {
+    std::string buffer = "Ref: " + std::to_string (get_ref ())
+			 + " TyRef: " + std::to_string (get_ty_ref ());
+    buffer += "[";
+    for (auto &ref : combined)
+      buffer += std::to_string (ref) + ",";
+    buffer += "]";
+    return "(" + buffer + ")";
+  }
+
+  std::string debug_str () const
+  {
+    return TypeKindFormat::to_string (get_kind ()) + ":" + as_string () + ":"
+	   + mappings_str () + ":" + bounds_as_string ();
+  }
+
+  void debug () const
+  {
+    rust_debug ("[%p] %s", static_cast<const void *> (this),
+		debug_str ().c_str ());
+  }
+
+  // FIXME this will eventually go away
+  const BaseType *get_root () const;
+
+  // This will get the monomorphized type from Params, Placeholders or
+  // Projections if available or error
+  const BaseType *destructure () const;
+
+  const RustIdent &get_ident () const { return ident; }
+
+  Location get_locus () const { return ident.locus; }
+
+protected:
+  BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : TypeBoundsMappings ({}), kind (kind), ref (ref), ty_ref (ty_ref),
+      combined (refs), ident (ident), mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident,
+	    std::vector<TypeBoundPredicate> specified_bounds,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : TypeBoundsMappings (specified_bounds), kind (kind), ref (ref),
+      ty_ref (ty_ref), combined (refs), ident (ident),
+      mappings (Analysis::Mappings::get ())
+  {}
+
+  TypeKind kind;
+  HirId ref;
+  HirId ty_ref;
+  std::set<HirId> combined;
+  RustIdent ident;
+
+  Analysis::Mappings *mappings;
+};
+
+// this is a placeholder for types that can change like inference variables
+class TyVar
+{
+public:
+  explicit TyVar (HirId ref);
+
+  HirId get_ref () const { return ref; }
+
+  BaseType *get_tyty () const;
+
+  TyVar clone () const;
+
+  TyVar monomorphized_clone () const;
+
+  static TyVar get_implicit_infer_var (Location locus);
+
+  static TyVar subst_covariant_var (TyTy::BaseType *orig,
+				    TyTy::BaseType *subst);
+
+private:
+  HirId ref;
+};
+
+class TyWithLocation
+{
+public:
+  TyWithLocation (BaseType *ty, Location locus);
+  TyWithLocation (BaseType *ty);
+
+  BaseType *get_ty () const { return ty; }
+  Location get_locus () const { return locus; }
+
+private:
+  BaseType *ty;
+  Location locus;
+};
+
+class InferType : public BaseType
+{
+public:
+  enum InferTypeKind
+  {
+    GENERAL,
+    INTEGRAL,
+    FLOAT
+  };
+
+  InferType (HirId ref, InferTypeKind infer_kind, Location locus,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::INFER,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      infer_kind (infer_kind)
+  {}
+
+  InferType (HirId ref, HirId ty_ref, InferTypeKind infer_kind, Location locus,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::INFER,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      infer_kind (infer_kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  InferTypeKind get_infer_kind () const { return infer_kind; }
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool default_type (BaseType **type) const;
+
+  bool is_concrete () const final override { return true; }
+
+private:
+  InferTypeKind infer_kind;
+};
+
+class ErrorType : public BaseType
+{
+public:
+  ErrorType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ERROR,
+		{Resolver::CanonicalPath::create_empty (), Location ()}, refs)
+  {}
+
+  ErrorType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ERROR,
+		{Resolver::CanonicalPath::create_empty (), Location ()}, refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  bool is_unit () const override { return true; }
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_concrete () const final override { return false; }
+};
+
+class SubstitutionArgumentMappings;
+class ParamType : public BaseType
+{
+public:
+  ParamType (std::string symbol, Location locus, HirId ref,
+	     HIR::GenericParam &param,
+	     std::vector<TypeBoundPredicate> specified_bounds,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PARAM,
+		{Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
+		 locus},
+		specified_bounds, refs),
+      symbol (symbol), param (param)
+  {}
+
+  ParamType (std::string symbol, Location locus, HirId ref, HirId ty_ref,
+	     HIR::GenericParam &param,
+	     std::vector<TypeBoundPredicate> specified_bounds,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PARAM,
+		{Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
+		 locus},
+		specified_bounds, refs),
+      symbol (symbol), param (param)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_symbol () const;
+
+  HIR::GenericParam &get_generic_param () { return param; }
+
+  bool can_resolve () const { return get_ref () != get_ty_ref (); }
+
+  BaseType *resolve () const;
+
+  std::string get_name () const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  bool is_concrete () const override final
+  {
+    auto r = resolve ();
+    if (r == this)
+      return false;
+
+    return r->is_concrete ();
+  }
+
+  ParamType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  std::string symbol;
+  HIR::GenericParam &param;
+};
+
+class StructFieldType
+{
+public:
+  StructFieldType (HirId ref, std::string name, BaseType *ty)
+    : ref (ref), name (name), ty (ty)
+  {}
+
+  HirId get_ref () const { return ref; }
+
+  std::string as_string () const;
+
+  bool is_equal (const StructFieldType &other) const;
+
+  std::string get_name () const { return name; }
+
+  BaseType *get_field_type () const { return ty; }
+
+  void set_field_type (BaseType *fty) { ty = fty; }
+
+  StructFieldType *clone () const;
+
+  StructFieldType *monomorphized_clone () const;
+
+  bool is_concrete () const { return ty->is_concrete (); }
+
+  void debug () const { rust_debug ("%s", as_string ().c_str ()); }
+
+private:
+  HirId ref;
+  std::string name;
+  BaseType *ty;
+};
+
+class TupleType : public BaseType
+{
+public:
+  TupleType (HirId ref, Location locus,
+	     std::vector<TyVar> fields = std::vector<TyVar> (),
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::TUPLE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      fields (fields)
+  {}
+
+  TupleType (HirId ref, HirId ty_ref, Location locus,
+	     std::vector<TyVar> fields = std::vector<TyVar> (),
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::TUPLE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      fields (fields)
+  {}
+
+  static TupleType *get_unit_type (HirId ref)
+  {
+    return new TupleType (ref, Linemap::predeclared_location ());
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  bool is_unit () const override { return this->fields.empty (); }
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  size_t num_fields () const { return fields.size (); }
+
+  BaseType *get_field (size_t index) const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    for (size_t i = 0; i < num_fields (); i++)
+      {
+	if (!get_field (i)->is_concrete ())
+	  return false;
+      }
+    return true;
+  }
+
+  const std::vector<TyVar> &get_fields () const { return fields; }
+
+  std::string get_name () const override final { return as_string (); }
+
+  TupleType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  std::vector<TyVar> fields;
+};
+
+class SubstitutionParamMapping
+{
+public:
+  SubstitutionParamMapping (const HIR::TypeParam &generic, ParamType *param)
+    : generic (generic), param (param)
+  {}
+
+  SubstitutionParamMapping (const SubstitutionParamMapping &other)
+    : generic (other.generic), param (other.param)
+  {}
+
+  std::string as_string () const
+  {
+    if (param == nullptr)
+      return "nullptr";
+
+    return param->get_name ();
+  }
+
+  bool fill_param_ty (SubstitutionArgumentMappings &subst_mappings,
+		      Location locus);
+
+  SubstitutionParamMapping clone () const
+  {
+    return SubstitutionParamMapping (generic, static_cast<ParamType *> (
+						param->clone ()));
+  }
+
+  ParamType *get_param_ty () { return param; }
+
+  const ParamType *get_param_ty () const { return param; }
+
+  const HIR::TypeParam &get_generic_param () { return generic; };
+
+  // this is used for the backend to override the HirId ref of the param to
+  // what the concrete type is for the rest of the context
+  void override_context ();
+
+  bool needs_substitution () const
+  {
+    return !(get_param_ty ()->is_concrete ());
+  }
+
+  Location get_param_locus () const { return generic.get_locus (); }
+
+  bool param_has_default_ty () const { return generic.has_type (); }
+
+  BaseType *get_default_ty () const
+  {
+    TyVar var (generic.get_type_mappings ().get_hirid ());
+    return var.get_tyty ();
+  }
+
+  bool need_substitution () const;
+
+private:
+  const HIR::TypeParam &generic;
+  ParamType *param;
+};
+
+class SubstitutionArg
+{
+public:
+  SubstitutionArg (const SubstitutionParamMapping *param, BaseType *argument)
+    : param (param), argument (argument)
+  {}
+
+  // FIXME
+  // the copy constructors need removed - they are unsafe see
+  // TypeBoundPredicate
+  SubstitutionArg (const SubstitutionArg &other)
+    : param (other.param), argument (other.argument)
+  {}
+
+  SubstitutionArg &operator= (const SubstitutionArg &other)
+  {
+    param = other.param;
+    argument = other.argument;
+    return *this;
+  }
+
+  BaseType *get_tyty () { return argument; }
+
+  const BaseType *get_tyty () const { return argument; }
+
+  const SubstitutionParamMapping *get_param_mapping () const { return param; }
+
+  static SubstitutionArg error () { return SubstitutionArg (nullptr, nullptr); }
+
+  bool is_error () const { return param == nullptr || argument == nullptr; }
+
+  bool is_conrete () const
+  {
+    if (argument != nullptr)
+      return true;
+
+    if (argument->get_kind () == TyTy::TypeKind::PARAM)
+      return false;
+
+    return argument->is_concrete ();
+  }
+
+  std::string as_string () const
+  {
+    return param->as_string ()
+	   + (argument != nullptr ? ":" + argument->as_string () : "");
+  }
+
+private:
+  const SubstitutionParamMapping *param;
+  BaseType *argument;
+};
+
+typedef std::function<void (const ParamType &, const SubstitutionArg &)>
+  ParamSubstCb;
+class SubstitutionArgumentMappings
+{
+public:
+  SubstitutionArgumentMappings (std::vector<SubstitutionArg> mappings,
+				Location locus,
+				ParamSubstCb param_subst_cb = nullptr,
+				bool trait_item_flag = false)
+    : mappings (mappings), locus (locus), param_subst_cb (param_subst_cb),
+      trait_item_flag (trait_item_flag)
+  {}
+
+  SubstitutionArgumentMappings (const SubstitutionArgumentMappings &other)
+    : mappings (other.mappings), locus (other.locus),
+      param_subst_cb (other.param_subst_cb),
+      trait_item_flag (other.trait_item_flag)
+  {}
+
+  SubstitutionArgumentMappings &
+  operator= (const SubstitutionArgumentMappings &other)
+  {
+    mappings = other.mappings;
+    locus = other.locus;
+    param_subst_cb = other.param_subst_cb;
+    trait_item_flag = other.trait_item_flag;
+
+    return *this;
+  }
+
+  static SubstitutionArgumentMappings error ()
+  {
+    return SubstitutionArgumentMappings ({}, Location (), nullptr, false);
+  }
+
+  bool is_error () const { return mappings.size () == 0; }
+
+  bool get_argument_for_symbol (const ParamType *param_to_find,
+				SubstitutionArg *argument)
+  {
+    for (auto &mapping : mappings)
+      {
+	const SubstitutionParamMapping *param = mapping.get_param_mapping ();
+	const ParamType *p = param->get_param_ty ();
+
+	if (p->get_symbol ().compare (param_to_find->get_symbol ()) == 0)
+	  {
+	    *argument = mapping;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool get_argument_at (size_t index, SubstitutionArg *argument)
+  {
+    if (index > mappings.size ())
+      return false;
+
+    *argument = mappings.at (index);
+    return true;
+  }
+
+  // is_concrete means if the used args is non error, ie: non empty this will
+  // verify if actual real types have been put in place of are they still
+  // ParamTy
+  bool is_concrete () const
+  {
+    for (auto &mapping : mappings)
+      {
+	if (!mapping.is_conrete ())
+	  return false;
+      }
+    return true;
+  }
+
+  Location get_locus () const { return locus; }
+
+  size_t size () const { return mappings.size (); }
+
+  bool is_empty () const { return size () == 0; }
+
+  std::vector<SubstitutionArg> &get_mappings () { return mappings; }
+
+  const std::vector<SubstitutionArg> &get_mappings () const { return mappings; }
+
+  std::string as_string () const
+  {
+    std::string buffer;
+    for (auto &mapping : mappings)
+      {
+	buffer += mapping.as_string () + ", ";
+      }
+    return "<" + buffer + ">";
+  }
+
+  void on_param_subst (const ParamType &p, const SubstitutionArg &a) const
+  {
+    if (param_subst_cb == nullptr)
+      return;
+
+    param_subst_cb (p, a);
+  }
+
+  ParamSubstCb get_subst_cb () const { return param_subst_cb; }
+
+  bool trait_item_mode () const { return trait_item_flag; }
+
+private:
+  std::vector<SubstitutionArg> mappings;
+  Location locus;
+  ParamSubstCb param_subst_cb;
+  bool trait_item_flag;
+};
+
+class SubstitutionRef
+{
+public:
+  SubstitutionRef (std::vector<SubstitutionParamMapping> substitutions,
+		   SubstitutionArgumentMappings arguments)
+    : substitutions (substitutions), used_arguments (arguments)
+  {}
+
+  bool has_substitutions () const { return substitutions.size () > 0; }
+
+  std::string subst_as_string () const
+  {
+    std::string buffer;
+    for (size_t i = 0; i < substitutions.size (); i++)
+      {
+	const SubstitutionParamMapping &sub = substitutions.at (i);
+	buffer += sub.as_string ();
+
+	if ((i + 1) < substitutions.size ())
+	  buffer += ", ";
+      }
+
+    return buffer.empty () ? "" : "<" + buffer + ">";
+  }
+
+  size_t get_num_substitutions () const { return substitutions.size (); }
+
+  std::vector<SubstitutionParamMapping> &get_substs () { return substitutions; }
+
+  const std::vector<SubstitutionParamMapping> &get_substs () const
+  {
+    return substitutions;
+  }
+
+  std::vector<SubstitutionParamMapping> clone_substs () const
+  {
+    std::vector<SubstitutionParamMapping> clone;
+
+    for (auto &sub : substitutions)
+      clone.push_back (sub.clone ());
+
+    return clone;
+  }
+
+  void override_context ()
+  {
+    for (auto &sub : substitutions)
+      {
+	sub.override_context ();
+      }
+  }
+
+  bool needs_substitution () const
+  {
+    for (auto &sub : substitutions)
+      {
+	if (sub.need_substitution ())
+	  return true;
+      }
+    return false;
+  }
+
+  bool was_substituted () const { return !needs_substitution (); }
+
+  SubstitutionArgumentMappings get_substitution_arguments () const
+  {
+    return used_arguments;
+  }
+
+  // this is the count of type params that are not substituted fuly
+  size_t num_required_substitutions () const
+  {
+    size_t n = 0;
+    for (auto &p : substitutions)
+      {
+	if (p.needs_substitution ())
+	  n++;
+      }
+    return n;
+  }
+
+  // this is the count of type params that need substituted taking into account
+  // possible defaults
+  size_t min_required_substitutions () const
+  {
+    size_t n = 0;
+    for (auto &p : substitutions)
+      {
+	if (p.needs_substitution () && !p.param_has_default_ty ())
+	  n++;
+      }
+    return n;
+  }
+
+  // We are trying to subst <i32, f32> into Struct Foo<X,Y> {}
+  // in the case of Foo<i32,f32>{...}
+  //
+  // the substitions we have here define X,Y but the arguments have no bindings
+  // so its a matter of ordering
+  SubstitutionArgumentMappings
+  get_mappings_from_generic_args (HIR::GenericArgs &args);
+
+  // Recursive substitutions
+  // Foo <A,B> { a:A, b: B}; Bar <X,Y,Z>{a:X, b: Foo<Y,Z>}
+  //
+  // we have bindings for X Y Z and need to propagate the binding Y,Z into Foo
+  // Which binds to A,B
+  SubstitutionArgumentMappings
+  adjust_mappings_for_this (SubstitutionArgumentMappings &mappings);
+
+  // Are the mappings here actually bound to this type. For example imagine the
+  // case:
+  //
+  // struct Foo<T>(T);
+  // impl<T> Foo<T> {
+  //   fn test(self) { ... }
+  // }
+  //
+  // In this case we have a generic ADT of Foo and an impl block of a generic T
+  // on Foo for the Self type. When we it comes to path resolution we can have:
+  //
+  // Foo::<i32>::test()
+  //
+  // This means the first segment of Foo::<i32> returns the ADT Foo<i32> not the
+  // Self ADT bound to the T from the impl block. This means when it comes to
+  // the next segment of test which resolves to the function we need to check
+  // wether the arguments in the struct definition of foo can be bound here
+  // before substituting the previous segments type here. This functions acts as
+  // a guard for the solve_mappings_from_receiver_for_self to handle the case
+  // where arguments are not bound. This is important for this next case:
+  //
+  // struct Baz<A, B>(A, B);
+  // impl Baz<i32, f32> {
+  //   fn test<X>(a: X) -> X {
+  //       a
+  //   }
+  // }
+  //
+  // In this case Baz has been already substituted for the impl's Self to become
+  // ADT<i32, f32> so that the function test only has 1 generic argument of X.
+  // The path for this will be:
+  //
+  // Baz::test::<_>(123)
+  //
+  // So the first segment here will be Baz<_, _> to try and infer the arguments
+  // which will be taken from the impl's Self type in this case since it is
+  // already substituted and like the previous case the check to see if we need
+  // to inherit the previous segments generic arguments takes place but the
+  // generic arguments are not bound to this type as they have already been
+  // substituted.
+  //
+  // Its important to remember from the first example the FnType actually looks
+  // like:
+  //
+  // fn <T>test(self :Foo<T>(T))
+  //
+  // As the generic parameters are "bound" to each of the items in the impl
+  // block. So this check is about wether the arguments we have here can
+  // actually be bound to this type.
+  bool are_mappings_bound (SubstitutionArgumentMappings &mappings);
+
+  // struct Foo<A, B>(A, B);
+  //
+  // impl<T> Foo<T, f32>;
+  //     -> fn test<X>(self, a: X) -> X
+  //
+  // We might invoke this via:
+  //
+  // a = Foo(123, 456f32);
+  // b = a.test::<bool>(false);
+  //
+  // we need to figure out relevant generic arguemts for self to apply to the
+  // fntype
+  SubstitutionArgumentMappings solve_mappings_from_receiver_for_self (
+    SubstitutionArgumentMappings &mappings) const;
+
+  // TODO comment
+  SubstitutionArgumentMappings
+  solve_missing_mappings_from_this (SubstitutionRef &ref, SubstitutionRef &to);
+
+  // TODO comment
+  BaseType *infer_substitions (Location locus)
+  {
+    std::vector<SubstitutionArg> args;
+    std::map<std::string, BaseType *> argument_mappings;
+    for (auto &p : get_substs ())
+      {
+	if (p.needs_substitution ())
+	  {
+	    const std::string &symbol = p.get_param_ty ()->get_symbol ();
+	    auto it = argument_mappings.find (symbol);
+	    if (it == argument_mappings.end ())
+	      {
+		TyVar infer_var = TyVar::get_implicit_infer_var (locus);
+		args.push_back (SubstitutionArg (&p, infer_var.get_tyty ()));
+		argument_mappings[symbol] = infer_var.get_tyty ();
+	      }
+	    else
+	      {
+		args.push_back (SubstitutionArg (&p, it->second));
+	      }
+	  }
+	else
+	  {
+	    args.push_back (
+	      SubstitutionArg (&p, p.get_param_ty ()->resolve ()));
+	  }
+      }
+
+    SubstitutionArgumentMappings infer_arguments (std::move (args), locus);
+    return handle_substitions (std::move (infer_arguments));
+  }
+
+  // TODO comment
+  bool monomorphize ();
+
+  // TODO comment
+  virtual BaseType *handle_substitions (SubstitutionArgumentMappings mappings)
+    = 0;
+
+  SubstitutionArgumentMappings get_used_arguments () const
+  {
+    return used_arguments;
+  }
+
+protected:
+  std::vector<SubstitutionParamMapping> substitutions;
+  SubstitutionArgumentMappings used_arguments;
+};
+
+class TypeBoundPredicate : public SubstitutionRef
+{
+public:
+  TypeBoundPredicate (const Resolver::TraitReference &trait_reference,
+		      Location locus);
+
+  TypeBoundPredicate (DefId reference,
+		      std::vector<SubstitutionParamMapping> substitutions,
+		      Location locus);
+
+  TypeBoundPredicate (const TypeBoundPredicate &other);
+
+  TypeBoundPredicate &operator= (const TypeBoundPredicate &other);
+
+  static TypeBoundPredicate error ();
+
+  std::string as_string () const;
+
+  std::string as_name () const;
+
+  const Resolver::TraitReference *get () const;
+
+  Location get_locus () const { return locus; }
+
+  std::string get_name () const;
+
+  // check that this predicate is object-safe see:
+  // https://doc.rust-lang.org/reference/items/traits.html#object-safety
+  bool is_object_safe (bool emit_error, Location locus) const;
+
+  void apply_generic_arguments (HIR::GenericArgs *generic_args);
+
+  bool contains_item (const std::string &search) const;
+
+  TypeBoundPredicateItem
+  lookup_associated_item (const std::string &search) const;
+
+  TypeBoundPredicateItem
+  lookup_associated_item (const Resolver::TraitItemReference *ref) const;
+
+  // WARNING THIS WILL ALWAYS RETURN NULLPTR
+  BaseType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+  bool is_error () const;
+
+  bool requires_generic_args () const;
+
+private:
+  DefId reference;
+  Location locus;
+  bool error_flag;
+};
+
+// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.VariantDef.html
+class VariantDef
+{
+public:
+  enum VariantType
+  {
+    NUM,
+    TUPLE,
+    STRUCT
+  };
+
+  static std::string variant_type_string (VariantType type)
+  {
+    switch (type)
+      {
+      case NUM:
+	return "enumeral";
+      case TUPLE:
+	return "tuple";
+      case STRUCT:
+	return "struct";
+      }
+    gcc_unreachable ();
+    return "";
+  }
+
+  VariantDef (HirId id, std::string identifier, RustIdent ident,
+	      HIR::Expr *discriminant)
+    : id (id), identifier (identifier), ident (ident),
+      discriminant (discriminant)
+
+  {
+    type = VariantType::NUM;
+    fields = {};
+  }
+
+  VariantDef (HirId id, std::string identifier, RustIdent ident,
+	      VariantType type, HIR::Expr *discriminant,
+	      std::vector<StructFieldType *> fields)
+    : id (id), identifier (identifier), ident (ident), type (type),
+      discriminant (discriminant), fields (fields)
+  {
+    rust_assert (
+      (type == VariantType::NUM && fields.empty ())
+      || (type == VariantType::TUPLE || type == VariantType::STRUCT));
+  }
+
+  VariantDef (const VariantDef &other)
+    : id (other.id), identifier (other.identifier), ident (other.ident),
+      type (other.type), discriminant (other.discriminant),
+      fields (other.fields)
+  {}
+
+  VariantDef &operator= (const VariantDef &other)
+  {
+    id = other.id;
+    identifier = other.identifier;
+    type = other.type;
+    discriminant = other.discriminant;
+    fields = other.fields;
+    ident = other.ident;
+
+    return *this;
+  }
+
+  static VariantDef &get_error_node ()
+  {
+    static VariantDef node
+      = VariantDef (UNKNOWN_HIRID, "",
+		    {Resolver::CanonicalPath::create_empty (),
+		     Linemap::unknown_location ()},
+		    nullptr);
+
+    return node;
+  }
+
+  bool is_error () const { return get_id () == UNKNOWN_HIRID; }
+
+  HirId get_id () const { return id; }
+
+  VariantType get_variant_type () const { return type; }
+  bool is_data_variant () const { return type != VariantType::NUM; }
+  bool is_dataless_variant () const { return type == VariantType::NUM; }
+
+  std::string get_identifier () const { return identifier; }
+
+  size_t num_fields () const { return fields.size (); }
+  StructFieldType *get_field_at_index (size_t index)
+  {
+    rust_assert (index < fields.size ());
+    return fields.at (index);
+  }
+
+  std::vector<StructFieldType *> &get_fields ()
+  {
+    rust_assert (type != NUM);
+    return fields;
+  }
+
+  bool lookup_field (const std::string &lookup, StructFieldType **field_lookup,
+		     size_t *index) const
+  {
+    size_t i = 0;
+    for (auto &field : fields)
+      {
+	if (field->get_name ().compare (lookup) == 0)
+	  {
+	    if (index != nullptr)
+	      *index = i;
+
+	    if (field_lookup != nullptr)
+	      *field_lookup = field;
+
+	    return true;
+	  }
+	i++;
+      }
+    return false;
+  }
+
+  HIR::Expr *get_discriminant () const
+  {
+    rust_assert (discriminant != nullptr);
+    return discriminant;
+  }
+
+  std::string as_string () const
+  {
+    if (type == VariantType::NUM)
+      return identifier + " = " + discriminant->as_string ();
+
+    std::string buffer;
+    for (size_t i = 0; i < fields.size (); ++i)
+      {
+	buffer += fields.at (i)->as_string ();
+	if ((i + 1) < fields.size ())
+	  buffer += ", ";
+      }
+
+    if (type == VariantType::TUPLE)
+      return identifier + " (" + buffer + ")";
+    else
+      return identifier + " {" + buffer + "}";
+  }
+
+  bool is_equal (const VariantDef &other) const
+  {
+    if (type != other.type)
+      return false;
+
+    if (identifier.compare (other.identifier) != 0)
+      return false;
+
+    if (discriminant != other.discriminant)
+      return false;
+
+    if (fields.size () != other.fields.size ())
+      return false;
+
+    for (size_t i = 0; i < fields.size (); i++)
+      {
+	if (!fields.at (i)->is_equal (*other.fields.at (i)))
+	  return false;
+      }
+
+    return true;
+  }
+
+  VariantDef *clone () const
+  {
+    std::vector<StructFieldType *> cloned_fields;
+    for (auto &f : fields)
+      cloned_fields.push_back ((StructFieldType *) f->clone ());
+
+    return new VariantDef (id, identifier, ident, type, discriminant,
+			   cloned_fields);
+  }
+
+  VariantDef *monomorphized_clone () const
+  {
+    std::vector<StructFieldType *> cloned_fields;
+    for (auto &f : fields)
+      cloned_fields.push_back ((StructFieldType *) f->monomorphized_clone ());
+
+    return new VariantDef (id, identifier, ident, type, discriminant,
+			   cloned_fields);
+  }
+
+  const RustIdent &get_ident () const { return ident; }
+
+private:
+  HirId id;
+  std::string identifier;
+  RustIdent ident;
+  VariantType type;
+  // can either be a structure or a discriminant value
+  HIR::Expr *discriminant;
+  std::vector<StructFieldType *> fields;
+};
+
+class ADTType : public BaseType, public SubstitutionRef
+{
+public:
+  enum ADTKind
+  {
+    STRUCT_STRUCT,
+    TUPLE_STRUCT,
+    UNION,
+    ENUM
+  };
+
+  // Representation options, specified via attributes e.g. #[repr(packed)]
+  struct ReprOptions
+  {
+    // bool is_c;
+    // bool is_transparent;
+    //...
+
+    // For align and pack: 0 = unspecified. Nonzero = byte alignment.
+    // It is an error for both to be nonzero, this should be caught when
+    // parsing the #[repr] attribute.
+    unsigned char align = 0;
+    unsigned char pack = 0;
+  };
+
+  ADTType (HirId ref, std::string identifier, RustIdent ident, ADTKind adt_kind,
+	   std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind)
+  {}
+
+  ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident,
+	   ADTKind adt_kind, std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind)
+  {}
+
+  ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident,
+	   ADTKind adt_kind, std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs, ReprOptions repr,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind),
+      repr (repr)
+  {}
+
+  ADTKind get_adt_kind () const { return adt_kind; }
+  ReprOptions get_repr_options () const { return repr; }
+
+  bool is_struct_struct () const { return adt_kind == STRUCT_STRUCT; }
+  bool is_tuple_struct () const { return adt_kind == TUPLE_STRUCT; }
+  bool is_union () const { return adt_kind == UNION; }
+  bool is_enum () const { return adt_kind == ENUM; }
+
+  bool is_unit () const override
+  {
+    if (number_of_variants () == 0)
+      return true;
+
+    if (number_of_variants () == 1)
+      return variants.at (0)->num_fields () == 0;
+
+    return false;
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  std::string get_identifier () const { return identifier; }
+
+  std::string get_name () const override final
+  {
+    return identifier + subst_as_string ();
+  }
+
+  bool is_concrete () const override final
+  {
+    for (auto &variant : variants)
+      {
+	for (auto &field : variant->get_fields ())
+	  {
+	    if (!field->is_concrete ())
+	      return false;
+	  }
+      }
+    return true;
+  }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  size_t number_of_variants () const { return variants.size (); }
+
+  std::vector<VariantDef *> &get_variants () { return variants; }
+  const std::vector<VariantDef *> &get_variants () const { return variants; }
+
+  bool lookup_variant (const std::string &lookup,
+		       VariantDef **found_variant) const
+  {
+    for (auto &variant : variants)
+      {
+	if (variant->get_identifier ().compare (lookup) == 0)
+	  {
+	    *found_variant = variant;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_variant_by_id (HirId id, VariantDef **found_variant,
+			     int *index = nullptr) const
+  {
+    int i = 0;
+    for (auto &variant : variants)
+      {
+	if (variant->get_id () == id)
+	  {
+	    if (index != nullptr)
+	      *index = i;
+
+	    *found_variant = variant;
+	    return true;
+	  }
+	i++;
+      }
+    return false;
+  }
+
+  ADTType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  std::string identifier;
+  std::vector<VariantDef *> variants;
+  ADTType::ADTKind adt_kind;
+  ReprOptions repr;
+};
+
+class FnType : public BaseType, public SubstitutionRef
+{
+public:
+  static const uint8_t FNTYPE_DEFAULT_FLAGS = 0x00;
+  static const uint8_t FNTYPE_IS_METHOD_FLAG = 0x01;
+  static const uint8_t FNTYPE_IS_EXTERN_FLAG = 0x02;
+  static const uint8_t FNTYPE_IS_VARADIC_FLAG = 0X04;
+
+  FnType (HirId ref, DefId id, std::string identifier, RustIdent ident,
+	  uint8_t flags, ABI abi,
+	  std::vector<std::pair<HIR::Pattern *, BaseType *>> params,
+	  BaseType *type, std::vector<SubstitutionParamMapping> subst_refs,
+	  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FNDEF, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      params (std::move (params)), type (type), flags (flags),
+      identifier (identifier), id (id), abi (abi)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  FnType (HirId ref, HirId ty_ref, DefId id, std::string identifier,
+	  RustIdent ident, uint8_t flags, ABI abi,
+	  std::vector<std::pair<HIR::Pattern *, BaseType *>> params,
+	  BaseType *type, std::vector<SubstitutionParamMapping> subst_refs,
+	  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FNDEF, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      params (params), type (type), flags (flags), identifier (identifier),
+      id (id), abi (abi)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  std::string get_identifier () const { return identifier; }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  size_t num_params () const { return params.size (); }
+
+  bool is_method () const
+  {
+    if (num_params () == 0)
+      return false;
+
+    return (flags & FNTYPE_IS_METHOD_FLAG) != 0;
+  }
+
+  bool is_extern () const { return (flags & FNTYPE_IS_EXTERN_FLAG) != 0; }
+
+  bool is_varadic () const { return (flags & FNTYPE_IS_VARADIC_FLAG) != 0; }
+
+  DefId get_id () const { return id; }
+
+  // get the Self type for the method
+  BaseType *get_self_type () const
+  {
+    rust_assert (is_method ());
+    return param_at (0).second;
+  }
+
+  bool is_concrete () const override final
+  {
+    for (const auto &param : params)
+      {
+	const BaseType *p = param.second;
+	if (!p->is_concrete ())
+	  return false;
+      }
+    return get_return_type ()->is_concrete ();
+  }
+
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> &get_params ()
+  {
+    return params;
+  }
+
+  const std::vector<std::pair<HIR::Pattern *, BaseType *>> &get_params () const
+  {
+    return params;
+  }
+
+  std::pair<HIR::Pattern *, BaseType *> &param_at (size_t idx)
+  {
+    return params.at (idx);
+  }
+
+  const std::pair<HIR::Pattern *, BaseType *> &param_at (size_t idx) const
+  {
+    return params.at (idx);
+  }
+
+  BaseType *get_return_type () const { return type; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  FnType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+  ABI get_abi () const { return abi; }
+
+private:
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> params;
+  BaseType *type;
+  uint8_t flags;
+  std::string identifier;
+  DefId id;
+  ABI abi;
+};
+
+class FnPtr : public BaseType
+{
+public:
+  FnPtr (HirId ref, Location locus, std::vector<TyVar> params,
+	 TyVar result_type, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FNPTR,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      params (std::move (params)), result_type (result_type)
+  {}
+
+  FnPtr (HirId ref, HirId ty_ref, Location locus, std::vector<TyVar> params,
+	 TyVar result_type, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FNPTR,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      params (params), result_type (result_type)
+  {}
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *get_return_type () const { return result_type.get_tyty (); }
+
+  size_t num_params () const { return params.size (); }
+
+  BaseType *param_at (size_t idx) const { return params.at (idx).get_tyty (); }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  void iterate_params (std::function<bool (BaseType *)> cb) const
+  {
+    for (auto &p : params)
+      {
+	if (!cb (p.get_tyty ()))
+	  return;
+      }
+  }
+
+  std::vector<TyVar> &get_params () { return params; }
+  const std::vector<TyVar> &get_params () const { return params; }
+
+  bool is_concrete () const override final
+  {
+    for (auto &p : params)
+      {
+	if (!p.get_tyty ()->is_concrete ())
+	  return false;
+      }
+    return result_type.get_tyty ()->is_concrete ();
+  }
+
+private:
+  std::vector<TyVar> params;
+  TyVar result_type;
+};
+
+class ClosureType : public BaseType, public SubstitutionRef
+{
+public:
+  ClosureType (HirId ref, DefId id, RustIdent ident,
+	       std::vector<TyVar> parameter_types, TyVar result_type,
+	       std::vector<SubstitutionParamMapping> subst_refs,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::CLOSURE, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      parameter_types (std::move (parameter_types)),
+      result_type (std::move (result_type)), id (id)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  ClosureType (HirId ref, HirId ty_ref, RustIdent ident, DefId id,
+	       std::vector<TyVar> parameter_types, TyVar result_type,
+	       std::vector<SubstitutionParamMapping> subst_refs,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::CLOSURE, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      parameter_types (std::move (parameter_types)),
+      result_type (std::move (result_type)), id (id)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    for (auto &param : parameter_types)
+      {
+	auto p = param.get_tyty ();
+	if (!p->is_concrete ())
+	  return false;
+      }
+    return result_type.get_tyty ()->is_concrete ();
+  }
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  ClosureType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  std::vector<TyVar> parameter_types;
+  TyVar result_type;
+  DefId id;
+};
+
+class ArrayType : public BaseType
+{
+public:
+  ArrayType (HirId ref, Location locus, HIR::Expr &capacity_expr, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ARRAY,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base), capacity_expr (capacity_expr)
+  {}
+
+  ArrayType (HirId ref, HirId ty_ref, Location locus, HIR::Expr &capacity_expr,
+	     TyVar base, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ARRAY,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base), capacity_expr (capacity_expr)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *get_element_type () const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const final override
+  {
+    return get_element_type ()->is_concrete ();
+  }
+
+  HIR::Expr &get_capacity_expr () const { return capacity_expr; }
+
+  ArrayType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  TyVar element_type;
+  HIR::Expr &capacity_expr;
+};
+
+class SliceType : public BaseType
+{
+public:
+  SliceType (HirId ref, Location locus, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::SLICE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base)
+  {}
+
+  SliceType (HirId ref, HirId ty_ref, Location locus, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::SLICE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *get_element_type () const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const final override
+  {
+    return get_element_type ()->is_concrete ();
+  }
+
+  SliceType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  TyVar element_type;
+};
+
+class BoolType : public BaseType
+{
+public:
+  BoolType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::BOOL,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  BoolType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::BOOL,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class IntType : public BaseType
+{
+public:
+  enum IntKind
+  {
+    I8,
+    I16,
+    I32,
+    I64,
+    I128
+  };
+
+  IntType (HirId ref, IntKind kind, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::INT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      int_kind (kind)
+  {}
+
+  IntType (HirId ref, HirId ty_ref, IntKind kind,
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::INT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      int_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  IntKind get_int_kind () const { return int_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  IntKind int_kind;
+};
+
+class UintType : public BaseType
+{
+public:
+  enum UintKind
+  {
+    U8,
+    U16,
+    U32,
+    U64,
+    U128
+  };
+
+  UintType (HirId ref, UintKind kind, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::UINT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      uint_kind (kind)
+  {}
+
+  UintType (HirId ref, HirId ty_ref, UintKind kind,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::UINT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      uint_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  UintKind get_uint_kind () const { return uint_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  UintKind uint_kind;
+};
+
+class FloatType : public BaseType
+{
+public:
+  enum FloatKind
+  {
+    F32,
+    F64
+  };
+
+  FloatType (HirId ref, FloatKind kind,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FLOAT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      float_kind (kind)
+  {}
+
+  FloatType (HirId ref, HirId ty_ref, FloatKind kind,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FLOAT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      float_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  FloatKind get_float_kind () const { return float_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  FloatKind float_kind;
+};
+
+class USizeType : public BaseType
+{
+public:
+  USizeType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::USIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  USizeType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::USIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class ISizeType : public BaseType
+{
+public:
+  ISizeType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ISIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  ISizeType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ISIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class CharType : public BaseType
+{
+public:
+  CharType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::CHAR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  CharType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::CHAR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class StrType : public BaseType
+{
+public:
+  StrType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::STR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  StrType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::STR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  std::string get_name () const override final { return as_string (); }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class ReferenceType : public BaseType
+{
+public:
+  ReferenceType (HirId ref, TyVar base, Mutability mut,
+		 std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::REF,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  ReferenceType (HirId ref, HirId ty_ref, TyVar base, Mutability mut,
+		 std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::REF,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  BaseType *get_base () const;
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final
+  {
+    return "&" + get_base ()->get_name ();
+  }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    return get_base ()->is_concrete ();
+  }
+
+  ReferenceType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+  Mutability mutability () const { return mut; }
+
+  bool is_mutable () const { return mut == Mutability::Mut; }
+
+  bool is_dyn_object () const
+  {
+    return is_dyn_slice_type () || is_dyn_str_type ();
+  }
+
+  bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::SLICE)
+      return false;
+    if (slice == nullptr)
+      return true;
+
+    *slice = static_cast<const TyTy::SliceType *> (element);
+    return true;
+  }
+
+  bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::STR)
+      return false;
+    if (str == nullptr)
+      return true;
+
+    *str = static_cast<const TyTy::StrType *> (element);
+    return true;
+  }
+
+private:
+  TyVar base;
+  Mutability mut;
+};
+
+class PointerType : public BaseType
+{
+public:
+  PointerType (HirId ref, TyVar base, Mutability mut,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::POINTER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  PointerType (HirId ref, HirId ty_ref, TyVar base, Mutability mut,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::POINTER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  BaseType *get_base () const;
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final
+  {
+    return "*" + get_base ()->get_name ();
+  }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    return get_base ()->is_concrete ();
+  }
+
+  PointerType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+  Mutability mutability () const { return mut; }
+
+  bool is_mutable () const { return mut == Mutability::Mut; }
+
+  bool is_const () const { return mut == Mutability::Imm; }
+
+  bool is_dyn_object () const
+  {
+    return is_dyn_slice_type () || is_dyn_str_type ();
+  }
+
+  bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::SLICE)
+      return false;
+    if (slice == nullptr)
+      return true;
+
+    *slice = static_cast<const TyTy::SliceType *> (element);
+    return true;
+  }
+
+  bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::STR)
+      return false;
+    if (str == nullptr)
+      return true;
+
+    *str = static_cast<const TyTy::StrType *> (element);
+    return true;
+  }
+
+private:
+  TyVar base;
+  Mutability mut;
+};
+
+// https://doc.rust-lang.org/std/primitive.never.html
+//
+// Since the `!` type is really complicated and it is even still unstable
+// in rustc, only fairly limited support for this type is introduced here.
+// Unification between `!` and ANY other type (including `<T?>`) is simply
+// not allowed. If it is needed, it should be handled manually. For example,
+// unifying `!` with other types is very necessary when resolving types of
+// `if/else` expressions.
+//
+// See related discussion at https://github.com/Rust-GCC/gccrs/pull/364
+class NeverType : public BaseType
+{
+public:
+  NeverType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::NEVER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  NeverType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::NEVER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override { return true; }
+  bool is_concrete () const override final { return true; }
+};
+
+// used at the type in associated types in traits
+// see: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+class PlaceholderType : public BaseType
+{
+public:
+  PlaceholderType (std::string symbol, HirId ref,
+		   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PLACEHOLDER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      symbol (symbol)
+  {}
+
+  PlaceholderType (std::string symbol, HirId ref, HirId ty_ref,
+		   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PLACEHOLDER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      symbol (symbol)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override
+  {
+    rust_assert (can_resolve ());
+    return resolve ()->is_unit ();
+  }
+
+  std::string get_symbol () const { return symbol; }
+
+  void set_associated_type (HirId ref);
+
+  void clear_associated_type ();
+
+  bool can_resolve () const;
+
+  BaseType *resolve () const;
+
+  bool is_equal (const BaseType &other) const override;
+
+  bool is_concrete () const override final
+  {
+    if (!can_resolve ())
+      return true;
+
+    return resolve ()->is_concrete ();
+  }
+
+private:
+  std::string symbol;
+};
+
+class ProjectionType : public BaseType, public SubstitutionRef
+{
+public:
+  ProjectionType (HirId ref, BaseType *base,
+		  const Resolver::TraitReference *trait, DefId item,
+		  std::vector<SubstitutionParamMapping> subst_refs,
+		  SubstitutionArgumentMappings generic_arguments
+		  = SubstitutionArgumentMappings::error (),
+		  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PROJECTION,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      base (base), trait (trait), item (item)
+  {}
+
+  ProjectionType (HirId ref, HirId ty_ref, BaseType *base,
+		  const Resolver::TraitReference *trait, DefId item,
+		  std::vector<SubstitutionParamMapping> subst_refs,
+		  SubstitutionArgumentMappings generic_arguments
+		  = SubstitutionArgumentMappings::error (),
+		  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PROJECTION,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      base (base), trait (trait), item (item)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override { return false; }
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  const BaseType *get () const { return base; }
+  BaseType *get () { return base; }
+
+  bool is_concrete () const override final { return base->is_concrete (); }
+
+  ProjectionType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  BaseType *base;
+  const Resolver::TraitReference *trait;
+  DefId item;
+};
+
+class DynamicObjectType : public BaseType
+{
+public:
+  DynamicObjectType (HirId ref, RustIdent ident,
+		     std::vector<TypeBoundPredicate> specified_bounds,
+		     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::DYNAMIC, ident, specified_bounds, refs)
+  {}
+
+  DynamicObjectType (HirId ref, HirId ty_ref, RustIdent ident,
+		     std::vector<TypeBoundPredicate> specified_bounds,
+		     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::DYNAMIC, ident, specified_bounds, refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final;
+
+  bool is_concrete () const override final { return true; }
+
+  // this returns a flat list of items including super trait bounds
+  const std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+  get_object_items () const;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY
-- 
2.25.1


  parent reply	other threads:[~2022-08-24 12:01 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-24 11:59 Rust frontend patches v2 herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
2022-08-24 14:28   ` Jason Merrill
2022-08-24 11:59 ` [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite herron.philip
2022-09-10  4:05   ` Mike Stump
2022-08-24 11:59 ` [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test cases herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 06/37] gccrs: Add execution " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust herron.philip
2022-09-14 13:41   ` Richard Biener
2022-09-14 14:04     ` Jakub Jelinek
2022-08-24 11:59 ` [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end herron.philip
2022-09-14 13:30   ` Richard Biener
2022-09-14 13:39     ` Jakub Jelinek
2022-08-24 11:59 ` [PATCH Rust front-end v2 10/37] gccrs: Add Parser " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite representation called HIR herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used during legacy symbol mangling herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical path and lang items herron.philip
2022-08-24 11:59 ` herron.philip [this message]
2022-08-24 11:59 ` [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 24/37] gccrs: Add const checker herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from reusing gccgo herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in herron.philip
2022-09-14 13:34   ` Richard Biener
2022-12-01 11:05     ` Thomas Schwinge
2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
2022-09-14 13:40   ` Richard Biener
2023-02-20 13:33   ` Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace' (was: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in) Thomas Schwinge
2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
2022-09-14 13:40   ` Richard Biener
2022-10-14 16:33   ` Iain Buclaw
2022-08-24 11:59 ` [PATCH Rust front-end v2 34/37] gccrs: add lang.opt herron.philip
2022-09-14 13:39   ` Richard Biener
2022-09-14 16:20     ` Thomas Schwinge
2022-09-15  6:23       ` Richard Biener
2022-08-24 11:59 ` [PATCH Rust front-end v2 35/37] gccrs: add compiler driver herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface kicks off the pipeline herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and compiler logo herron.philip
2022-08-25  9:46 ` Rust frontend patches v2 Philip Herron
2022-08-25  9:52   ` Martin Liška
2022-08-25 10:18     ` Philip Herron
2022-08-25 12:50       ` Frank Ch. Eigler
2022-08-25 13:44         ` Philip Herron
2022-08-25 11:13     ` Mark Wielaard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220824115956.737931-23-philip.herron@embecosm.com \
    --to=herron.philip@googlemail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=gcc-rust@gcc.gnu.org \
    --cc=philip.herron@embecosm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).