(root)/
gcc-13.2.0/
gcc/
rust/
typecheck/
rust-unify.cc
// 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/>.

#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 ());

  // check bounds
  if (ltype->num_specified_bounds () > 0)
    {
      if (!ltype->bounds_compatible (*rtype, locus, true))
	{
	  // already emitted an error
	  emit_error = false;
	  return new TyTy::ErrorType (0);
	}
    }

  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