diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 4752bb83562..1d2f34d7919 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -134,6 +134,7 @@ GRS_OBJS = \
     rust/rust-hir-path-probe.o \
     rust/rust-coercion.o \
     rust/rust-casts.o \
+    rust/rust-unify.o \
     rust/rust-hir-type-check-base.o \
     rust/rust-autoderef.o \
     rust/rust-substitution-mapper.o \
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index c26f6f440ec..d7945dbf26b 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -26,6 +26,7 @@
 #include "rust-compile-block.h"
 #include "rust-compile-implitem.h"
 #include "rust-constexpr.h"
+#include "rust-unify.h"
 #include "rust-gcc.h"
 
 #include "fold-const.h"
@@ -2006,7 +2007,10 @@ CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,
 	{
 	  TyTy::BaseType *infer_impl_call
 	    = candidate_call->infer_substitions (expr_locus);
-	  monomorphized = infer_impl_call->unify (fntype);
+	  monomorphized = Resolver::UnifyRules::Resolve (
+	    TyTy::TyWithLocation (infer_impl_call),
+	    TyTy::TyWithLocation (fntype), expr_locus, true /* commit */,
+	    true /* emit_errors */);
 	}
 
       return CompileInherentImplItem::Compile (impl_item, ctx, monomorphized);
diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc
index fdf8eb95a33..bea40840fbf 100644
--- a/gcc/rust/typecheck/rust-coercion.cc
+++ b/gcc/rust/typecheck/rust-coercion.cc
@@ -18,6 +18,7 @@
 
 #include "rust-hir-type-check-base.h"
 #include "rust-coercion.h"
+#include "rust-unify.h"
 
 namespace Rust {
 namespace Resolver {
@@ -218,7 +219,10 @@ TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
 	// we might be able to replace this with a can_eq because we default
 	// back to a final unity anyway
 	rust_debug ("coerce_borrowed_pointer -- unify");
-	TyTy::BaseType *result = receiver->unify (expected);
+	TyTy::BaseType *result
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (receiver),
+				 TyTy::TyWithLocation (expected), locus,
+				 true /* commit */, true /* emit_errors */);
 	return CoercionResult{{}, result};
       }
     }
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc
index 85826aec8fe..6e42b7b7b23 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -22,6 +22,7 @@
 #include "rust-hir-type-check-expr.h"
 #include "rust-hir-type-check-implitem.h"
 #include "rust-coercion.h"
+#include "rust-unify.h"
 #include "rust-casts.h"
 
 namespace Rust {
@@ -359,18 +360,8 @@ TypeCheckBase::unify_site (HirId id, TyTy::TyWithLocation lhs,
   rust_debug ("unify_site id={%u} expected={%s} expr={%s}", id,
 	      expected->debug_str ().c_str (), expr->debug_str ().c_str ());
 
-  TyTy::BaseType *unified = expected->unify (expr);
-  if (unified->get_kind () == TyTy::TypeKind::ERROR)
-    {
-      RichLocation r (unify_locus);
-      r.add_range (lhs.get_locus ());
-      r.add_range (rhs.get_locus ());
-      rust_error_at (r, "expected %<%s%> got %<%s%>",
-		     expected->get_name ().c_str (),
-		     expr->get_name ().c_str ());
-    }
-
-  return unified;
+  return UnifyRules::Resolve (lhs, rhs, unify_locus, true /*commit*/,
+			      true /*emit_error*/);
 }
 
 TyTy::BaseType *
diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h
deleted file mode 100644
index cfe269221bd..00000000000
--- a/gcc/rust/typecheck/rust-tyty-rules.h
+++ /dev/null
@@ -1,1406 +0,0 @@
-// Copyright (C) 2020-2023 Free Software Foundation, Inc.
-
-// This file is part of GCC.
-
-// GCC is free software; you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3, or (at your option) any later
-// version.
-
-// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-// for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with GCC; see the file COPYING3.  If not see
-// <http://www.gnu.org/licenses/>.
-
-#ifndef RUST_TYTY_RULES
-#define RUST_TYTY_RULES
-
-#include "rust-diagnostics.h"
-#include "rust-tyty.h"
-#include "rust-tyty-visitor.h"
-#include "rust-hir-map.h"
-#include "rust-hir-type-check.h"
-
-namespace Rust {
-namespace TyTy {
-
-/* Rules specify how to unify two Ty. For example, the result of unifying the
-   two tuples (u64, A) and (B, i64) would be (u64, i64).
-
-   Performing a unification requires a double dispatch. To illustrate, suppose
-   we want to unify `ty1` and `ty2`. Here's what it looks like:
-     1. The caller calls `ty1.unify(ty2)`. This is the first dispatch.
-     2. `ty1` creates a rule specific to its type(e.g. TupleRules).
-     3. The rule calls `ty2.accept_vis(rule)`. This is the second dispatch.
-     4. `ty2` calls `rule.visit(*this)`, which will method-overload to the
-	      correct implementation at compile time.
-
-   The nice thing about Rules is that they seperate unification logic from the
-   representation of Ty. To support unifying a new Ty, implement its
-   `accept_vis` and `unify` method to pass the unification request to Rules.
-   Then, create a new `XXXRules` class and implement one `visit` method for
-   every Ty it can unify with. */
-class BaseRules : public TyVisitor
-{
-public:
-  virtual ~BaseRules () {}
-
-  /* Unify two ty. Returns a pointer to the newly-created unified ty, or nullptr
-     if the two types cannot be unified. The caller is responsible for releasing
-     the memory of the returned ty.
-
-     This method is meant to be used internally by Ty. If you're trying to unify
-     two ty, you can simply call `unify` on ty themselves. */
-  virtual BaseType *unify (BaseType *other)
-  {
-    if (other->get_kind () == TypeKind::PARAM)
-      {
-	ParamType *p = static_cast<ParamType *> (other);
-	other = p->resolve ();
-      }
-    else if (other->get_kind () == TypeKind::PLACEHOLDER)
-      {
-	PlaceholderType *p = static_cast<PlaceholderType *> (other);
-	if (p->can_resolve ())
-	  {
-	    other = p->resolve ();
-	    return get_base ()->unify (other);
-	  }
-      }
-    else if (other->get_kind () == TypeKind::PROJECTION)
-      {
-	ProjectionType *p = static_cast<ProjectionType *> (other);
-	other = p->get ();
-	return get_base ()->unify (other);
-      }
-
-    other->accept_vis (*this);
-    if (resolved->get_kind () == TyTy::TypeKind::ERROR)
-      return resolved;
-
-    resolved->append_reference (get_base ()->get_ref ());
-    resolved->append_reference (other->get_ref ());
-    for (auto ref : get_base ()->get_combined_refs ())
-      resolved->append_reference (ref);
-    for (auto ref : other->get_combined_refs ())
-      resolved->append_reference (ref);
-
-    other->append_reference (resolved->get_ref ());
-    other->append_reference (get_base ()->get_ref ());
-    get_base ()->append_reference (resolved->get_ref ());
-    get_base ()->append_reference (other->get_ref ());
-
-    bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER;
-    bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER;
-    bool results_is_non_general_infer_var
-      = (result_is_infer_var
-	 && (static_cast<InferType *> (resolved))->get_infer_kind ()
-	      != TyTy::InferType::GENERAL);
-    if (result_resolved || results_is_non_general_infer_var)
-      {
-	for (auto &ref : resolved->get_combined_refs ())
-	  {
-	    TyTy::BaseType *ref_tyty = nullptr;
-	    bool ok = context->lookup_type (ref, &ref_tyty);
-	    if (!ok)
-	      continue;
-
-	    // if any of the types are inference variables lets fix them
-	    if (ref_tyty->get_kind () == TyTy::TypeKind::INFER)
-	      {
-		context->insert_type (
-		  Analysis::NodeMapping (mappings->get_current_crate (),
-					 UNKNOWN_NODEID, ref,
-					 UNKNOWN_LOCAL_DEFID),
-		  resolved->clone ());
-	      }
-	  }
-      }
-    return resolved;
-  }
-
-  virtual void visit (TupleType &) override {}
-
-  virtual void visit (ADTType &) override {}
-
-  virtual void visit (InferType &) override {}
-
-  virtual void visit (FnType &) override {}
-
-  virtual void visit (FnPtr &) override {}
-
-  virtual void visit (ArrayType &) override {}
-
-  virtual void visit (SliceType &) override {}
-
-  virtual void visit (BoolType &) override {}
-
-  virtual void visit (IntType &) override {}
-
-  virtual void visit (UintType &) override {}
-
-  virtual void visit (USizeType &) override {}
-
-  virtual void visit (ISizeType &) override {}
-
-  virtual void visit (FloatType &) override {}
-
-  virtual void visit (ErrorType &) override {}
-
-  virtual void visit (CharType &) override {}
-
-  virtual void visit (ReferenceType &) override {}
-
-  virtual void visit (PointerType &) override {}
-
-  virtual void visit (ParamType &) override {}
-
-  virtual void visit (StrType &) override {}
-
-  virtual void visit (NeverType &) override {}
-
-  virtual void visit (PlaceholderType &) override {}
-
-  virtual void visit (ProjectionType &) override {}
-
-  virtual void visit (DynamicObjectType &) override {}
-
-  virtual void visit (ClosureType &) override {}
-
-protected:
-  BaseRules (BaseType *base)
-    : mappings (Analysis::Mappings::get ()),
-      context (Resolver::TypeCheckContext::get ()),
-      resolved (new ErrorType (base->get_ref (), base->get_ref ()))
-  {}
-
-  Analysis::Mappings *mappings;
-  Resolver::TypeCheckContext *context;
-
-  /* Temporary storage for the result of a unification.
-     We could return the result directly instead of storing it in the rule
-     object, but that involves modifying the visitor pattern to accommodate
-     the return value, which is too complex. */
-  BaseType *resolved;
-
-private:
-  /* Returns a pointer to the ty that created this rule. */
-  virtual BaseType *get_base () = 0;
-};
-
-class InferRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  InferRules (InferType *base) : BaseRules (base), base (base) {}
-
-  void visit (BoolType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (IntType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
-	|| (base->get_infer_kind ()
-	    == TyTy::InferType::InferTypeKind::INTEGRAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (UintType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
-	|| (base->get_infer_kind ()
-	    == TyTy::InferType::InferTypeKind::INTEGRAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (USizeType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
-	|| (base->get_infer_kind ()
-	    == TyTy::InferType::InferTypeKind::INTEGRAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (ISizeType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
-	|| (base->get_infer_kind ()
-	    == TyTy::InferType::InferTypeKind::INTEGRAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (FloatType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
-	|| (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (ArrayType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (SliceType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (ADTType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (TupleType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (InferType &type) override
-  {
-    switch (base->get_infer_kind ())
-      {
-      case InferType::InferTypeKind::GENERAL:
-	resolved = type.clone ();
-	return;
-
-	case InferType::InferTypeKind::INTEGRAL: {
-	  if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
-	    {
-	      resolved = type.clone ();
-	      return;
-	    }
-	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
-	    {
-	      resolved = base->clone ();
-	      return;
-	    }
-	}
-	break;
-
-	case InferType::InferTypeKind::FLOAT: {
-	  if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
-	    {
-	      resolved = type.clone ();
-	      return;
-	    }
-	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
-	    {
-	      resolved = base->clone ();
-	      return;
-	    }
-	}
-	break;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (CharType &type) override
-  {
-    {
-      bool is_valid
-	= (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-      if (is_valid)
-	{
-	  resolved = type.clone ();
-	  return;
-	}
-
-      BaseRules::visit (type);
-    }
-  }
-
-  void visit (ReferenceType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (PointerType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (ParamType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (DynamicObjectType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-  void visit (ClosureType &type) override
-  {
-    bool is_valid
-      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
-    if (is_valid)
-      {
-	resolved = type.clone ();
-	return;
-      }
-
-    BaseRules::visit (type);
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  InferType *base;
-};
-
-class FnRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  FnRules (FnType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (FnType &type) override
-  {
-    if (base->num_params () != type.num_params ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    for (size_t i = 0; i < base->num_params (); i++)
-      {
-	auto a = base->param_at (i).second;
-	auto b = type.param_at (i).second;
-
-	auto unified_param = a->unify (b);
-	if (unified_param == nullptr)
-	  {
-	    BaseRules::visit (type);
-	    return;
-	  }
-      }
-
-    auto unified_return
-      = base->get_return_type ()->unify (type.get_return_type ());
-    if (unified_return == nullptr)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  FnType *base;
-};
-
-class FnptrRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  FnptrRules (FnPtr *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (FnPtr &type) override
-  {
-    auto this_ret_type = base->get_return_type ();
-    auto other_ret_type = type.get_return_type ();
-    auto unified_result = this_ret_type->unify (other_ret_type);
-    if (unified_result == nullptr
-	|| unified_result->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    if (base->num_params () != type.num_params ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    for (size_t i = 0; i < base->num_params (); i++)
-      {
-	auto this_param = base->param_at (i);
-	auto other_param = type.param_at (i);
-	auto unified_param = this_param->unify (other_param);
-	if (unified_param == nullptr
-	    || unified_param->get_kind () == TypeKind::ERROR)
-	  {
-	    BaseRules::visit (type);
-	    return;
-	  }
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (FnType &type) override
-  {
-    auto this_ret_type = base->get_return_type ();
-    auto other_ret_type = type.get_return_type ();
-    auto unified_result = this_ret_type->unify (other_ret_type);
-    if (unified_result == nullptr
-	|| unified_result->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    if (base->num_params () != type.num_params ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    for (size_t i = 0; i < base->num_params (); i++)
-      {
-	auto this_param = base->param_at (i);
-	auto other_param = type.param_at (i).second;
-	auto unified_param = this_param->unify (other_param);
-	if (unified_param == nullptr
-	    || unified_param->get_kind () == TypeKind::ERROR)
-	  {
-	    BaseRules::visit (type);
-	    return;
-	  }
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  FnPtr *base;
-};
-
-class ClosureRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ClosureRules (ClosureType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (ClosureType &type) override
-  {
-    if (base->get_def_id () != type.get_def_id ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    TyTy::BaseType *args_res
-      = base->get_parameters ().unify (&type.get_parameters ());
-    if (args_res == nullptr || args_res->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    TyTy::BaseType *res
-      = base->get_result_type ().unify (&type.get_result_type ());
-    if (res == nullptr || res->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ClosureType *base;
-};
-
-class ArrayRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ArrayRules (ArrayType *base) : BaseRules (base), base (base) {}
-
-  void visit (ArrayType &type) override
-  {
-    // check base type
-    auto base_resolved
-      = base->get_element_type ()->unify (type.get_element_type ());
-    if (base_resolved == nullptr)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved
-      = new ArrayType (type.get_ref (), type.get_ty_ref (),
-		       type.get_ident ().locus, type.get_capacity_expr (),
-		       TyVar (base_resolved->get_ref ()));
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ArrayType *base;
-};
-
-class SliceRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  SliceRules (SliceType *base) : BaseRules (base), base (base) {}
-
-  void visit (SliceType &type) override
-  {
-    // check base type
-    auto base_resolved
-      = base->get_element_type ()->unify (type.get_element_type ());
-    if (base_resolved == nullptr)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = new SliceType (type.get_ref (), type.get_ty_ref (),
-			      type.get_ident ().locus,
-			      TyVar (base_resolved->get_ref ()));
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  SliceType *base;
-};
-
-class BoolRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  BoolRules (BoolType *base) : BaseRules (base), base (base) {}
-
-  void visit (BoolType &type) override
-  {
-    resolved = new BoolType (type.get_ref (), type.get_ty_ref ());
-  }
-
-  void visit (InferType &type) override
-  {
-    switch (type.get_infer_kind ())
-      {
-      case InferType::InferTypeKind::GENERAL:
-	resolved = base->clone ();
-	break;
-
-      default:
-	BaseRules::visit (type);
-	break;
-      }
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  BoolType *base;
-};
-
-class IntRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  IntRules (IntType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    // cant assign a float inference variable
-    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (IntType &type) override
-  {
-    if (type.get_int_kind () != base->get_int_kind ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved
-      = new IntType (type.get_ref (), type.get_ty_ref (), type.get_int_kind ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  IntType *base;
-};
-
-class UintRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  UintRules (UintType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    // cant assign a float inference variable
-    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (UintType &type) override
-  {
-    if (type.get_uint_kind () != base->get_uint_kind ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = new UintType (type.get_ref (), type.get_ty_ref (),
-			     type.get_uint_kind ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  UintType *base;
-};
-
-class FloatRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  FloatRules (FloatType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (FloatType &type) override
-  {
-    if (type.get_float_kind () != base->get_float_kind ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = new FloatType (type.get_ref (), type.get_ty_ref (),
-			      type.get_float_kind ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  FloatType *base;
-};
-
-class ADTRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ADTRules (ADTType *base) : BaseRules (base), base (base) {}
-
-  void visit (ADTType &type) override
-  {
-    if (base->get_adt_kind () != type.get_adt_kind ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    if (base->get_identifier ().compare (type.get_identifier ()) != 0)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    if (base->number_of_variants () != type.number_of_variants ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    for (size_t i = 0; i < type.number_of_variants (); ++i)
-      {
-	TyTy::VariantDef *a = base->get_variants ().at (i);
-	TyTy::VariantDef *b = type.get_variants ().at (i);
-
-	if (a->num_fields () != b->num_fields ())
-	  {
-	    BaseRules::visit (type);
-	    return;
-	  }
-
-	for (size_t j = 0; j < a->num_fields (); j++)
-	  {
-	    TyTy::StructFieldType *base_field = a->get_field_at_index (j);
-	    TyTy::StructFieldType *other_field = b->get_field_at_index (j);
-
-	    TyTy::BaseType *this_field_ty = base_field->get_field_type ();
-	    TyTy::BaseType *other_field_ty = other_field->get_field_type ();
-
-	    BaseType *unified_ty = this_field_ty->unify (other_field_ty);
-	    if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
-	      return;
-	  }
-      }
-
-    // generic args for the unit-struct case
-    if (type.is_unit () && base->is_unit ())
-      {
-	rust_assert (type.get_num_substitutions ()
-		     == base->get_num_substitutions ());
-
-	for (size_t i = 0; i < type.get_num_substitutions (); i++)
-	  {
-	    auto &a = base->get_substs ().at (i);
-	    auto &b = type.get_substs ().at (i);
-
-	    auto pa = a.get_param_ty ();
-	    auto pb = b.get_param_ty ();
-
-	    auto res = pa->unify (pb);
-	    if (res->get_kind () == TyTy::TypeKind::ERROR)
-	      {
-		return;
-	      }
-	  }
-      }
-
-    resolved = type.clone ();
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ADTType *base;
-};
-
-class TupleRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  TupleRules (TupleType *base) : BaseRules (base), base (base) {}
-
-  void visit (TupleType &type) override
-  {
-    if (base->num_fields () != type.num_fields ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    std::vector<TyVar> fields;
-    for (size_t i = 0; i < base->num_fields (); i++)
-      {
-	BaseType *bo = base->get_field (i);
-	BaseType *fo = type.get_field (i);
-
-	BaseType *unified_ty = bo->unify (fo);
-	if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
-	  return;
-
-	fields.push_back (TyVar (unified_ty->get_ref ()));
-      }
-
-    resolved = new TyTy::TupleType (type.get_ref (), type.get_ty_ref (),
-				    type.get_ident ().locus, fields);
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  TupleType *base;
-};
-
-class USizeRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  USizeRules (USizeType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    // cant assign a float inference variable
-    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (USizeType &type) override { resolved = type.clone (); }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  USizeType *base;
-};
-
-class ISizeRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ISizeRules (ISizeType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    // cant assign a float inference variable
-    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (ISizeType &type) override { resolved = type.clone (); }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ISizeType *base;
-};
-
-class CharRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  CharRules (CharType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-  void visit (CharType &type) override { resolved = type.clone (); }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  CharType *base;
-};
-
-class ReferenceRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ReferenceRules (ReferenceType *base) : BaseRules (base), base (base) {}
-
-  void visit (ReferenceType &type) override
-  {
-    auto base_type = base->get_base ();
-    auto other_base_type = type.get_base ();
-
-    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
-    if (base_resolved == nullptr
-	|| base_resolved->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    // rust is permissive about mutablity here you can always go from mutable to
-    // immutable but not the otherway round
-    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
-    if (!mutability_ok)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = new ReferenceType (base->get_ref (), base->get_ty_ref (),
-				  TyVar (base_resolved->get_ref ()),
-				  base->mutability ());
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ReferenceType *base;
-};
-
-class PointerRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  PointerRules (PointerType *base) : BaseRules (base), base (base) {}
-
-  void visit (PointerType &type) override
-  {
-    auto base_type = base->get_base ();
-    auto other_base_type = type.get_base ();
-
-    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
-    if (base_resolved == nullptr
-	|| base_resolved->get_kind () == TypeKind::ERROR)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    // rust is permissive about mutablity here you can always go from mutable to
-    // immutable but not the otherway round
-    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
-    if (!mutability_ok)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = new PointerType (base->get_ref (), base->get_ty_ref (),
-				TyVar (base_resolved->get_ref ()),
-				base->mutability ());
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-    resolved->set_ref (type.get_ref ());
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  PointerType *base;
-};
-
-class ParamRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  ParamRules (ParamType *base) : BaseRules (base), base (base) {}
-
-  // param types are a placeholder we shouldn't have cases where we unify
-  // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
-  //
-  // foo<i32>{ a: 123 }.
-  // Then this enforces the i32 type to be referenced on the
-  // field via an hirid.
-  //
-  // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
-  // to handle the typing of the struct
-  BaseType *unify (BaseType *other) override final
-  {
-    if (!base->can_resolve ())
-      return BaseRules::unify (other);
-
-    auto lookup = base->resolve ();
-    return lookup->unify (other);
-  }
-
-  void visit (ParamType &type) override
-  {
-    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = type.clone ();
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  ParamType *base;
-};
-
-class StrRules : public BaseRules
-{
-  // FIXME we will need a enum for the StrType like ByteBuf etc..
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  StrRules (StrType *base) : BaseRules (base), base (base) {}
-
-  void visit (StrType &type) override { resolved = type.clone (); }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  StrType *base;
-};
-
-class NeverRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  NeverRules (NeverType *base) : BaseRules (base), base (base) {}
-
-  void visit (NeverType &type) override { resolved = type.clone (); }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  NeverType *base;
-};
-
-class PlaceholderRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  PlaceholderRules (PlaceholderType *base) : BaseRules (base), base (base) {}
-
-  BaseType *unify (BaseType *other) override final
-  {
-    if (!base->can_resolve ())
-      return BaseRules::unify (other);
-
-    BaseType *lookup = base->resolve ();
-    return lookup->unify (other);
-  }
-
-  void visit (PlaceholderType &type) override
-  {
-    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = type.clone ();
-  }
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  PlaceholderType *base;
-};
-
-class DynamicRules : public BaseRules
-{
-  using Rust::TyTy::BaseRules::visit;
-
-public:
-  DynamicRules (DynamicObjectType *base) : BaseRules (base), base (base) {}
-
-  void visit (InferType &type) override
-  {
-    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-  }
-
-  void visit (DynamicObjectType &type) override
-  {
-    if (base->num_specified_bounds () != type.num_specified_bounds ())
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    Location ref_locus = mappings->lookup_location (type.get_ref ());
-    if (!base->bounds_compatible (type, ref_locus, true))
-      {
-	BaseRules::visit (type);
-	return;
-      }
-
-    resolved = base->clone ();
-  }
-
-private:
-  BaseType *get_base () override { return base; }
-
-  DynamicObjectType *base;
-};
-
-} // namespace TyTy
-} // namespace Rust
-
-#endif // RUST_TYTY_RULES
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index a3271eb0d1a..fe5aa2b059c 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -30,7 +30,6 @@
 #include "rust-hir-trait-reference.h"
 #include "rust-hir-type-bounds.h"
 #include "rust-hir-trait-resolve.h"
-#include "rust-tyty-rules.h"
 #include "rust-tyty-cmp.h"
 
 #include "options.h"
@@ -376,6 +375,58 @@ BaseType::get_root () const
   return root;
 }
 
+BaseType *
+BaseType::destructure ()
+{
+  int recurisve_ops = 0;
+  BaseType *x = this;
+  while (true)
+    {
+      if (recurisve_ops++ >= rust_max_recursion_depth)
+	{
+	  rust_error_at (
+	    Location (),
+	    "%<recursion depth%> count exceeds limit of %i (use "
+	    "%<frust-max-recursion-depth=%> to increase the limit)",
+	    rust_max_recursion_depth);
+	  return new ErrorType (get_ref ());
+	}
+
+      switch (x->get_kind ())
+	{
+	  case TyTy::TypeKind::PARAM: {
+	    TyTy::ParamType *p = static_cast<TyTy::ParamType *> (x);
+	    TyTy::BaseType *pr = p->resolve ();
+	    if (pr == x)
+	      return pr;
+
+	    x = pr;
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PLACEHOLDER: {
+	    TyTy::PlaceholderType *p = static_cast<TyTy::PlaceholderType *> (x);
+	    if (!p->can_resolve ())
+	      return p;
+
+	    x = p->resolve ();
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PROJECTION: {
+	    TyTy::ProjectionType *p = static_cast<TyTy::ProjectionType *> (x);
+	    x = p->get ();
+	  }
+	  break;
+
+	default:
+	  return x;
+	}
+    }
+
+  return x;
+}
+
 const BaseType *
 BaseType::destructure () const
 {
@@ -397,14 +448,20 @@ BaseType::destructure () const
 	{
 	  case TyTy::TypeKind::PARAM: {
 	    const TyTy::ParamType *p = static_cast<const TyTy::ParamType *> (x);
-	    x = p->resolve ();
+	    const TyTy::BaseType *pr = p->resolve ();
+	    if (pr == x)
+	      return pr;
+
+	    x = pr;
 	  }
 	  break;
 
 	  case TyTy::TypeKind::PLACEHOLDER: {
 	    const TyTy::PlaceholderType *p
 	      = static_cast<const TyTy::PlaceholderType *> (x);
-	    rust_assert (p->can_resolve ());
+	    if (!p->can_resolve ())
+	      return p;
+
 	    x = p->resolve ();
 	  }
 	  break;
@@ -512,13 +569,6 @@ InferType::as_string () const
   return "<infer::error>";
 }
 
-BaseType *
-InferType::unify (BaseType *other)
-{
-  InferRules r (this);
-  return r.unify (other);
-}
-
 bool
 InferType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -637,12 +687,6 @@ ErrorType::as_string () const
   return "<tyty::error>";
 }
 
-BaseType *
-ErrorType::unify (BaseType *other)
-{
-  return this;
-}
-
 bool
 ErrorType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1009,13 +1053,6 @@ ADTType::as_string () const
   return identifier + subst_as_string () + "{" + variants_buffer + "}";
 }
 
-BaseType *
-ADTType::unify (BaseType *other)
-{
-  ADTRules r (this);
-  return r.unify (other);
-}
-
 bool
 ADTType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1278,13 +1315,6 @@ TupleType::get_field (size_t index) const
   return fields.at (index).get_tyty ();
 }
 
-BaseType *
-TupleType::unify (BaseType *other)
-{
-  TupleRules r (this);
-  return r.unify (other);
-}
-
 bool
 TupleType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1385,13 +1415,6 @@ FnType::as_string () const
   return "fn" + subst_as_string () + " (" + params_str + ") -> " + ret_str;
 }
 
-BaseType *
-FnType::unify (BaseType *other)
-{
-  FnRules r (this);
-  return r.unify (other);
-}
-
 bool
 FnType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1616,13 +1639,6 @@ FnPtr::as_string () const
   return "fnptr (" + params_str + ") -> " + get_return_type ()->as_string ();
 }
 
-BaseType *
-FnPtr::unify (BaseType *other)
-{
-  FnptrRules r (this);
-  return r.unify (other);
-}
-
 bool
 FnPtr::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1696,13 +1712,6 @@ ClosureType::as_string () const
   return "|" + params_buf + "| {" + result_type.get_tyty ()->as_string () + "}";
 }
 
-BaseType *
-ClosureType::unify (BaseType *other)
-{
-  ClosureRules r (this);
-  return r.unify (other);
-}
-
 bool
 ClosureType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1812,13 +1821,6 @@ ArrayType::as_string () const
   return "[" + get_element_type ()->as_string () + ":" + "CAPACITY" + "]";
 }
 
-BaseType *
-ArrayType::unify (BaseType *other)
-{
-  ArrayRules r (this);
-  return r.unify (other);
-}
-
 bool
 ArrayType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -1895,13 +1897,6 @@ SliceType::as_string () const
   return "[" + get_element_type ()->as_string () + "]";
 }
 
-BaseType *
-SliceType::unify (BaseType *other)
-{
-  SliceRules r (this);
-  return r.unify (other);
-}
-
 bool
 SliceType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2006,13 +2001,6 @@ BoolType::as_string () const
   return "bool";
 }
 
-BaseType *
-BoolType::unify (BaseType *other)
-{
-  BoolRules r (this);
-  return r.unify (other);
-}
-
 bool
 BoolType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2094,13 +2082,6 @@ IntType::as_string () const
   return "__unknown_int_type";
 }
 
-BaseType *
-IntType::unify (BaseType *other)
-{
-  IntRules r (this);
-  return r.unify (other);
-}
-
 bool
 IntType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2200,13 +2181,6 @@ UintType::as_string () const
   return "__unknown_uint_type";
 }
 
-BaseType *
-UintType::unify (BaseType *other)
-{
-  UintRules r (this);
-  return r.unify (other);
-}
-
 bool
 UintType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2306,13 +2280,6 @@ FloatType::as_string () const
   return "__unknown_float_type";
 }
 
-BaseType *
-FloatType::unify (BaseType *other)
-{
-  FloatRules r (this);
-  return r.unify (other);
-}
-
 bool
 FloatType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2389,13 +2356,6 @@ USizeType::as_string () const
   return "usize";
 }
 
-BaseType *
-USizeType::unify (BaseType *other)
-{
-  USizeRules r (this);
-  return r.unify (other);
-}
-
 bool
 USizeType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2461,13 +2421,6 @@ ISizeType::as_string () const
   return "isize";
 }
 
-BaseType *
-ISizeType::unify (BaseType *other)
-{
-  ISizeRules r (this);
-  return r.unify (other);
-}
-
 bool
 ISizeType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2533,13 +2486,6 @@ CharType::as_string () const
   return "char";
 }
 
-BaseType *
-CharType::unify (BaseType *other)
-{
-  CharRules r (this);
-  return r.unify (other);
-}
-
 bool
 CharType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2655,13 +2601,6 @@ ReferenceType::get_name () const
 	 + get_base ()->get_name ();
 }
 
-BaseType *
-ReferenceType::unify (BaseType *other)
-{
-  ReferenceRules r (this);
-  return r.unify (other);
-}
-
 bool
 ReferenceType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2821,13 +2760,6 @@ PointerType::get_name () const
 	 + get_base ()->get_name ();
 }
 
-BaseType *
-PointerType::unify (BaseType *other)
-{
-  PointerRules r (this);
-  return r.unify (other);
-}
-
 bool
 PointerType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -2964,13 +2896,6 @@ ParamType::get_name () const
   return resolve ()->get_name ();
 }
 
-BaseType *
-ParamType::unify (BaseType *other)
-{
-  ParamRules r (this);
-  return r.unify (other);
-}
-
 bool
 ParamType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -3130,13 +3055,6 @@ StrType::as_string () const
   return "str";
 }
 
-BaseType *
-StrType::unify (BaseType *other)
-{
-  StrRules r (this);
-  return r.unify (other);
-}
-
 bool
 StrType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -3202,13 +3120,6 @@ NeverType::as_string () const
   return "!";
 }
 
-BaseType *
-NeverType::unify (BaseType *other)
-{
-  NeverRules r (this);
-  return r.unify (other);
-}
-
 bool
 NeverType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -3295,13 +3206,6 @@ PlaceholderType::as_string () const
 	 + ">";
 }
 
-BaseType *
-PlaceholderType::unify (BaseType *other)
-{
-  PlaceholderRules r (this);
-  return r.unify (other);
-}
-
 bool
 PlaceholderType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -3465,12 +3369,6 @@ ProjectionType::as_string () const
   return "<Projection=" + subst_as_string () + "::" + base->as_string () + ">";
 }
 
-BaseType *
-ProjectionType::unify (BaseType *other)
-{
-  return base->unify (other);
-}
-
 bool
 ProjectionType::can_eq (const BaseType *other, bool emit_errors) const
 {
@@ -3602,13 +3500,6 @@ DynamicObjectType::as_string () const
   return "dyn [" + raw_bounds_as_string () + "]";
 }
 
-BaseType *
-DynamicObjectType::unify (BaseType *other)
-{
-  DynamicRules r (this);
-  return r.unify (other);
-}
-
 bool
 DynamicObjectType::can_eq (const BaseType *other, bool emit_errors) const
 {
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 1cf7131c1cb..d2cf5b07fc1 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -105,11 +105,6 @@ public:
 
   virtual std::string get_name () const = 0;
 
-  // Unify two types. Returns a pointer to the newly-created unified ty, or
-  // nullptr if the two ty cannot be unified. The caller is responsible for
-  // releasing the memory of the returned ty.
-  virtual BaseType *unify (BaseType *other) = 0;
-
   // similar to unify but does not actually perform type unification but
   // determines whether they are compatible. Consider the following
   //
@@ -177,6 +172,7 @@ public:
 
   // This will get the monomorphized type from Params, Placeholders or
   // Projections if available or error
+  BaseType *destructure ();
   const BaseType *destructure () const;
 
   const RustIdent &get_ident () const;
@@ -221,8 +217,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
-
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -255,7 +249,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -284,7 +277,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -361,7 +353,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -598,7 +589,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -738,7 +728,6 @@ public:
 
   std::string get_identifier () const { return identifier; }
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -858,7 +847,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -938,7 +926,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final { return as_string (); }
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1007,7 +994,6 @@ public:
 
   std::string get_name () const override final { return as_string (); }
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1055,7 +1041,6 @@ public:
 
   std::string get_name () const override final { return as_string (); }
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1089,7 +1074,6 @@ public:
 
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1120,7 +1104,6 @@ public:
 
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   IntKind get_int_kind () const;
@@ -1159,7 +1142,6 @@ public:
 
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   UintKind get_uint_kind () const;
@@ -1194,7 +1176,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   FloatKind get_float_kind () const;
@@ -1222,7 +1203,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1243,7 +1223,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1263,7 +1242,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1284,7 +1262,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1311,7 +1288,6 @@ public:
 
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1354,7 +1330,6 @@ public:
   std::string as_string () const override;
   std::string get_name () const override final;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
@@ -1401,7 +1376,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1428,7 +1402,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1478,7 +1451,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   BaseType *clone () const final override;
@@ -1524,7 +1496,6 @@ public:
 
   std::string as_string () const override;
 
-  BaseType *unify (BaseType *other) override;
   bool can_eq (const BaseType *other, bool emit_errors) const override final;
 
   bool is_equal (const BaseType &other) const override;
diff --git a/gcc/rust/typecheck/rust-unify.cc b/gcc/rust/typecheck/rust-unify.cc
new file mode 100644
index 00000000000..072f76133bc
--- /dev/null
+++ b/gcc/rust/typecheck/rust-unify.cc
@@ -0,0 +1,1640 @@
+// 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-unify.h"
+
+namespace Rust {
+namespace Resolver {
+
+UnifyRules::UnifyRules (TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
+			Location locus, bool commit_flag, bool emit_error)
+  : lhs (lhs), rhs (rhs), locus (locus), commit_flag (commit_flag),
+    emit_error (emit_error), mappings (*Analysis::Mappings::get ()),
+    context (*TypeCheckContext::get ())
+{}
+
+TyTy::BaseType *
+UnifyRules::Resolve (TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
+		     Location locus, bool commit_flag, bool emit_error)
+{
+  UnifyRules r (lhs, rhs, locus, commit_flag, emit_error);
+  TyTy::BaseType *result = r.go ();
+
+  if (r.commit_flag)
+    r.commit (result);
+
+  bool failed = result->get_kind () == TyTy::TypeKind::ERROR;
+  if (failed && r.emit_error)
+    r.emit_type_mismatch ();
+
+  return result;
+}
+
+TyTy::BaseType *
+UnifyRules::get_base ()
+{
+  return lhs.get_ty ()->destructure ();
+}
+
+TyTy::BaseType *
+UnifyRules::get_other ()
+{
+  return rhs.get_ty ()->destructure ();
+}
+
+void
+UnifyRules::commit (TyTy::BaseType *resolved)
+{
+  resolved->append_reference (get_base ()->get_ref ());
+  resolved->append_reference (get_other ()->get_ref ());
+  for (auto ref : get_base ()->get_combined_refs ())
+    resolved->append_reference (ref);
+  for (auto ref : get_other ()->get_combined_refs ())
+    resolved->append_reference (ref);
+
+  get_other ()->append_reference (resolved->get_ref ());
+  get_other ()->append_reference (get_base ()->get_ref ());
+  get_base ()->append_reference (resolved->get_ref ());
+  get_base ()->append_reference (get_other ()->get_ref ());
+
+  bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER;
+  bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER;
+  bool results_is_non_general_infer_var
+    = (result_is_infer_var
+       && (static_cast<TyTy::InferType *> (resolved))->get_infer_kind ()
+	    != TyTy::InferType::GENERAL);
+  if (result_resolved || results_is_non_general_infer_var)
+    {
+      for (auto &ref : resolved->get_combined_refs ())
+	{
+	  TyTy::BaseType *ref_tyty = nullptr;
+	  bool ok = context.lookup_type (ref, &ref_tyty);
+	  if (!ok)
+	    continue;
+
+	  // if any of the types are inference variables lets fix them
+	  if (ref_tyty->get_kind () == TyTy::TypeKind::INFER)
+	    {
+	      auto node = Analysis::NodeMapping (mappings.get_current_crate (),
+						 UNKNOWN_NODEID, ref,
+						 UNKNOWN_LOCAL_DEFID);
+	      context.insert_type (node, resolved->clone ());
+	    }
+	}
+    }
+}
+
+void
+UnifyRules::emit_type_mismatch () const
+{
+  TyTy::BaseType *expected = lhs.get_ty ();
+  TyTy::BaseType *expr = rhs.get_ty ();
+
+  RichLocation r (locus);
+  r.add_range (lhs.get_locus ());
+  r.add_range (rhs.get_locus ());
+  rust_error_at (r, "expected %<%s%> got %<%s%>",
+		 expected->get_name ().c_str (), expr->get_name ().c_str ());
+}
+
+TyTy::BaseType *
+UnifyRules::go ()
+{
+  TyTy::BaseType *ltype = lhs.get_ty ();
+  TyTy::BaseType *rtype = rhs.get_ty ();
+
+  ltype = lhs.get_ty ()->destructure ();
+  rtype = rhs.get_ty ()->destructure ();
+
+  rust_debug ("unify::go ltype={%s} rtype={%s}", ltype->debug_str ().c_str (),
+	      rtype->debug_str ().c_str ());
+
+  switch (ltype->get_kind ())
+    {
+    case TyTy::INFER:
+      return expect_inference_variable (static_cast<TyTy::InferType *> (ltype),
+					rtype);
+
+    case TyTy::ADT:
+      return expect_adt (static_cast<TyTy::ADTType *> (ltype), rtype);
+
+    case TyTy::STR:
+      return expect_str (static_cast<TyTy::StrType *> (ltype), rtype);
+
+    case TyTy::REF:
+      return expect_reference (static_cast<TyTy::ReferenceType *> (ltype),
+			       rtype);
+
+    case TyTy::POINTER:
+      return expect_pointer (static_cast<TyTy::PointerType *> (ltype), rtype);
+
+    case TyTy::PARAM:
+      return expect_param (static_cast<TyTy::ParamType *> (ltype), rtype);
+
+    case TyTy::ARRAY:
+      return expect_array (static_cast<TyTy::ArrayType *> (ltype), rtype);
+
+    case TyTy::SLICE:
+      return expect_slice (static_cast<TyTy::SliceType *> (ltype), rtype);
+
+    case TyTy::FNDEF:
+      return expect_fndef (static_cast<TyTy::FnType *> (ltype), rtype);
+
+    case TyTy::FNPTR:
+      return expect_fnptr (static_cast<TyTy::FnPtr *> (ltype), rtype);
+
+    case TyTy::TUPLE:
+      return expect_tuple (static_cast<TyTy::TupleType *> (ltype), rtype);
+
+    case TyTy::BOOL:
+      return expect_bool (static_cast<TyTy::BoolType *> (ltype), rtype);
+
+    case TyTy::CHAR:
+      return expect_char (static_cast<TyTy::CharType *> (ltype), rtype);
+
+    case TyTy::INT:
+      return expect_int (static_cast<TyTy::IntType *> (ltype), rtype);
+
+    case TyTy::UINT:
+      return expect_uint (static_cast<TyTy::UintType *> (ltype), rtype);
+
+    case TyTy::FLOAT:
+      return expect_float (static_cast<TyTy::FloatType *> (ltype), rtype);
+
+    case TyTy::USIZE:
+      return expect_usize (static_cast<TyTy::USizeType *> (ltype), rtype);
+
+    case TyTy::ISIZE:
+      return expect_isize (static_cast<TyTy::ISizeType *> (ltype), rtype);
+
+    case TyTy::NEVER:
+      return expect_never (static_cast<TyTy::NeverType *> (ltype), rtype);
+
+    case TyTy::PLACEHOLDER:
+      return expect_placeholder (static_cast<TyTy::PlaceholderType *> (ltype),
+				 rtype);
+
+    case TyTy::PROJECTION:
+      return expect_projection (static_cast<TyTy::ProjectionType *> (ltype),
+				rtype);
+
+    case TyTy::DYNAMIC:
+      return expect_dyn (static_cast<TyTy::DynamicObjectType *> (ltype), rtype);
+
+    case TyTy::CLOSURE:
+      return expect_closure (static_cast<TyTy::ClosureType *> (ltype), rtype);
+
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_inference_variable (TyTy::InferType *ltype,
+				       TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	switch (ltype->get_infer_kind ())
+	  {
+	  case TyTy::InferType::InferTypeKind::GENERAL:
+	    return rtype->clone ();
+
+	    case TyTy::InferType::InferTypeKind::INTEGRAL: {
+	      bool is_valid = r->get_infer_kind ()
+				== TyTy::InferType::InferTypeKind::INTEGRAL
+			      || r->get_infer_kind ()
+				   == TyTy::InferType::InferTypeKind::GENERAL;
+	      if (is_valid)
+		return rtype->clone ();
+	    }
+	    break;
+
+	    case TyTy::InferType::InferTypeKind::FLOAT: {
+	      bool is_valid
+		= r->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT
+		  || r->get_infer_kind ()
+		       == TyTy::InferType::InferTypeKind::GENERAL;
+	      if (is_valid)
+		return rtype->clone ();
+	    }
+	    break;
+	  }
+      }
+      break;
+
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+      case TyTy::ISIZE: {
+	bool is_valid = (ltype->get_infer_kind ()
+			 == TyTy::InferType::InferTypeKind::GENERAL)
+			|| (ltype->get_infer_kind ()
+			    == TyTy::InferType::InferTypeKind::INTEGRAL);
+	if (is_valid)
+	  return rtype->clone ();
+      }
+      break;
+
+      case TyTy::FLOAT: {
+	bool is_valid = (ltype->get_infer_kind ()
+			 == TyTy::InferType::InferTypeKind::GENERAL)
+			|| (ltype->get_infer_kind ()
+			    == TyTy::InferType::InferTypeKind::FLOAT);
+	if (is_valid)
+	  return rtype->clone ();
+      }
+      break;
+
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+      case TyTy::CLOSURE: {
+	bool is_valid = (ltype->get_infer_kind ()
+			 == TyTy::InferType::InferTypeKind::GENERAL);
+	if (is_valid)
+	  return rtype->clone ();
+      }
+      break;
+
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_adt (TyTy::ADTType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::ADT: {
+	TyTy::ADTType &type = *static_cast<TyTy::ADTType *> (rtype);
+	if (ltype->get_adt_kind () != type.get_adt_kind ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	if (ltype->get_identifier ().compare (type.get_identifier ()) != 0)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	if (ltype->number_of_variants () != type.number_of_variants ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	for (size_t i = 0; i < type.number_of_variants (); ++i)
+	  {
+	    TyTy::VariantDef *a = ltype->get_variants ().at (i);
+	    TyTy::VariantDef *b = type.get_variants ().at (i);
+
+	    if (a->num_fields () != b->num_fields ())
+	      {
+		return new TyTy::ErrorType (0);
+	      }
+
+	    for (size_t j = 0; j < a->num_fields (); j++)
+	      {
+		TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+		TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+		TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+		TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+		TyTy::BaseType *unified_ty
+		  = UnifyRules::Resolve (TyTy::TyWithLocation (this_field_ty),
+					 TyTy::TyWithLocation (other_field_ty),
+					 locus, commit_flag,
+					 false /* emit_error */);
+		if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+		  {
+		    return new TyTy::ErrorType (0);
+		  }
+	      }
+	  }
+
+	// generic args for the unit-struct case
+	if (type.is_unit () && ltype->is_unit ())
+	  {
+	    rust_assert (type.get_num_substitutions ()
+			 == ltype->get_num_substitutions ());
+
+	    for (size_t i = 0; i < type.get_num_substitutions (); i++)
+	      {
+		auto &a = ltype->get_substs ().at (i);
+		auto &b = type.get_substs ().at (i);
+
+		auto pa = a.get_param_ty ();
+		auto pb = b.get_param_ty ();
+
+		auto res
+		  = UnifyRules::Resolve (TyTy::TyWithLocation (pa),
+					 TyTy::TyWithLocation (pb), locus,
+					 commit_flag, false /* emit_error */);
+		if (res->get_kind () == TyTy::TypeKind::ERROR)
+		  {
+		    return new TyTy::ErrorType (0);
+		  }
+	      }
+	  }
+
+	return type.clone ();
+      }
+      break;
+
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_str (TyTy::StrType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::STR:
+      return rtype->clone ();
+
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_reference (TyTy::ReferenceType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::REF: {
+	TyTy::ReferenceType &type = *static_cast<TyTy::ReferenceType *> (rtype);
+	auto base_type = ltype->get_base ();
+	auto other_base_type = type.get_base ();
+
+	TyTy::BaseType *base_resolved
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (base_type),
+				 TyTy::TyWithLocation (other_base_type), locus,
+				 commit_flag, false /* emit_error */);
+	if (base_resolved->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	// rust is permissive about mutablity here you can always go from
+	// mutable to immutable but not the otherway round
+	bool mutability_ok = ltype->is_mutable () ? type.is_mutable () : true;
+	if (!mutability_ok)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return new TyTy::ReferenceType (ltype->get_ref (), ltype->get_ty_ref (),
+					TyTy::TyVar (base_resolved->get_ref ()),
+					ltype->mutability ());
+      }
+      break;
+
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_pointer (TyTy::PointerType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::POINTER: {
+	TyTy::PointerType &type = *static_cast<TyTy::PointerType *> (rtype);
+	auto base_type = ltype->get_base ();
+	auto other_base_type = type.get_base ();
+
+	TyTy::BaseType *base_resolved
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (base_type),
+				 TyTy::TyWithLocation (other_base_type), locus,
+				 commit_flag, false /* emit_error */);
+	if (base_resolved->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	// rust is permissive about mutablity here you can always go from
+	// mutable to immutable but not the otherway round
+	bool mutability_ok = ltype->is_mutable () ? type.is_mutable () : true;
+	if (!mutability_ok)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return new TyTy::PointerType (ltype->get_ref (), ltype->get_ty_ref (),
+				      TyTy::TyVar (base_resolved->get_ref ()),
+				      ltype->mutability ());
+      }
+      break;
+
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_param (TyTy::ParamType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::PARAM: {
+	TyTy::ParamType &type = *static_cast<TyTy::ParamType *> (rtype);
+	// bool symbol_matches
+	//   = ltype->get_symbol ().compare (type.get_symbol ()) == 0;
+	// // TODO
+	// // I think rustc checks a debruinj index
+	// if (symbol_matches)
+	//   {
+	//     return type.clone ();
+	//   }
+
+	// matching symbol is not going to work when we mix symbol's and have
+	// nested generics
+
+	// bounds match? FIXME
+
+	return type.clone ();
+      }
+      break;
+
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_array (TyTy::ArrayType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::ARRAY: {
+	TyTy::ArrayType &type = *static_cast<TyTy::ArrayType *> (rtype);
+	TyTy::BaseType *element_unify = UnifyRules::Resolve (
+	  TyTy::TyWithLocation (ltype->get_element_type ()),
+	  TyTy::TyWithLocation (type.get_element_type ()), locus, commit_flag,
+	  false /* emit_error*/);
+
+	if (element_unify->get_kind () != TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ArrayType (type.get_ref (), type.get_ty_ref (),
+					type.get_ident ().locus,
+					type.get_capacity_expr (),
+					TyTy::TyVar (
+					  element_unify->get_ref ()));
+	  }
+      }
+      break;
+
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_slice (TyTy::SliceType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::SLICE: {
+	TyTy::SliceType &type = *static_cast<TyTy::SliceType *> (rtype);
+	TyTy::BaseType *element_unify = UnifyRules::Resolve (
+	  TyTy::TyWithLocation (ltype->get_element_type ()),
+	  TyTy::TyWithLocation (type.get_element_type ()), locus, commit_flag,
+	  false /* emit_error*/);
+
+	if (element_unify->get_kind () != TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::SliceType (type.get_ref (), type.get_ty_ref (),
+					type.get_ident ().locus,
+					TyTy::TyVar (
+					  element_unify->get_ref ()));
+	  }
+      }
+      break;
+
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_fndef (TyTy::FnType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::FNDEF: {
+	TyTy::FnType &type = *static_cast<TyTy::FnType *> (rtype);
+	if (ltype->num_params () != type.num_params ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	for (size_t i = 0; i < ltype->num_params (); i++)
+	  {
+	    auto a = ltype->param_at (i).second;
+	    auto b = type.param_at (i).second;
+
+	    auto unified_param
+	      = UnifyRules::Resolve (TyTy::TyWithLocation (a),
+				     TyTy::TyWithLocation (b), locus,
+				     commit_flag, false /* emit_errors */);
+	    if (unified_param->get_kind () == TyTy::TypeKind::ERROR)
+	      {
+		return new TyTy::ErrorType (0);
+	      }
+	  }
+
+	auto unified_return
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (
+				   ltype->get_return_type ()),
+				 TyTy::TyWithLocation (type.get_return_type ()),
+				 locus, commit_flag, false /* emit_errors */);
+	if (unified_return->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return ltype->clone ();
+      }
+      break;
+
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNPTR:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_fnptr (TyTy::FnPtr *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::FNPTR: {
+	TyTy::FnPtr &type = *static_cast<TyTy::FnPtr *> (rtype);
+	if (ltype->num_params () != type.num_params ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	for (size_t i = 0; i < ltype->num_params (); i++)
+	  {
+	    auto a = ltype->param_at (i);
+	    auto b = type.param_at (i);
+
+	    auto unified_param
+	      = UnifyRules::Resolve (TyTy::TyWithLocation (a),
+				     TyTy::TyWithLocation (b), locus,
+				     commit_flag, false /* emit_errors */);
+	    if (unified_param->get_kind () == TyTy::TypeKind::ERROR)
+	      {
+		return new TyTy::ErrorType (0);
+	      }
+	  }
+
+	auto unified_return
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (
+				   ltype->get_return_type ()),
+				 TyTy::TyWithLocation (type.get_return_type ()),
+				 locus, commit_flag, false /* emit_errors */);
+	if (unified_return->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return ltype->clone ();
+      }
+      break;
+
+      case TyTy::FNDEF: {
+	TyTy::FnType &type = *static_cast<TyTy::FnType *> (rtype);
+	auto this_ret_type = ltype->get_return_type ();
+	auto other_ret_type = type.get_return_type ();
+
+	auto unified_result
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (this_ret_type),
+				 TyTy::TyWithLocation (other_ret_type), locus,
+				 commit_flag, false /*emit_errors*/);
+	if (unified_result->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	if (ltype->num_params () != type.num_params ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	for (size_t i = 0; i < ltype->num_params (); i++)
+	  {
+	    auto this_param = ltype->param_at (i);
+	    auto other_param = type.param_at (i).second;
+
+	    auto unified_param
+	      = UnifyRules::Resolve (TyTy::TyWithLocation (this_param),
+				     TyTy::TyWithLocation (other_param), locus,
+				     commit_flag, false /* emit_errors */);
+	    if (unified_param->get_kind () == TyTy::TypeKind::ERROR)
+	      {
+		return new TyTy::ErrorType (0);
+	      }
+	  }
+
+	return ltype->clone ();
+      }
+      break;
+
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_tuple (TyTy::TupleType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::TUPLE: {
+	TyTy::TupleType &type = *static_cast<TyTy::TupleType *> (rtype);
+	if (ltype->num_fields () != type.num_fields ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	std::vector<TyTy::TyVar> fields;
+	for (size_t i = 0; i < ltype->num_fields (); i++)
+	  {
+	    TyTy::BaseType *bo = ltype->get_field (i);
+	    TyTy::BaseType *fo = type.get_field (i);
+
+	    TyTy::BaseType *unified_ty
+	      = UnifyRules::Resolve (TyTy::TyWithLocation (bo),
+				     TyTy::TyWithLocation (fo), locus,
+				     commit_flag, false /* emit_errors */);
+	    if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+	      return new TyTy::ErrorType (0);
+
+	    fields.push_back (TyTy::TyVar (unified_ty->get_ref ()));
+	  }
+
+	return new TyTy::TupleType (type.get_ref (), type.get_ty_ref (),
+				    type.get_ident ().locus, fields);
+      }
+      break;
+
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_bool (TyTy::BoolType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::BOOL:
+      return rtype->clone ();
+
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_char (TyTy::CharType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::CHAR:
+      return rtype->clone ();
+
+    case TyTy::INT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_int (TyTy::IntType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL
+	    || r->get_infer_kind () == TyTy::InferType::InferTypeKind::INTEGRAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::INT: {
+	TyTy::IntType &type = *static_cast<TyTy::IntType *> (rtype);
+	bool is_valid = ltype->get_int_kind () == type.get_int_kind ();
+	if (is_valid)
+	  return new TyTy::IntType (type.get_ref (), type.get_ty_ref (),
+				    type.get_int_kind ());
+      }
+      break;
+
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_uint (TyTy::UintType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL
+	    || r->get_infer_kind () == TyTy::InferType::InferTypeKind::INTEGRAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::UINT: {
+	TyTy::UintType &type = *static_cast<TyTy::UintType *> (rtype);
+	bool is_valid = ltype->get_uint_kind () == type.get_uint_kind ();
+	if (is_valid)
+	  return new TyTy::UintType (type.get_ref (), type.get_ty_ref (),
+				     type.get_uint_kind ());
+      }
+      break;
+
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_float (TyTy::FloatType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL
+	    || r->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::FLOAT: {
+	TyTy::FloatType &type = *static_cast<TyTy::FloatType *> (rtype);
+	bool is_valid = ltype->get_float_kind () == type.get_float_kind ();
+	if (is_valid)
+	  return new TyTy::FloatType (type.get_ref (), type.get_ty_ref (),
+				      type.get_float_kind ());
+      }
+      break;
+
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_isize (TyTy::ISizeType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () != TyTy::InferType::InferTypeKind::FLOAT;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::ISIZE:
+      return rtype->clone ();
+
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_usize (TyTy::USizeType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () != TyTy::InferType::InferTypeKind::FLOAT;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::USIZE:
+      return rtype->clone ();
+
+    case TyTy::ADT:
+    case TyTy::STR:
+    case TyTy::REF:
+    case TyTy::POINTER:
+    case TyTy::PARAM:
+    case TyTy::ARRAY:
+    case TyTy::SLICE:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_never (TyTy::NeverType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+    case TyTy::NEVER:
+      return rtype->clone ();
+
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::SLICE:
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_placeholder (TyTy::PlaceholderType *ltype,
+				TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::PLACEHOLDER: {
+	TyTy::PlaceholderType &type
+	  = *static_cast<TyTy::PlaceholderType *> (rtype);
+	bool symbol_match
+	  = ltype->get_symbol ().compare (type.get_symbol ()) == 0;
+	if (symbol_match)
+	  {
+	    return type.clone ();
+	  }
+      }
+      break;
+
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::SLICE:
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_projection (TyTy::ProjectionType *ltype,
+			       TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      // FIXME
+    case TyTy::PROJECTION:
+      gcc_unreachable ();
+      break;
+
+    case TyTy::DYNAMIC:
+    case TyTy::CLOSURE:
+    case TyTy::SLICE:
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_dyn (TyTy::DynamicObjectType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::DYNAMIC: {
+	TyTy::DynamicObjectType &type
+	  = *static_cast<TyTy::DynamicObjectType *> (rtype);
+	if (ltype->num_specified_bounds () != type.num_specified_bounds ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	if (!ltype->bounds_compatible (type, locus, true))
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return ltype->clone ();
+      }
+      break;
+
+    case TyTy::CLOSURE:
+    case TyTy::SLICE:
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+TyTy::BaseType *
+UnifyRules::expect_closure (TyTy::ClosureType *ltype, TyTy::BaseType *rtype)
+{
+  switch (rtype->get_kind ())
+    {
+      case TyTy::INFER: {
+	TyTy::InferType *r = static_cast<TyTy::InferType *> (rtype);
+	bool is_valid
+	  = r->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL;
+	if (is_valid)
+	  return ltype->clone ();
+      }
+      break;
+
+      case TyTy::CLOSURE: {
+	TyTy::ClosureType &type = *static_cast<TyTy::ClosureType *> (rtype);
+	if (ltype->get_def_id () != type.get_def_id ())
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	TyTy::BaseType *args_res
+	  = UnifyRules::Resolve (TyTy::TyWithLocation (
+				   &ltype->get_parameters ()),
+				 TyTy::TyWithLocation (&type.get_parameters ()),
+				 locus, commit_flag, false /* emit_error */);
+	if (args_res->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	TyTy::BaseType *res = UnifyRules::Resolve (
+	  TyTy::TyWithLocation (&ltype->get_result_type ()),
+	  TyTy::TyWithLocation (&type.get_result_type ()), locus, commit_flag,
+	  false /* emit_error */);
+	if (res == nullptr || res->get_kind () == TyTy::TypeKind::ERROR)
+	  {
+	    return new TyTy::ErrorType (0);
+	  }
+
+	return ltype->clone ();
+      }
+      break;
+
+    case TyTy::SLICE:
+    case TyTy::PARAM:
+    case TyTy::POINTER:
+    case TyTy::STR:
+    case TyTy::ADT:
+    case TyTy::REF:
+    case TyTy::ARRAY:
+    case TyTy::FNDEF:
+    case TyTy::FNPTR:
+    case TyTy::TUPLE:
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::NEVER:
+    case TyTy::PLACEHOLDER:
+    case TyTy::PROJECTION:
+    case TyTy::DYNAMIC:
+    case TyTy::ERROR:
+      return new TyTy::ErrorType (0);
+    }
+  return new TyTy::ErrorType (0);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-unify.h b/gcc/rust/typecheck/rust-unify.h
new file mode 100644
index 00000000000..6e46b9642a6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-unify.h
@@ -0,0 +1,91 @@
+// 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_UNIFY
+#define RUST_UNIFY
+
+#include "rust-tyty-util.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+class UnifyRules
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::TyWithLocation lhs,
+				  TyTy::TyWithLocation rhs, Location locus,
+				  bool commit_flag, bool emit_error);
+
+protected:
+  TyTy::BaseType *expect_inference_variable (TyTy::InferType *ltype,
+					     TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_adt (TyTy::ADTType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_str (TyTy::StrType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_reference (TyTy::ReferenceType *ltype,
+				    TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_pointer (TyTy::PointerType *ltype,
+				  TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_param (TyTy::ParamType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_array (TyTy::ArrayType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_slice (TyTy::SliceType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_fndef (TyTy::FnType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_fnptr (TyTy::FnPtr *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_tuple (TyTy::TupleType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_bool (TyTy::BoolType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_char (TyTy::CharType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_int (TyTy::IntType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_uint (TyTy::UintType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_float (TyTy::FloatType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_isize (TyTy::ISizeType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_usize (TyTy::USizeType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_never (TyTy::NeverType *ltype, TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_placeholder (TyTy::PlaceholderType *ltype,
+				      TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_projection (TyTy::ProjectionType *ltype,
+				     TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_dyn (TyTy::DynamicObjectType *ltype,
+			      TyTy::BaseType *rtype);
+  TyTy::BaseType *expect_closure (TyTy::ClosureType *ltype,
+				  TyTy::BaseType *rtype);
+
+private:
+  UnifyRules (TyTy::TyWithLocation lhs, TyTy::TyWithLocation rhs,
+	      Location locus, bool commit_flag, bool emit_error);
+
+  void emit_type_mismatch () const;
+  void commit (TyTy::BaseType *resolved);
+  TyTy::BaseType *go ();
+
+  TyTy::BaseType *get_base ();
+  TyTy::BaseType *get_other ();
+
+  TyTy::TyWithLocation lhs;
+  TyTy::TyWithLocation rhs;
+  Location locus;
+  bool commit_flag;
+  bool emit_error;
+
+  Analysis::Mappings &mappings;
+  TypeCheckContext &context;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_UNIFY
diff --git a/gcc/testsuite/rust/compile/never_type_err1.rs b/gcc/testsuite/rust/compile/never_type1.rs
similarity index 52%
rename from gcc/testsuite/rust/compile/never_type_err1.rs
rename to gcc/testsuite/rust/compile/never_type1.rs
index 52b1283fadf..8d56176fee3 100644
--- a/gcc/testsuite/rust/compile/never_type_err1.rs
+++ b/gcc/testsuite/rust/compile/never_type1.rs
@@ -1,8 +1,7 @@
 fn test() {
     let a;
 
-    // FIXME: Unimplemented features
-    a = if true { // { dg-error "expected .T.. got .!." }
+    a = if true {
         return;
     } else {
         return;
