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-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-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
