Repository: rust-analyzer/rust-analyzer
Pull request #8856 (2021-05-16 16:34:44)

Use Chalk for unification
 - use Chalk's unification, get rid of our own `unify`
 - rewrite coercion to not use unification internals and to be more analogous to rustc
 - fix various coercion bugs
 - rewrite handling of obligations, since the old hacky optimization where we noted when an inference variable changes wasn't possible anymore
 - stop trying to deeply resolve types all the time during inference, instead only do it shallowly where necessary


Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d443b124c4..52d72c3c52 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1712,15 +1712,17 @@ impl Type {
         resolver: &Resolver,
         ty: Ty,
     ) -> Type {
-        let environment =
-            resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d));
+        let environment = resolver
+            .generic_def()
+            .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
         Type { krate, env: environment, ty }
     }
 
     fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type {
         let resolver = lexical_env.resolver(db.upcast());
-        let environment =
-            resolver.generic_def().map_or_else(Default::default, |d| db.trait_environment(d));
+        let environment = resolver
+            .generic_def()
+            .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
         Type { krate, env: environment, ty }
     }
 
@@ -2051,11 +2053,7 @@ impl Type {
         name: Option<&Name>,
         mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
     ) -> Option<T> {
-        // There should be no inference vars in types passed here
-        // FIXME check that?
-        // FIXME replace Unknown by bound vars here
-        let canonical =
-            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(&Interner) };
+        let canonical = hir_ty::replace_errors_with_variables(self.ty.clone());
 
         let env = self.env.clone();
         let krate = krate.id;
@@ -2223,8 +2221,9 @@ impl Type {
         walk_type(db, self, &mut cb);
     }
 
-    pub fn could_unify_with(&self, other: &Type) -> bool {
-        could_unify(&self.ty, &other.ty)
+    pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
+        let tys = hir_ty::replace_errors_with_variables((self.ty.clone(), other.ty.clone()));
+        could_unify(db, self.env.clone(), &tys)
     }
 }
 
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 797b905d1b..cad8a7479a 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -132,7 +132,6 @@ impl ItemTree {
             let ItemTreeData {
                 imports,
                 extern_crates,
-                extern_blocks,
                 functions,
                 params,
                 structs,
@@ -155,7 +154,6 @@ impl ItemTree {
 
             imports.shrink_to_fit();
             extern_crates.shrink_to_fit();
-            extern_blocks.shrink_to_fit();
             functions.shrink_to_fit();
             params.shrink_to_fit();
             structs.shrink_to_fit();
@@ -241,7 +239,6 @@ static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(P
 struct ItemTreeData {
     imports: Arena<Import>,
     extern_crates: Arena<ExternCrate>,
-    extern_blocks: Arena<ExternBlock>,
     functions: Arena<Function>,
     params: Arena<Param>,
     structs: Arena<Struct>,
@@ -435,7 +432,6 @@ macro_rules! mod_items {
 mod_items! {
     Import in imports -> ast::Use,
     ExternCrate in extern_crates -> ast::ExternCrate,
-    ExternBlock in extern_blocks -> ast::ExternBlock,
     Function in functions -> ast::Fn,
     Struct in structs -> ast::Struct,
     Union in unions -> ast::Union,
@@ -511,13 +507,6 @@ pub struct ExternCrate {
     pub ast_id: FileAstId<ast::ExternCrate>,
 }
 
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct ExternBlock {
-    pub abi: Option<Interned<str>>,
-    pub ast_id: FileAstId<ast::ExternBlock>,
-    pub children: Box<[ModItem]>,
-}
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Function {
     pub name: Name,
@@ -702,7 +691,6 @@ impl ModItem {
         match self {
             ModItem::Import(_)
             | ModItem::ExternCrate(_)
-            | ModItem::ExternBlock(_)
             | ModItem::Struct(_)
             | ModItem::Union(_)
             | ModItem::Enum(_)
@@ -727,7 +715,6 @@ impl ModItem {
         match self {
             ModItem::Import(it) => tree[it.index].ast_id().upcast(),
             ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
-            ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(),
             ModItem::Function(it) => tree[it.index].ast_id().upcast(),
             ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
             ModItem::Union(it) => tree[it.index].ast_id().upcast(),
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index a7ffc6364e..fe348091d0 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -147,7 +147,9 @@ impl<'a> Ctx<'a> {
             ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
             ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into),
             ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into),
-            ast::Item::ExternBlock(ast) => Some(self.lower_extern_block(ast).into()),
+            ast::Item::ExternBlock(ast) => {
+                Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
+            }
         };
 
         if !attrs.is_empty() {
@@ -395,7 +397,19 @@ impl<'a> Ctx<'a> {
             ret_type
         };
 
-        let abi = func.abi().map(lower_abi);
+        let abi = func.abi().map(|abi| {
+            // FIXME: Abi::abi() -> Option<SyntaxToken>?
+            match abi.syntax().last_token() {
+                Some(tok) if tok.kind() == SyntaxKind::STRING => {
+                    // FIXME: Better way to unescape?
+                    Interned::new_str(tok.text().trim_matches('"'))
+                }
+                _ => {
+                    // `extern` default to be `extern "C"`.
+                    Interned::new_str("C")
+                }
+            }
+        });
 
         let ast_id = self.source_ast_id_map.ast_id(func);
 
@@ -633,10 +647,8 @@ impl<'a> Ctx<'a> {
         Some(id(self.data().macro_defs.alloc(res)))
     }
 
-    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> FileItemTreeId<ExternBlock> {
-        let ast_id = self.source_ast_id_map.ast_id(block);
-        let abi = block.abi().map(lower_abi);
-        let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| {
+    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
+        block.extern_item_list().map_or(Vec::new(), |list| {
             list.extern_items()
                 .filter_map(|item| {
                     self.collect_inner_items(item.syntax());
@@ -661,20 +673,13 @@ impl<'a> Ctx<'a> {
                             self.data().type_aliases[foreign_ty.index].is_extern = true;
                             foreign_ty.into()
                         }
-                        ast::ExternItem::MacroCall(call) => {
-                            // FIXME: we need some way of tracking that the macro call is in an
-                            // extern block
-                            self.lower_macro_call(&call)?.into()
-                        }
+                        ast::ExternItem::MacroCall(_) => return None,
                     };
                     self.add_attrs(id.into(), attrs);
                     Some(id)
                 })
                 .collect()
-        });
-
-        let res = ExternBlock { abi, ast_id, children };
-        id(self.data().extern_blocks.alloc(res))
+        })
     }
 
     /// Lowers generics defined on `node` and collects inner items defined within.
@@ -874,17 +879,3 @@ fn is_intrinsic_fn_unsafe(name: &Name) -> bool {
     ]
     .contains(&name)
 }
-
-fn lower_abi(abi: ast::Abi) -> Interned<str> {
-    // FIXME: Abi::abi() -> Option<SyntaxToken>?
-    match abi.syntax().last_token() {
-        Some(tok) if tok.kind() == SyntaxKind::STRING => {
-            // FIXME: Better way to unescape?
-            Interned::new_str(tok.text().trim_matches('"'))
-        }
-        _ => {
-            // `extern` default to be `extern "C"`.
-            Interned::new_str("C")
-        }
-    }
-}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index fa4b135fd3..e76d039b8a 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1243,7 +1243,6 @@ impl ModCollector<'_, '_> {
                         status: PartialResolvedImport::Unresolved,
                     })
                 }
-                ModItem::ExternBlock(block) => self.collect(&self.item_tree[block].children),
                 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
                 ModItem::MacroRules(id) => self.collect_macro_rules(id),
                 ModItem::MacroDef(id) => self.collect_macro_def(id),
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index e25ef866d7..893e727c2e 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -6,15 +6,15 @@ use chalk_ir::{
     cast::{Cast, CastTo, Caster},
     fold::Fold,
     interner::HasInterner,
-    AdtId, BoundVar, DebruijnIndex, Safety, Scalar,
+    AdtId, BoundVar, DebruijnIndex, Scalar,
 };
 use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId};
 use smallvec::SmallVec;
 
 use crate::{
     db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders,
-    CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution,
-    TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId,
+    CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
+    TyKind, ValueTyDefId,
 };
 
 /// This is a builder for `Ty` or anything that needs a `Substitution`.
@@ -77,15 +77,7 @@ impl TyBuilder<()> {
     }
 
     pub fn fn_ptr(sig: CallableSig) -> Ty {
-        TyKind::Function(FnPointer {
-            num_binders: 0,
-            sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs },
-            substitution: FnSubst(Substitution::from_iter(
-                &Interner,
-                sig.params_and_return.iter().cloned(),
-            )),
-        })
-        .intern(&Interner)
+        TyKind::Function(sig.to_fn_ptr()).intern(&Interner)
     }
 
     pub fn builtin(builtin: BuiltinType) -> Ty {
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 8f054d06bf..b108fd559c 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -344,20 +344,20 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
     }
 
     fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
-        self
+        &self.db
     }
 }
 
-impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> {
+impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
     fn fn_def_variance(
         &self,
         fn_def_id: chalk_ir::FnDefId<Interner>,
     ) -> chalk_ir::Variances<Interner> {
-        self.db.fn_def_variance(self.krate, fn_def_id)
+        HirDatabase::fn_def_variance(*self, fn_def_id)
     }
 
     fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> {
-        self.db.adt_variance(self.krate, adt_id)
+        HirDatabase::adt_variance(*self, adt_id)
     }
 }
 
@@ -651,11 +651,7 @@ pub(crate) fn fn_def_datum_query(
     Arc::new(datum)
 }
 
-pub(crate) fn fn_def_variance_query(
-    db: &dyn HirDatabase,
-    _krate: CrateId,
-    fn_def_id: FnDefId,
-) -> Variances {
+pub(crate) fn fn_def_variance_query(db: &dyn HirDatabase, fn_def_id: FnDefId) -> Variances {
     let callable_def: CallableDefId = from_chalk(db, fn_def_id);
     let generic_params = generics(db.upcast(), callable_def.into());
     Variances::from_iter(
@@ -666,7 +662,6 @@ pub(crate) fn fn_def_variance_query(
 
 pub(crate) fn adt_variance_query(
     db: &dyn HirDatabase,
-    _krate: CrateId,
     chalk_ir::AdtId(adt_id): AdtId,
 ) -> Variances {
     let generic_params = generics(db.upcast(), adt_id.into());
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 5232a7d803..df340a6ca1 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -18,6 +18,7 @@ pub trait TyExt {
     fn is_unit(&self) -> bool;
     fn is_never(&self) -> bool;
     fn is_unknown(&self) -> bool;
+    fn is_ty_var(&self) -> bool;
 
     fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
     fn as_builtin(&self) -> Option<BuiltinType>;
@@ -55,6 +56,10 @@ impl TyExt for Ty {
         matches!(self.kind(&Interner), TyKind::Error)
     }
 
+    fn is_ty_var(&self) -> bool {
+        matches!(self.kind(&Interner), TyKind::InferenceVar(_, _))
+    }
+
     fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
         match self.kind(&Interner) {
             TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 9da0a02e33..be5b9110ed 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -117,10 +117,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
 
     #[salsa::invoke(chalk_db::fn_def_variance_query)]
-    fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances;
+    fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
 
     #[salsa::invoke(chalk_db::adt_variance_query)]
-    fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
+    fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
 
     #[salsa::invoke(chalk_db::associated_ty_value_query)]
     fn associated_ty_value(
@@ -134,14 +134,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     fn trait_solve(
         &self,
         krate: CrateId,
-        goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
+        goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
     ) -> Option<crate::Solution>;
 
     #[salsa::invoke(crate::traits::trait_solve_query)]
     fn trait_solve_query(
         &self,
         krate: CrateId,
-        goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
+        goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
     ) -> Option<crate::Solution>;
 
     #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
@@ -168,7 +168,7 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
 fn trait_solve_wait(
     db: &dyn HirDatabase,
     krate: CrateId,
-    goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
+    goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
 ) -> Option<crate::Solution> {
     let _p = profile::span("trait_solve::wait");
     db.trait_solve_query(krate, goal)
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 0ee851a74c..f1cebbdb98 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -13,8 +13,6 @@
 //! to certain types. To record this, we use the union-find implementation from
 //! the `ena` crate, which is extracted from rustc.
 
-use std::borrow::Cow;
-use std::mem;
 use std::ops::Index;
 use std::sync::Arc;
 
@@ -27,8 +25,8 @@ use hir_def::{
     path::{path, Path},
     resolver::{HasResolver, Resolver, TypeNs},
     type_ref::TypeRef,
-    AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId,
-    TypeAliasId, VariantId,
+    AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
+    TraitId, TypeAliasId, VariantId,
 };
 use hir_expand::{diagnostics::DiagnosticSink, name::name};
 use la_arena::ArenaMap;
@@ -36,13 +34,11 @@ use rustc_hash::FxHashMap;
 use stdx::impl_from;
 use syntax::SmolStr;
 
-use super::{
-    DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty,
-};
+use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
 use crate::{
     db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
-    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner,
-    TyBuilder, TyExt, TyKind,
+    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder,
+    TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -106,6 +102,14 @@ impl Default for BindingMode {
     }
 }
 
+#[derive(Debug)]
+pub(crate) struct InferOk {
+    goals: Vec<InEnvironment<Goal>>,
+}
+#[derive(Debug)]
+pub(crate) struct TypeError;
+pub(crate) type InferResult = Result<InferOk, TypeError>;
+
 /// A mismatch between an expected and an inferred type.
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct TypeMismatch {
@@ -217,10 +221,8 @@ struct InferenceContext<'a> {
     owner: DefWithBodyId,
     body: Arc<Body>,
     resolver: Resolver,
-    table: unify::InferenceTable,
+    table: unify::InferenceTable<'a>,
     trait_env: Arc<TraitEnvironment>,
-    obligations: Vec<DomainGoal>,
-    last_obligations_check: Option<u32>,
     result: InferenceResult,
     /// The return type of the function being inferred, or the closure if we're
     /// currently within one.
@@ -252,15 +254,15 @@ fn find_breakable<'c>(
 
 impl<'a> InferenceContext<'a> {
     fn new(db: &'a dyn HirDatabase, owner: DefWithBodyId, resolver: Resolver) -> Self {
+        let krate = owner.module(db.upcast()).krate();
+        let trait_env = owner
+            .as_generic_def_id()
+            .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
         InferenceContext {
             result: InferenceResult::default(),
-            table: unify::InferenceTable::new(),
-            obligations: Vec::default(),
-            last_obligations_check: None,
+            table: unify::InferenceTable::new(db, trait_env.clone()),
+            trait_env,
             return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature
-            trait_env: owner
-                .as_generic_def_id()
-                .map_or_else(Default::default, |d| db.trait_environment(d)),
             db,
             owner,
             body: db.body(owner),
@@ -271,19 +273,25 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn err_ty(&self) -> Ty {
-        TyKind::Error.intern(&Interner)
+        self.result.standard_types.unknown.clone()
     }
 
     fn resolve_all(mut self) -> InferenceResult {
         // FIXME resolve obligations as well (use Guidance if necessary)
+        self.table.resolve_obligations_as_possible();
+
+        // make sure diverging type variables are marked as such
+        self.table.propagate_diverging_flag();
         let mut result = std::mem::take(&mut self.result);
         for ty in result.type_of_expr.values_mut() {
-            let resolved = self.table.resolve_ty_completely(ty.clone());
-            *ty = resolved;
+            *ty = self.table.resolve_ty_completely(ty.clone());
         }
         for ty in result.type_of_pat.values_mut() {
-            let resolved = self.table.resolve_ty_completely(ty.clone());
-            *ty = resolved;
+            *ty = self.table.resolve_ty_completely(ty.clone());
+        }
+        for mismatch in result.type_mismatches.values_mut() {
+            mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone());
+            mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone());
         }
         result
     }
@@ -337,6 +345,14 @@ impl<'a> InferenceContext<'a> {
     fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
         match ty.kind(&Interner) {
             TyKind::Error => self.table.new_type_var(),
+            TyKind::InferenceVar(..) => {
+                let ty_resolved = self.resolve_ty_shallow(&ty);
+                if ty_resolved.is_unknown() {
+                    self.table.new_type_var()
+                } else {
+                    ty
+                }
+            }
             _ => ty,
         }
     }
@@ -346,66 +362,19 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn resolve_obligations_as_possible(&mut self) {
-        if self.last_obligations_check == Some(self.table.revision) {
-            // no change
-            return;
-        }
-        let _span = profile::span("resolve_obligations_as_possible");
-
-        self.last_obligations_check = Some(self.table.revision);
-        let obligations = mem::replace(&mut self.obligations, Vec::new());
-        for obligation in obligations {
-            let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone());
-            let canonicalized = self.canonicalizer().canonicalize_obligation(in_env);
-            let solution =
-                self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
-
-            match solution {
-                Some(Solution::Unique(canonical_subst)) => {
-                    canonicalized.apply_solution(
-                        self,
-                        Canonical {
-                            binders: canonical_subst.binders,
-                            // FIXME: handle constraints
-                            value: canonical_subst.value.subst,
-                        },
-                    );
-                }
-                Some(Solution::Ambig(Guidance::Definite(substs))) => {
-                    canonicalized.apply_solution(self, substs);
-                    self.obligations.push(obligation);
-                }
-                Some(_) => {
-                    // FIXME use this when trying to resolve everything at the end
-                    self.obligations.push(obligation);
-                }
-                None => {
-                    // FIXME obligation cannot be fulfilled => diagnostic
-                }
-            };
-        }
+        self.table.resolve_obligations_as_possible();
     }
 
     fn push_obligation(&mut self, o: DomainGoal) {
-        self.obligations.push(o);
-        self.last_obligations_check = None;
+        self.table.register_obligation(o.cast(&Interner));
     }
 
     fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
         self.table.unify(ty1, ty2)
     }
 
-    /// Resolves the type as far as currently possible, replacing type variables
-    /// by their known types. All types returned by the infer_* functions should
-    /// be resolved as far as possible, i.e. contain no type variables with
-    /// known type.
-    fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
+    fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
         self.resolve_obligations_as_possible();
-
-        self.table.resolve_ty_as_possible(ty)
-    }
-
-    fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
         self.table.resolve_ty_shallow(ty)
     }
 
@@ -439,7 +408,7 @@ impl<'a> InferenceContext<'a> {
                 };
                 self.push_obligation(trait_ref.cast(&Interner));
                 self.push_obligation(alias_eq.cast(&Interner));
-                self.resolve_ty_as_possible(ty)
+                ty
             }
             None => self.err_ty(),
         }
@@ -452,25 +421,7 @@ impl<'a> InferenceContext<'a> {
     /// call). `make_ty` handles this already, but e.g. for field types we need
     /// to do it as well.
     fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
-        let ty = self.resolve_ty_as_possible(ty);
-        fold_tys(
-            ty,
-            |ty, _| match ty.kind(&Interner) {
-                TyKind::Alias(AliasTy::Projection(proj_ty)) => {
-                    self.normalize_projection_ty(proj_ty.clone())
-                }
-                _ => ty,
-            },
-            DebruijnIndex::INNERMOST,
-        )
-    }
-
-    fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
-        let var = self.table.new_type_var();
-        let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
-        let obligation = alias_eq.cast(&Interner);
-        self.push_obligation(obligation);
-        var
+        self.table.normalize_associated_types_in(ty)
     }
 
     fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) {
@@ -720,17 +671,23 @@ impl<'a> InferenceContext<'a> {
 /// When inferring an expression, we propagate downward whatever type hint we
 /// are able in the form of an `Expectation`.
 #[derive(Clone, PartialEq, Eq, Debug)]
-struct Expectation {
-    ty: Ty,
-    /// See the `rvalue_hint` method.
-    rvalue_hint: bool,
+enum Expectation {
+    None,
+    HasType(Ty),
+    // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts
+    RValueLikeUnsized(Ty),
 }
 
 impl Expectation {
     /// The expectation that the type of the expression needs to equal the given
     /// type.
     fn has_type(ty: Ty) -> Self {
-        Expectation { ty, rvalue_hint: false }
+        if ty.is_unknown() {
+            // FIXME: get rid of this?
+            Expectation::None
+        } else {
+            Expectation::HasType(ty)
+        }
     }
 
     /// The following explanation is copied straight from rustc:
@@ -754,24 +711,41 @@ impl Expectation {
     /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
     /// for examples of where this comes up,.
     fn rvalue_hint(ty: Ty) -> Self {
-        Expectation { ty, rvalue_hint: true }
+        match ty.strip_references().kind(&Interner) {
+            TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
+            _ => Expectation::has_type(ty),
+        }
     }
 
     /// This expresses no expectation on the type.
     fn none() -> Self {
-        Expectation {
-            // FIXME
-            ty: TyKind::Error.intern(&Interner),
-            rvalue_hint: false,
+        Expectation::None
+    }
+
+    fn resolve(&self, table: &mut unify::InferenceTable) -> Expectation {
+        match self {
+            Expectation::None => Expectation::None,
+            Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)),
+            Expectation::RValueLikeUnsized(t) => {
+                Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t))
+            }
         }
     }
 
-    fn coercion_target(&self) -> Ty {
-        if self.rvalue_hint {
-            // FIXME
-            TyKind::Error.intern(&Interner)
-        } else {
-            self.ty.clone()
+    fn to_option(&self, table: &mut unify::InferenceTable) -> Option<Ty> {
+        match self.resolve(table) {
+            Expectation::None => None,
+            Expectation::HasType(t) |
+            // Expectation::Castable(t) |
+            Expectation::RValueLikeUnsized(t) => Some(t),
+        }
+    }
+
+    fn only_has_type(&self, table: &mut unify::InferenceTable) -> Option<Ty> {
+        match self {
+            Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)),
+            // Expectation::Castable(_) |
+            Expectation::RValueLikeUnsized(_) | Expectation::None => None,
         }
     }
 }
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 1f463a4255..765a02b1ce 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -2,156 +2,414 @@
 //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
 //! like going from `&Vec<T>` to `&[T]`.
 //!
-//! See: https://doc.rust-lang.org/nomicon/coercions.html
+//! See https://doc.rust-lang.org/nomicon/coercions.html and
+//! librustc_typeck/check/coercion.rs.
 
 use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
-use hir_def::lang_item::LangItemTarget;
+use hir_def::{expr::ExprId, lang_item::LangItemTarget};
 
-use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind};
+use crate::{
+    autoderef, infer::TypeMismatch, static_lifetime, Canonical, DomainGoal, FnPointer, FnSig,
+    Interner, Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
+};
 
-use super::{InEnvironment, InferenceContext};
+use super::{InEnvironment, InferOk, InferResult, InferenceContext, TypeError};
 
 impl<'a> InferenceContext<'a> {
     /// Unify two types, but may coerce the first one to the second one
     /// using "implicit coercion rules" if needed.
     pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
-        let from_ty = self.resolve_ty_shallow(from_ty).into_owned();
+        let from_ty = self.resolve_ty_shallow(from_ty);
         let to_ty = self.resolve_ty_shallow(to_ty);
-        self.coerce_inner(from_ty, &to_ty)
+        match self.coerce_inner(from_ty, &to_ty) {
+            Ok(result) => {
+                self.table.register_infer_ok(result);
+                true
+            }
+            Err(_) => {
+                // FIXME deal with error
+                false
+            }
+        }
     }
 
     /// Merge two types from different branches, with possible coercion.
     ///
     /// Mostly this means trying to coerce one to the other, but
-    ///  - if we have two function types for different functions, we need to
+    ///  - if we have two function types for different functions or closures, we need to
     ///    coerce both to function pointers;
     ///  - if we were concerned with lifetime subtyping, we'd need to look for a
     ///    least upper bound.
-    pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
-        if self.coerce(ty1, ty2) {
-            ty2.clone()
-        } else if self.coerce(ty2, ty1) {
+    pub(super) fn coerce_merge_branch(&mut self, id: Option<ExprId>, ty1: &Ty, ty2: &Ty) -> Ty {
+        let ty1 = self.resolve_ty_shallow(ty1);
+        let ty2 = self.resolve_ty_shallow(ty2);
+        // Special case: two function types. Try to coerce both to
+        // pointers to have a chance at getting a match. See
+        // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
+        let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) {
+            (TyKind::FnDef(..), TyKind::FnDef(..))
+            | (TyKind::Closure(..), TyKind::FnDef(..))
+            | (TyKind::FnDef(..), TyKind::Closure(..))
+            | (TyKind::Closure(..), TyKind::Closure(..)) => {
+                // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
+                // we should be coercing the closure to a fn pointer of the safety of the FnDef
+                cov_mark::hit!(coerce_fn_reification);
+                let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig");
+                Some(sig)
+            }
+            _ => None,
+        };
+        if let Some(sig) = sig {
+            let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
+            let result1 = self.coerce_inner(ty1.clone(), &target_ty);
+            let result2 = self.coerce_inner(ty2.clone(), &target_ty);
+            if let (Ok(result1), Ok(result2)) = (result1, result2) {
+                self.table.register_infer_ok(result1);
+                self.table.register_infer_ok(result2);
+                return target_ty;
+            }
+        }
+
+        // It might not seem like it, but order is important here: ty1 is our
+        // "previous" type, ty2 is the "new" one being added. If the previous
+        // type is a type variable and the new one is `!`, trying it the other
+        // way around first would mean we make the type variable `!`, instead of
+        // just marking it as possibly diverging.
+        if self.coerce(&ty2, &ty1) {
             ty1.clone()
+        } else if self.coerce(&ty1, &ty2) {
+            ty2.clone()
         } else {
-            if let (TyKind::FnDef(..), TyKind::FnDef(..)) =
-                (ty1.kind(&Interner), ty2.kind(&Interner))
-            {
-                cov_mark::hit!(coerce_fn_reification);
-                // Special case: two function types. Try to coerce both to
-                // pointers to have a chance at getting a match. See
-                // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
-                let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
-                let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
-                let ptr_ty1 = TyBuilder::fn_ptr(sig1);
-                let ptr_ty2 = TyBuilder::fn_ptr(sig2);
-                self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
-            } else {
-                cov_mark::hit!(coerce_merge_fail_fallback);
-                ty1.clone()
+            if let Some(id) = id {
+                self.result
+                    .type_mismatches
+                    .insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2.clone() });
             }
+            cov_mark::hit!(coerce_merge_fail_fallback);
+            ty1.clone()
         }
     }
 
-    fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
-        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
-            // Never type will make type variable to fallback to Never Type instead of Unknown.
-            (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => {
-                self.table.type_variable_table.set_diverging(*tv, true);
-                return true;
+    fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
+        if from_ty.is_never() {
+            // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
+            // type variable, we want `?T` to fallback to `!` if not
+            // otherwise constrained. An example where this arises:
+            //
+            //     let _: Option<?T> = Some({ return; });
+            //
+            // here, we would coerce from `!` to `?T`.
+            match to_ty.kind(&Interner) {
+                TyKind::InferenceVar(tv, TyVariableKind::General) => {
+                    self.table.set_diverging(*tv, true);
+                }
+                _ => {}
             }
-            (TyKind::Never, _) => return true,
+            return Ok(InferOk { goals: Vec::new() });
+        }
 
-            // Trivial cases, this should go after `never` check to
-            // avoid infer result type to be never
-            _ => {
-                if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
-                    return true;
-                }
+        // Consider coercing the subtype to a DST
+        if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
+            return Ok(ret);
+        }
+
+        // Examine the supertype and consider auto-borrowing.
+        match to_ty.kind(&Interner) {
+            TyKind::Raw(mt, _) => {
+                return self.coerce_ptr(from_ty, to_ty, *mt);
             }
+            TyKind::Ref(mt, _, _) => {
+                return self.coerce_ref(from_ty, to_ty, *mt);
+            }
+            _ => {}
         }
 
-        // Pointer weakening and function to pointer
-        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
-            // `*mut T` -> `*const T`
-            (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => {
-                from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner);
+        match from_ty.kind(&Interner) {
+            TyKind::FnDef(..) => {
+                // Function items are coercible to any closure
+                // type; function pointers are not (that would
+                // require double indirection).
+                // Additionally, we permit coercion of function
+                // items to drop the unsafe qualifier.
+                self.coerce_from_fn_item(from_ty, to_ty)
             }
-            // `&mut T` -> `&T`
-            (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
-                from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner);
+            TyKind::Function(from_fn_ptr) => {
+                // We permit coercion of fn pointers to drop the
+                // unsafe qualifier.
+                self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty)
             }
-            // `&T` -> `*const T`
-            // `&mut T` -> `*mut T`/`*const T`
-            (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..))
-            | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => {
-                from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
+            TyKind::Closure(_, from_substs) => {
+                // Non-capturing closures are coercible to
+                // function pointers or unsafe function pointers.
+                // It cannot convert closures that require unsafe.
+                self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty)
             }
+            _ => {
+                // Otherwise, just use unification rules.
+                self.table.try_unify(&from_ty, to_ty)
+            }
+        }
+    }
 
-            // Illegal mutability conversion
-            (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..))
-            | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false,
+    fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
+        let (_is_ref, from_mt, from_inner) = match from_ty.kind(&Interner) {
+            TyKind::Ref(mt, _, ty) => (true, mt, ty),
+            TyKind::Raw(mt, ty) => (false, mt, ty),
+            _ => return self.table.try_unify(&from_ty, to_ty),
+        };
 
-            // `{function_type}` -> `fn()`
-            (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) {
-                None => return false,
-                Some(sig) => {
-                    from_ty = TyBuilder::fn_ptr(sig);
-                }
+        coerce_mutabilities(*from_mt, to_mt)?;
+
+        // Check that the types which they point at are compatible.
+        let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner);
+        // FIXME: behavior differs based on is_ref once we're computing adjustments
+        self.table.try_unify(&from_raw, to_ty)
+    }
+
+    /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
+    /// To match `A` with `B`, autoderef will be performed,
+    /// calling `deref`/`deref_mut` where necessary.
+    fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> InferResult {
+        match from_ty.kind(&Interner) {
+            TyKind::Ref(mt, _, _) => {
+                coerce_mutabilities(*mt, to_mt)?;
+            }
+            _ => return self.table.try_unify(&from_ty, to_ty),
+        };
+
+        // NOTE: this code is mostly copied and adapted from rustc, and
+        // currently more complicated than necessary, carrying errors around
+        // etc.. This complication will become necessary when we actually track
+        // details of coercion errors though, so I think it's useful to leave
+        // the structure like it is.
+
+        let canonicalized = self.canonicalize(from_ty.clone());
+        let autoderef = autoderef::autoderef(
+            self.db,
+            self.resolver.krate(),
+            InEnvironment {
+                goal: canonicalized.value.clone(),
+                environment: self.trait_env.env.clone(),
             },
+        );
+        let mut first_error = None;
+        let mut found = None;
 
-            (TyKind::Closure(.., substs), TyKind::Function { .. }) => {
-                from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
+        for (autoderefs, referent_ty) in autoderef.enumerate() {
+            if autoderefs == 0 {
+                // Don't let this pass, otherwise it would cause
+                // &T to autoref to &&T.
+                continue;
             }
 
-            _ => {}
+            let referent_ty = canonicalized.decanonicalize_ty(referent_ty.value);
+
+            // At this point, we have deref'd `a` to `referent_ty`.  So
+            // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
+            // In the autoderef loop for `&'a mut Vec<T>`, we would get
+            // three callbacks:
+            //
+            // - `&'a mut Vec<T>` -- 0 derefs, just ignore it
+            // - `Vec<T>` -- 1 deref
+            // - `[T]` -- 2 deref
+            //
+            // At each point after the first callback, we want to
+            // check to see whether this would match out target type
+            // (`&'b mut [T]`) if we autoref'd it. We can't just
+            // compare the referent types, though, because we still
+            // have to consider the mutability. E.g., in the case
+            // we've been considering, we have an `&mut` reference, so
+            // the `T` in `[T]` needs to be unified with equality.
+            //
+            // Therefore, we construct reference types reflecting what
+            // the types will be after we do the final auto-ref and
+            // compare those. Note that this means we use the target
+            // mutability [1], since it may be that we are coercing
+            // from `&mut T` to `&U`.
+            let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc
+            let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(&Interner);
+            match self.table.try_unify(&derefd_from_ty, to_ty) {
+                Ok(result) => {
+                    found = Some(result);
+                    break;
+                }
+                Err(err) => {
+                    if first_error.is_none() {
+                        first_error = Some(err);
+                    }
+                }
+            }
         }
 
-        if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
-            return ret;
+        // Extract type or return an error. We return the first error
+        // we got, which should be from relating the "base" type
+        // (e.g., in example above, the failure from relating `Vec<T>`
+        // to the target type), since that should be the least
+        // confusing.
+        let result = match found {
+            Some(d) => d,
+            None => {
+                let err = first_error.expect("coerce_borrowed_pointer had no error");
+                return Err(err);
+            }
+        };
+
+        Ok(result)
+    }
+
+    /// Attempts to coerce from the type of a Rust function item into a function pointer.
+    fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> InferResult {
+        match to_ty.kind(&Interner) {
+            TyKind::Function(_) => {
+                let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig");
+
+                // FIXME check ABI: Intrinsics are not coercible to function pointers
+                // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396)
+
+                // FIXME rustc normalizes assoc types in the sig here, not sure if necessary
+
+                let from_sig = from_sig.to_fn_ptr();
+                let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(&Interner);
+                let ok = self.coerce_from_safe_fn(from_fn_pointer, &from_sig, to_ty)?;
+
+                Ok(ok)
+            }
+            _ => self.table.try_unify(&from_ty, to_ty),
         }
+    }
+
+    fn coerce_from_fn_pointer(
+        &mut self,
+        from_ty: Ty,
+        from_f: &FnPointer,
+        to_ty: &Ty,
+    ) -> InferResult {
+        self.coerce_from_safe_fn(from_ty, from_f, to_ty)
+    }
 
-        // Auto Deref if cannot coerce
-        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
-            // FIXME: DerefMut
-            (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => {
-                self.unify_autoderef_behind_ref(st1, st2)
+    fn coerce_from_safe_fn(
+        &mut self,
+        from_ty: Ty,
+        from_fn_ptr: &FnPointer,
+        to_ty: &Ty,
+    ) -> InferResult {
+        if let TyKind::Function(to_fn_ptr) = to_ty.kind(&Interner) {
+            if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) =
+                (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety)
+            {
+                let from_unsafe =
+                    TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(&Interner);
+                return self.table.try_unify(&from_unsafe, to_ty);
             }
+        }
+        self.table.try_unify(&from_ty, to_ty)
+    }
 
-            // Otherwise, normal unify
-            _ => self.unify(&from_ty, to_ty),
+    /// Attempts to coerce from the type of a non-capturing closure into a
+    /// function pointer.
+    fn coerce_closure_to_fn(
+        &mut self,
+        from_ty: Ty,
+        from_substs: &Substitution,
+        to_ty: &Ty,
+    ) -> InferResult {
+        match to_ty.kind(&Interner) {
+            TyKind::Function(fn_ty) /* if from_substs is non-capturing (FIXME) */ => {
+                // We coerce the closure, which has fn type
+                //     `extern "rust-call" fn((arg0,arg1,...)) -> _`
+                // to
+                //     `fn(arg0,arg1,...) -> _`
+                // or
+                //     `unsafe fn(arg0,arg1,...) -> _`
+                let safety = fn_ty.sig.safety;
+                let pointer_ty = coerce_closure_fn_ty(from_substs, safety);
+                self.table.try_unify(&pointer_ty, to_ty)
+            }
+            _ => self.table.try_unify(&from_ty, to_ty),
         }
     }
 
     /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
     ///
     /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html
-    fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> {
+    fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult {
+        // These 'if' statements require some explanation.
+        // The `CoerceUnsized` trait is special - it is only
+        // possible to write `impl CoerceUnsized<B> for A` where
+        // A and B have 'matching' fields. This rules out the following
+        // two types of blanket impls:
+        //
+        // `impl<T> CoerceUnsized<T> for SomeType`
+        // `impl<T> CoerceUnsized<SomeType> for T`
+        //
+        // Both of these trigger a special `CoerceUnsized`-related error (E0376)
+        //
+        // We can take advantage of this fact to avoid performing unecessary work.
+        // If either `source` or `target` is a type variable, then any applicable impl
+        // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`)
+        // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for
+        // SomeType`).
+        //
+        // However, these are exactly the kinds of impls which are forbidden by
+        // the compiler! Therefore, we can be sure that coercion will always fail
+        // when either the source or target type is a type variable. This allows us
+        // to skip performing any trait selection, and immediately bail out.
+        if from_ty.is_ty_var() {
+            return Err(TypeError);
+        }
+        if to_ty.is_ty_var() {
+            return Err(TypeError);
+        }
+
+        // Handle reborrows before trying to solve `Source: CoerceUnsized<Target>`.
+        let coerce_from = match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
+            (TyKind::Ref(from_mt, _, from_inner), TyKind::Ref(to_mt, _, _)) => {
+                coerce_mutabilities(*from_mt, *to_mt)?;
+
+                let lt = static_lifetime();
+                TyKind::Ref(*to_mt, lt, from_inner.clone()).intern(&Interner)
+            }
+            (TyKind::Ref(from_mt, _, from_inner), TyKind::Raw(to_mt, _)) => {
+                coerce_mutabilities(*from_mt, *to_mt)?;
+
+                TyKind::Raw(*to_mt, from_inner.clone()).intern(&Interner)
+            }
+            _ => from_ty.clone(),
+        };
+
         let krate = self.resolver.krate().unwrap();
         let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
             Some(LangItemTarget::TraitId(trait_)) => trait_,
-            _ => return None,
+            _ => return Err(TypeError),
         };
 
         let trait_ref = {
             let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait);
             if b.remaining() != 2 {
                 // The CoerceUnsized trait should have two generic params: Self and T.
-                return None;
+                return Err(TypeError);
             }
-            b.push(from_ty.clone()).push(to_ty.clone()).build()
+            b.push(coerce_from.clone()).push(to_ty.clone()).build()
         };
 
-        let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
+        let goal: InEnvironment<DomainGoal> =
+            InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
 
-        let canonicalizer = self.canonicalizer();
-        let canonicalized = canonicalizer.canonicalize_obligation(goal);
+        let canonicalized = self.canonicalize(goal);
 
-        let solution = self.db.trait_solve(krate, canonicalized.value.clone())?;
+        // FIXME: rustc's coerce_unsized is more specialized -- it only tries to
+        // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the
+        // rest for later. Also, there's some logic about sized type variables.
+        // Need to find out in what cases this is necessary
+        let solution = self
+            .db
+            .trait_solve(krate, canonicalized.value.clone().cast(&Interner))
+            .ok_or(TypeError)?;
 
         match solution {
             Solution::Unique(v) => {
                 canonicalized.apply_solution(
-                    self,
+                    &mut self.table,
                     Canonical {
                         binders: v.binders,
                         // FIXME handle constraints
@@ -159,38 +417,40 @@ impl<'a> InferenceContext<'a> {
                     },
                 );
             }
-            _ => return None,
+            // FIXME: should we accept ambiguous results here?
+            _ => return Err(TypeError),
         };
 
-        Some(true)
+        Ok(InferOk { goals: Vec::new() })
     }
+}
 
-    /// Unify `from_ty` to `to_ty` with optional auto Deref
-    ///
-    /// Note that the parameters are already stripped the outer reference.
-    fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
-        let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone());
-        let to_ty = self.resolve_ty_shallow(&to_ty);
-        // FIXME: Auto DerefMut
-        for derefed_ty in autoderef::autoderef(
-            self.db,
-            self.resolver.krate(),
-            InEnvironment {
-                goal: canonicalized.value.clone(),
-                environment: self.trait_env.env.clone(),
-            },
-        ) {
-            let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
-            let from_ty = self.resolve_ty_shallow(&derefed_ty);
-            // Stop when constructor matches.
-            if from_ty.equals_ctor(&to_ty) {
-                // It will not recurse to `coerce`.
-                return self.table.unify(&from_ty, &to_ty);
-            } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
-                return true;
-            }
-        }
+fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
+    let closure_sig = closure_substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
+    match closure_sig.kind(&Interner) {
+        TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
+            num_binders: fn_ty.num_binders,
+            sig: FnSig { safety, ..fn_ty.sig },
+            substitution: fn_ty.substitution.clone(),
+        })
+        .intern(&Interner),
+        _ => TyKind::Error.intern(&Interner),
+    }
+}
+
+fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer {
+    FnPointer {
+        num_binders: fn_ty.num_binders,
+        sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig },
+        substitution: fn_ty.substitution,
+    }
+}
 
-        false
+fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> {
+    match (from, to) {
+        (Mutability::Mut, Mutability::Mut)
+        | (Mutability::Mut, Mutability::Not)
+        | (Mutability::Not, Mutability::Not) => Ok(()),
+        (Mutability::Not, Mutability::Mut) => Err(TypeError),
     }
 }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 7278faeec0..08c05c67cc 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -35,39 +35,43 @@ use super::{
 impl<'a> InferenceContext<'a> {
     pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         let ty = self.infer_expr_inner(tgt_expr, expected);
-        if ty.is_never() {
+        if self.resolve_ty_shallow(&ty).is_never() {
             // Any expression that produces a value of type `!` must have diverged
             self.diverges = Diverges::Always;
         }
-        let could_unify = self.unify(&ty, &expected.ty);
-        if !could_unify {
-            self.result.type_mismatches.insert(
-                tgt_expr.into(),
-                TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
-            );
+        if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+            let could_unify = self.unify(&ty, &expected_ty);
+            if !could_unify {
+                self.result.type_mismatches.insert(
+                    tgt_expr.into(),
+                    TypeMismatch { expected: expected_ty.clone(), actual: ty.clone() },
+                );
+            }
         }
-        self.resolve_ty_as_possible(ty)
+        ty
     }
 
     /// Infer type of expression with possibly implicit coerce to the expected type.
     /// Return the type after possible coercion.
     pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
         let ty = self.infer_expr_inner(expr, &expected);
-        let ty = if !self.coerce(&ty, &expected.coercion_target()) {
-            self.result.type_mismatches.insert(
-                expr.into(),
-                TypeMismatch { expected: expected.ty.clone(), actual: ty.clone() },
-            );
-            // Return actual type when type mismatch.
-            // This is needed for diagnostic when return type mismatch.
-            ty
-        } else if expected.coercion_target().is_unknown() {
-            ty
+        let ty = if let Some(target) = expected.only_has_type(&mut self.table) {
+            if !self.coerce(&ty, &target) {
+                self.result.type_mismatches.insert(
+                    expr.into(),
+                    TypeMismatch { expected: target.clone(), actual: ty.clone() },
+                );
+                // Return actual type when type mismatch.
+                // This is needed for diagnostic when return type mismatch.
+                ty
+            } else {
+                target.clone()
+            }
         } else {
-            expected.ty.clone()
+            ty
         };
 
-        self.resolve_ty_as_possible(ty)
+        ty
     }
 
     fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
@@ -98,10 +102,10 @@ impl<'a> InferenceContext<'a> {
             goal: projection.trait_ref(self.db).cast(&Interner),
             environment: trait_env,
         };
-        let canonical = self.canonicalizer().canonicalize_obligation(obligation.clone());
-        if self.db.trait_solve(krate, canonical.value).is_some() {
+        let canonical = self.canonicalize(obligation.clone());
+        if self.db.trait_solve(krate, canonical.value.cast(&Interner)).is_some() {
             self.push_obligation(obligation.goal);
-            let return_ty = self.normalize_projection_ty(projection);
+            let return_ty = self.table.normalize_projection_ty(projection);
             Some((arg_tys, return_ty))
         } else {
             None
@@ -131,17 +135,21 @@ impl<'a> InferenceContext<'a> {
                 let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let mut both_arms_diverge = Diverges::Always;
 
+                let mut result_ty = self.table.new_type_var();
                 let then_ty = self.infer_expr_inner(*then_branch, &expected);
                 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
+                result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty);
                 let else_ty = match else_branch {
                     Some(else_branch) => self.infer_expr_inner(*else_branch, &expected),
                     None => TyBuilder::unit(),
                 };
                 both_arms_diverge &= self.diverges;
+                // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
+                result_ty = self.coerce_merge_branch(*else_branch, &result_ty, &else_ty);
 
                 self.diverges = condition_diverges | both_arms_diverge;
 
-                self.coerce_merge_branch(&then_ty, &else_ty)
+                result_ty
             }
             Expr::Block { statements, tail, label, id: _ } => {
                 let old_resolver = mem::replace(
@@ -277,12 +285,13 @@ impl<'a> InferenceContext<'a> {
                 // Eagerly try to relate the closure type with the expected
                 // type, otherwise we often won't have enough information to
                 // infer the body.
-                self.coerce(&closure_ty, &expected.ty);
+                if let Some(t) = expected.only_has_type(&mut self.table) {
+                    self.coerce(&closure_ty, &t);
+                }
 
                 // Now go through the argument patterns
                 for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
-                    let resolved = self.resolve_ty_as_possible(arg_ty);
-                    self.infer_pat(*arg_pat, &resolved, BindingMode::default());
+                    self.infer_pat(*arg_pat, &arg_ty, BindingMode::default());
                 }
 
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
@@ -297,13 +306,13 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Call { callee, args } => {
                 let callee_ty = self.infer_expr(*callee, &Expectation::none());
-                let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
+                let canonicalized = self.canonicalize(callee_ty.clone());
                 let mut derefs = autoderef(
                     self.db,
                     self.resolver.krate(),
                     InEnvironment {
                         goal: canonicalized.value.clone(),
-                        environment: self.trait_env.env.clone(),
+                        environment: self.table.trait_env.env.clone(),
                     },
                 );
                 let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
@@ -350,7 +359,7 @@ impl<'a> InferenceContext<'a> {
 
                     let arm_ty = self.infer_expr_inner(arm.expr, &expected);
                     all_arms_diverge &= self.diverges;
-                    result_ty = self.coerce_merge_branch(&result_ty, &arm_ty);
+                    result_ty = self.coerce_merge_branch(Some(arm.expr), &result_ty, &arm_ty);
                 }
 
                 self.diverges = matchee_diverges | all_arms_diverge;
@@ -364,12 +373,6 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Continue { .. } => TyKind::Never.intern(&Interner),
             Expr::Break { expr, label } => {
-                let val_ty = if let Some(expr) = expr {
-                    self.infer_expr(*expr, &Expectation::none())
-                } else {
-                    TyBuilder::unit()
-                };
-
                 let last_ty =
                     if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
                         ctxt.break_ty.clone()
@@ -377,7 +380,14 @@ impl<'a> InferenceContext<'a> {
                         self.err_ty()
                     };
 
-                let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
+                let val_ty = if let Some(expr) = expr {
+                    self.infer_expr(*expr, &Expectation::none())
+                } else {
+                    TyBuilder::unit()
+                };
+
+                // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
+                let merged_type = self.coerce_merge_branch(*expr, &last_ty, &val_ty);
 
                 if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
                     ctxt.break_ty = merged_type;
@@ -411,7 +421,9 @@ impl<'a> InferenceContext<'a> {
                     self.write_variant_resolution(tgt_expr.into(), variant);
                 }
 
-                self.unify(&ty, &expected.ty);
+                if let Some(t) = expected.only_has_type(&mut self.table) {
+                    self.unify(&ty, &t);
+                }
 
                 let substs = ty
                     .as_adt()
@@ -442,7 +454,7 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Field { expr, name } => {
                 let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
-                let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
+                let canonicalized = self.canonicalize(receiver_ty);
                 let ty = autoderef::autoderef(
                     self.db,
                     self.resolver.krate(),
@@ -514,6 +526,7 @@ impl<'a> InferenceContext<'a> {
                 self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
             }
             Expr::Cast { expr, type_ref } => {
+                // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
                 let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 let cast_ty = self.make_ty(type_ref);
                 // FIXME check the cast...
@@ -521,15 +534,17 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Ref { expr, rawness, mutability } => {
                 let mutability = lower_to_chalk_mutability(*mutability);
-                let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) =
-                    &expected.ty.as_reference_or_ptr()
+                let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected
+                    .only_has_type(&mut self.table)
+                    .as_ref()
+                    .and_then(|t| t.as_reference_or_ptr())
                 {
-                    if *exp_mutability == Mutability::Mut && mutability == Mutability::Not {
-                        // FIXME: throw type error - expected mut reference but found shared ref,
+                    if exp_mutability == Mutability::Mut && mutability == Mutability::Not {
+                        // FIXME: record type error - expected mut reference but found shared ref,
                         // which cannot be coerced
                     }
-                    if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
-                        // FIXME: throw type error - expected reference but found ptr,
+                    if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
+                        // FIXME: record type error - expected reference but found ptr,
                         // which cannot be coerced
                     }
                     Expectation::rvalue_hint(Ty::clone(exp_inner))
@@ -556,10 +571,11 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::UnaryOp { expr, op } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
+                let inner_ty = self.resolve_ty_shallow(&inner_ty);
                 match op {
                     UnaryOp::Deref => match self.resolver.krate() {
                         Some(krate) => {
-                            let canonicalized = self.canonicalizer().canonicalize_ty(inner_ty);
+                            let canonicalized = self.canonicalize(inner_ty);
                             match autoderef::deref(
                                 self.db,
                                 krate,
@@ -612,8 +628,10 @@ impl<'a> InferenceContext<'a> {
                         _ => Expectation::none(),
                     };
                     let lhs_ty = self.infer_expr(*lhs, &lhs_expectation);
+                    let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
                     let rhs_expectation = op::binary_op_rhs_expectation(*op, lhs_ty.clone());
                     let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(rhs_expectation));
+                    let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
 
                     let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone());
 
@@ -676,7 +694,7 @@ impl<'a> InferenceContext<'a> {
                 if let (Some(index_trait), Some(krate)) =
                     (self.resolve_ops_index(), self.resolver.krate())
                 {
-                    let canonicalized = self.canonicalizer().canonicalize_ty(base_ty);
+                    let canonicalized = self.canonicalize(base_ty);
                     let self_ty = method_resolution::resolve_indexing_op(
                         self.db,
                         &canonicalized.value,
@@ -696,8 +714,12 @@ impl<'a> InferenceContext<'a> {
                 }
             }
             Expr::Tuple { exprs } => {
-                let mut tys = match expected.ty.kind(&Interner) {
-                    TyKind::Tuple(_, substs) => substs
+                let mut tys = match expected
+                    .only_has_type(&mut self.table)
+                    .as_ref()
+                    .map(|t| t.kind(&Interner))
+                {
+                    Some(TyKind::Tuple(_, substs)) => substs
                         .iter(&Interner)
                         .map(|a| a.assert_ty_ref(&Interner).clone())
                         .chain(repeat_with(|| self.table.new_type_var()))
@@ -713,14 +735,16 @@ impl<'a> InferenceContext<'a> {
                 TyKind::Tuple(tys.len(), Substitution::from_iter(&Interner, tys)).intern(&Interner)
             }
             Expr::Array(array) => {
-                let elem_ty = match expected.ty.kind(&Interner) {
-                    TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
-                    _ => self.table.new_type_var(),
-                };
+                let elem_ty =
+                    match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(&Interner)) {
+                        Some(TyKind::Array(st, _)) | Some(TyKind::Slice(st)) => st.clone(),
+                        _ => self.table.new_type_var(),
+                    };
 
                 let len = match array {
                     Array::ElementList(items) => {
                         for expr in items.iter() {
+                            // FIXME: use CoerceMany (coerce_merge_branch)
                             self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
                         }
                         Some(items.len() as u64)
@@ -785,7 +809,6 @@ impl<'a> InferenceContext<'a> {
         };
         // use a new type variable if we got unknown here
         let ty = self.insert_type_vars_shallow(ty);
-        let ty = self.resolve_ty_as_possible(ty);
         self.write_expr_ty(tgt_expr, ty.clone());
         ty
     }
@@ -813,7 +836,6 @@ impl<'a> InferenceContext<'a> {
                         }
                     }
 
-                    let ty = self.resolve_ty_as_possible(ty);
                     self.infer_pat(*pat, &ty, BindingMode::default());
                 }
                 Statement::Expr { expr, .. } => {
@@ -836,7 +858,9 @@ impl<'a> InferenceContext<'a> {
                 // we don't even make an attempt at coercion
                 self.table.new_maybe_never_var()
             } else {
-                self.coerce(&TyBuilder::unit(), &expected.coercion_target());
+                if let Some(t) = expected.only_has_type(&mut self.table) {
+                    self.coerce(&TyBuilder::unit(), &t);
+                }
                 TyBuilder::unit()
             }
         };
@@ -852,7 +876,7 @@ impl<'a> InferenceContext<'a> {
         generic_args: Option<&GenericArgs>,
     ) -> Ty {
         let receiver_ty = self.infer_expr(receiver, &Expectation::none());
-        let canonicalized_receiver = self.canonicalizer().canonicalize_ty(receiver_ty.clone());
+        let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 
         let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
 
@@ -891,7 +915,8 @@ impl<'a> InferenceContext<'a> {
         };
         // Apply autoref so the below unification works correctly
         // FIXME: return correct autorefs from lookup_method
-        let actual_receiver_ty = match expected_receiver_ty.as_reference() {
+        let actual_receiver_ty = match self.resolve_ty_shallow(&expected_receiver_ty).as_reference()
+        {
             Some((_, lifetime, mutability)) => {
                 TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner)
             }
@@ -971,6 +996,7 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
+        let callable_ty = self.resolve_ty_shallow(&callable_ty);
         if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) {
             let def: CallableDefId = from_chalk(self.db, *fn_def);
             let generic_predicates = self.db.generic_predicates(def.into());
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index b15f4977dd..9c8e3b6aea 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -94,14 +94,15 @@ impl<'a> InferenceContext<'a> {
     pub(super) fn infer_pat(
         &mut self,
         pat: PatId,
-        mut expected: &Ty,
+        expected: &Ty,
         mut default_bm: BindingMode,
     ) -> Ty {
         let body = Arc::clone(&self.body); // avoid borrow checker problem
+        let mut expected = self.resolve_ty_shallow(expected);
 
         if is_non_ref_pat(&body, pat) {
             while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
-                expected = inner;
+                expected = self.resolve_ty_shallow(inner);
                 default_bm = match default_bm {
                     BindingMode::Move => BindingMode::Ref(mutability),
                     BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not),
@@ -147,9 +148,9 @@ impl<'a> InferenceContext<'a> {
             }
             Pat::Or(ref pats) => {
                 if let Some((first_pat, rest)) = pats.split_first() {
-                    let ty = self.infer_pat(*first_pat, expected, default_bm);
+                    let ty = self.infer_pat(*first_pat, &expected, default_bm);
                     for pat in rest {
-                        self.infer_pat(*pat, expected, default_bm);
+                        self.infer_pat(*pat, &expected, default_bm);
                     }
                     ty
                 } else {
@@ -173,13 +174,13 @@ impl<'a> InferenceContext<'a> {
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
                 p.as_deref(),
                 subpats,
-                expected,
+                &expected,
                 default_bm,
                 pat,
                 *ellipsis,
             ),
             Pat::Record { path: p, args: fields, ellipsis: _ } => {
-                self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat)
+                self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat)
             }
             Pat::Path(path) => {
                 // FIXME use correct resolver for the surrounding expression
@@ -193,7 +194,7 @@ impl<'a> InferenceContext<'a> {
                     BindingMode::convert(*mode)
                 };
                 let inner_ty = if let Some(subpat) = subpat {
-                    self.infer_pat(*subpat, expected, default_bm)
+                    self.infer_pat(*subpat, &expected, default_bm)
                 } else {
                     expected.clone()
                 };
@@ -206,7 +207,6 @@ impl<'a> InferenceContext<'a> {
                     }
                     BindingMode::Move => inner_ty.clone(),
                 };
-                let bound_ty = self.resolve_ty_as_possible(bound_ty);
                 self.write_pat_ty(pat, bound_ty);
                 return inner_ty;
             }
@@ -265,13 +265,12 @@ impl<'a> InferenceContext<'a> {
         };
         // use a new type variable if we got error type here
         let ty = self.insert_type_vars_shallow(ty);
-        if !self.unify(&ty, expected) {
+        if !self.unify(&ty, &expected) {
             self.result.type_mismatches.insert(
                 pat.into(),
                 TypeMismatch { expected: expected.clone(), actual: ty.clone() },
             );
         }
-        let ty = self.resolve_ty_as_possible(ty);
         self.write_pat_ty(pat, ty.clone());
         ty
     }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 495282eba8..14c99eafd3 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -65,7 +65,6 @@ impl<'a> InferenceContext<'a> {
         let typable: ValueTyDefId = match value {
             ValueNs::LocalBinding(pat) => {
                 let ty = self.result.type_of_pat.get(pat)?.clone();
-                let ty = self.resolve_ty_as_possible(ty);
                 return Some(ty);
             }
             ValueNs::FunctionId(it) => it.into(),
@@ -218,14 +217,14 @@ impl<'a> InferenceContext<'a> {
             return Some(result);
         }
 
-        let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
+        let canonical_ty = self.canonicalize(ty.clone());
         let krate = self.resolver.krate()?;
         let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
 
         method_resolution::iterate_method_candidates(
             &canonical_ty.value,
             self.db,
-            self.trait_env.clone(),
+            self.table.trait_env.clone(),
             krate,
             &traits_in_scope,
             None,
@@ -275,6 +274,7 @@ impl<'a> InferenceContext<'a> {
         name: &Name,
         id: ExprOrPatId,
     ) -> Option<(ValueNs, Option<Substitution>)> {
+        let ty = self.resolve_ty_shallow(ty);
         let (enum_id, subst) = match ty.as_adt() {
             Some((AdtId::EnumId(e), subst)) => (e, subst),
             _ => return None,
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index d8e0b43206..21d3fb54e9 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -1,177 +1,95 @@
 //! Unification and canonicalization logic.
 
-use std::borrow::Cow;
+use std::{fmt, mem, sync::Arc};
 
 use chalk_ir::{
-    cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex,
-    VariableKind,
+    cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, TyVariableKind,
+    UniverseIndex,
 };
-use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
+use chalk_solve::infer::ParameterEnaVariableExt;
+use ena::unify::UnifyKey;
 
-use super::{DomainGoal, InferenceContext};
+use super::{InferOk, InferResult, InferenceContext, TypeError};
 use crate::{
-    fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
-    DebruijnIndex, FnPointer, FnSubst, InEnvironment, InferenceVar, Interner, Scalar, Substitution,
-    Ty, TyExt, TyKind, WhereClause,
+    db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical,
+    DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, ProjectionTy,
+    Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
 };
 
 impl<'a> InferenceContext<'a> {
-    pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b>
+    pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+        &mut self,
+        t: T,
+    ) -> Canonicalized<T::Result>
     where
-        'a: 'b,
+        T::Result: HasInterner<Interner = Interner>,
     {
-        Canonicalizer { ctx: self, free_vars: Vec::new(), var_stack: Vec::new() }
+        // try to resolve obligations before canonicalizing, since this might
+        // result in new knowledge about variables
+        self.resolve_obligations_as_possible();
+        self.table.canonicalize(t)
     }
 }
 
-pub(super) struct Canonicalizer<'a, 'b>
-where
-    'a: 'b,
-{
-    ctx: &'b mut InferenceContext<'a>,
-    free_vars: Vec<(InferenceVar, TyVariableKind)>,
-    /// A stack of type variables that is used to detect recursive types (which
-    /// are an error, but we need to protect against them to avoid stack
-    /// overflows).
-    var_stack: Vec<TypeVarId>,
-}
-
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub(super) struct Canonicalized<T>
 where
     T: HasInterner<Interner = Interner>,
 {
     pub(super) value: Canonical<T>,
-    free_vars: Vec<(InferenceVar, TyVariableKind)>,
-}
-
-impl<'a, 'b> Canonicalizer<'a, 'b> {
-    fn add(&mut self, free_var: InferenceVar, kind: TyVariableKind) -> usize {
-        self.free_vars.iter().position(|&(v, _)| v == free_var).unwrap_or_else(|| {
-            let next_index = self.free_vars.len();
-            self.free_vars.push((free_var, kind));
-            next_index
-        })
-    }
-
-    fn do_canonicalize<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
-        &mut self,
-        t: T,
-        binders: DebruijnIndex,
-    ) -> T {
-        fold_tys(
-            t,
-            |ty, binders| match ty.kind(&Interner) {
-                &TyKind::InferenceVar(var, kind) => {
-                    let inner = from_inference_var(var);
-                    if self.var_stack.contains(&inner) {
-                        // recursive type
-                        return self.ctx.table.type_variable_table.fallback_value(var, kind);
-                    }
-                    if let Some(known_ty) =
-                        self.ctx.table.var_unification_table.inlined_probe_value(inner).known()
-                    {
-                        self.var_stack.push(inner);
-                        let result = self.do_canonicalize(known_ty.clone(), binders);
-                        self.var_stack.pop();
-                        result
-                    } else {
-                        let root = self.ctx.table.var_unification_table.find(inner);
-                        let position = self.add(to_inference_var(root), kind);
-                        TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner)
-                    }
-                }
-                _ => ty,
-            },
-            binders,
-        )
-    }
-
-    fn into_canonicalized<T: HasInterner<Interner = Interner>>(
-        self,
-        result: T,
-    ) -> Canonicalized<T> {
-        let kinds = self
-            .free_vars
-            .iter()
-            .map(|&(_, k)| chalk_ir::WithKind::new(VariableKind::Ty(k), UniverseIndex::ROOT));
-        Canonicalized {
-            value: Canonical {
-                value: result,
-                binders: CanonicalVarKinds::from_iter(&Interner, kinds),
-            },
-            free_vars: self.free_vars,
-        }
-    }
-
-    pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
-        let result = self.do_canonicalize(ty, DebruijnIndex::INNERMOST);
-        self.into_canonicalized(result)
-    }
-
-    pub(crate) fn canonicalize_obligation(
-        mut self,
-        obligation: InEnvironment<DomainGoal>,
-    ) -> Canonicalized<InEnvironment<DomainGoal>> {
-        let result = match obligation.goal {
-            DomainGoal::Holds(wc) => {
-                DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST))
-            }
-            _ => unimplemented!(),
-        };
-        self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment })
-    }
+    free_vars: Vec<GenericArg>,
 }
 
 impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
     pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty {
-        crate::fold_free_vars(ty, |bound, _binders| {
-            let (v, k) = self.free_vars[bound.index];
-            TyKind::InferenceVar(v, k).intern(&Interner)
-        })
+        chalk_ir::Substitute::apply(&self.free_vars, ty, &Interner)
     }
 
     pub(super) fn apply_solution(
         &self,
-        ctx: &mut InferenceContext<'_>,
+        ctx: &mut InferenceTable,
         solution: Canonical<Substitution>,
     ) {
         // the solution may contain new variables, which we need to convert to new inference vars
         let new_vars = Substitution::from_iter(
             &Interner,
             solution.binders.iter(&Interner).map(|k| match k.kind {
-                VariableKind::Ty(TyVariableKind::General) => {
-                    ctx.table.new_type_var().cast(&Interner)
-                }
-                VariableKind::Ty(TyVariableKind::Integer) => {
-                    ctx.table.new_integer_var().cast(&Interner)
-                }
-                VariableKind::Ty(TyVariableKind::Float) => {
-                    ctx.table.new_float_var().cast(&Interner)
-                }
+                VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(&Interner),
+                VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(&Interner),
+                VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(&Interner),
                 // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere
                 VariableKind::Lifetime => static_lifetime().cast(&Interner),
                 _ => panic!("const variable in solution"),
             }),
         );
-        for (i, ty) in solution.value.iter(&Interner).enumerate() {
-            let (v, k) = self.free_vars[i];
-            // eagerly replace projections in the type; we may be getting types
-            // e.g. from where clauses where this hasn't happened yet
-            let ty = ctx.normalize_associated_types_in(
-                new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner),
-            );
-            ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty);
+        for (i, v) in solution.value.iter(&Interner).enumerate() {
+            let var = self.free_vars[i].clone();
+            if let Some(ty) = v.ty(&Interner) {
+                // eagerly replace projections in the type; we may be getting types
+                // e.g. from where clauses where this hasn't happened yet
+                let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), &Interner));
+                ctx.unify(var.assert_ty_ref(&Interner), &ty);
+            } else {
+                let _ = ctx.try_unify(&var, &new_vars.apply(v.clone(), &Interner));
+            }
         }
     }
 }
 
-pub fn could_unify(t1: &Ty, t2: &Ty) -> bool {
-    InferenceTable::new().unify(t1, t2)
+pub fn could_unify(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    tys: &Canonical<(Ty, Ty)>,
+) -> bool {
+    unify(db, env, tys).is_some()
 }
 
-pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> {
-    let mut table = InferenceTable::new();
+pub(crate) fn unify(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    tys: &Canonical<(Ty, Ty)>,
+) -> Option<Substitution> {
+    let mut table = InferenceTable::new(db, env);
     let vars = Substitution::from_iter(
         &Interner,
         tys.binders
@@ -187,77 +105,151 @@ pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> {
     }
     // default any type vars that weren't unified back to their original bound vars
     // (kind of hacky)
-    for (i, var) in vars.iter(&Interner).enumerate() {
-        let var = var.assert_ty_ref(&Interner);
-        if &*table.resolve_ty_shallow(var) == var {
-            table.unify(
-                var,
-                &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i)).intern(&Interner),
-            );
-        }
-    }
+    let find_var = |iv| {
+        vars.iter(&Interner).position(|v| match v.interned() {
+            chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner),
+            chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner),
+            chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner),
+        } == Some(iv))
+    };
+    let fallback = |iv, kind, default, binder| match kind {
+        chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_ty(&Interner).cast(&Interner)),
+        chalk_ir::VariableKind::Lifetime => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(&Interner).cast(&Interner)),
+        chalk_ir::VariableKind::Const(ty) => find_var(iv)
+            .map_or(default, |i| BoundVar::new(binder, i).to_const(&Interner, ty).cast(&Interner)),
+    };
     Some(Substitution::from_iter(
         &Interner,
         vars.iter(&Interner)
-            .map(|v| table.resolve_ty_completely(v.assert_ty_ref(&Interner).clone())),
+            .map(|v| table.resolve_with_fallback(v.assert_ty_ref(&Interner).clone(), fallback)),
     ))
 }
 
-#[derive(Clone, Debug)]
-pub(super) struct TypeVariableTable {
-    inner: Vec<TypeVariableData>,
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct TypeVariableData {
+    diverging: bool,
 }
 
-impl TypeVariableTable {
-    fn push(&mut self, data: TypeVariableData) {
-        self.inner.push(data);
+type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
+
+#[derive(Clone)]
+pub(crate) struct InferenceTable<'a> {
+    pub(crate) db: &'a dyn HirDatabase,
+    pub(crate) trait_env: Arc<TraitEnvironment>,
+    var_unification_table: ChalkInferenceTable,
+    type_variable_table: Vec<TypeVariableData>,
+    pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
+}
+
+impl<'a> InferenceTable<'a> {
+    pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc<TraitEnvironment>) -> Self {
+        InferenceTable {
+            db,
+            trait_env,
+            var_unification_table: ChalkInferenceTable::new(),
+            type_variable_table: Vec::new(),
+            pending_obligations: Vec::new(),
+        }
     }
 
-    pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
-        self.inner[from_inference_var(iv).0 as usize].diverging = diverging;
+    /// Chalk doesn't know about the `diverging` flag, so when it unifies two
+    /// type variables of which one is diverging, the chosen root might not be
+    /// diverging and we have no way of marking it as such at that time. This
+    /// function goes through all type variables and make sure their root is
+    /// marked as diverging if necessary, so that resolving them gives the right
+    /// result.
+    pub(super) fn propagate_diverging_flag(&mut self) {
+        for i in 0..self.type_variable_table.len() {
+            if !self.type_variable_table[i].diverging {
+                continue;
+            }
+            let v = InferenceVar::from(i as u32);
+            let root = self.var_unification_table.inference_var_root(v);
+            if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) {
+                data.diverging = true;
+            }
+        }
     }
 
-    fn is_diverging(&mut self, iv: InferenceVar) -> bool {
-        self.inner[from_inference_var(iv).0 as usize].diverging
+    pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
+        self.type_variable_table[iv.index() as usize].diverging = diverging;
     }
 
     fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
         match kind {
-            _ if self.inner[from_inference_var(iv).0 as usize].diverging => TyKind::Never,
+            _ if self
+                .type_variable_table
+                .get(iv.index() as usize)
+                .map_or(false, |data| data.diverging) =>
+            {
+                TyKind::Never
+            }
             TyVariableKind::General => TyKind::Error,
             TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
             TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
         }
         .intern(&Interner)
     }
-}
 
-#[derive(Copy, Clone, Debug)]
-pub(crate) struct TypeVariableData {
-    diverging: bool,
-}
+    pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+        &mut self,
+        t: T,
+    ) -> Canonicalized<T::Result>
+    where
+        T::Result: HasInterner<Interner = Interner>,
+    {
+        let result = self.var_unification_table.canonicalize(&Interner, t);
+        let free_vars = result
+            .free_vars
+            .into_iter()
+            .map(|free_var| free_var.to_generic_arg(&Interner))
+            .collect();
+        Canonicalized { value: result.quantified, free_vars }
+    }
+
+    /// Recurses through the given type, normalizing associated types mentioned
+    /// in it by replacing them by type variables and registering obligations to
+    /// resolve later. This should be done once for every type we get from some
+    /// type annotation (e.g. from a let type annotation, field type or function
+    /// call). `make_ty` handles this already, but e.g. for field types we need
+    /// to do it as well.
+    pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
+        fold_tys(
+            ty,
+            |ty, _| match ty.kind(&Interner) {
+                TyKind::Alias(AliasTy::Projection(proj_ty)) => {
+                    self.normalize_projection_ty(proj_ty.clone())
+                }
+                _ => ty,
+            },
+            DebruijnIndex::INNERMOST,
+        )
+    }
 
-#[derive(Clone, Debug)]
-pub(crate) struct InferenceTable {
-    pub(super) var_unification_table: InPlaceUnificationTable<TypeVarId>,
-    pub(super) type_variable_table: TypeVariableTable,
-    pub(super) revision: u32,
-}
+    pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
+        let var = self.new_type_var();
+        let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
+        let obligation = alias_eq.cast(&Interner);
+        self.register_obligation(obligation);
+        var
+    }
 
-impl InferenceTable {
-    pub(crate) fn new() -> Self {
-        InferenceTable {
-            var_unification_table: InPlaceUnificationTable::new(),
-            type_variable_table: TypeVariableTable { inner: Vec::new() },
-            revision: 0,
-        }
+    fn extend_type_variable_table(&mut self, to_index: usize) {
+        self.type_variable_table.extend(
+            (0..1 + to_index - self.type_variable_table.len())
+                .map(|_| TypeVariableData { diverging: false }),
+        );
     }
 
     fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
-        self.type_variable_table.push(TypeVariableData { diverging });
-        let key = self.var_unification_table.new_key(TypeVarValue::Unknown);
-        assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1);
-        TyKind::InferenceVar(to_inference_var(key), kind).intern(&Interner)
+        let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
+        // Chalk might have created some type variables for its own purposes that we don't know about...
+        self.extend_type_variable_table(var.index() as usize);
+        assert_eq!(var.index() as usize, self.type_variable_table.len() - 1);
+        self.type_variable_table[var.index() as usize].diverging = diverging;
+        var.to_ty_with_kind(&Interner, kind)
     }
 
     pub(crate) fn new_type_var(&mut self) -> Ty {
@@ -276,350 +268,261 @@ impl InferenceTable {
         self.new_var(TyVariableKind::General, true)
     }
 
-    pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
-        self.resolve_ty_completely_inner(&mut Vec::new(), ty)
+    pub(crate) fn resolve_with_fallback<T>(
+        &mut self,
+        t: T,
+        fallback: impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+    ) -> T::Result
+    where
+        T: HasInterner<Interner = Interner> + Fold<Interner>,
+    {
+        self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
     }
 
-    pub(crate) fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
-        self.resolve_ty_as_possible_inner(&mut Vec::new(), ty)
+    fn resolve_with_fallback_inner<T>(
+        &mut self,
+        var_stack: &mut Vec<InferenceVar>,
+        t: T,
+        fallback: &impl Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+    ) -> T::Result
+    where
+        T: HasInterner<Interner = Interner> + Fold<Interner>,
+    {
+        t.fold_with(
+            &mut resolve::Resolver { table: self, var_stack, fallback },
+            DebruijnIndex::INNERMOST,
+        )
+        .expect("fold failed unexpectedly")
     }
 
-    pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
-        self.unify_inner(ty1, ty2, 0)
+    pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
+        self.resolve_with_fallback(ty, |_, _, d, _| d)
     }
 
-    pub(crate) fn unify_substs(
-        &mut self,
-        substs1: &Substitution,
-        substs2: &Substitution,
-        depth: usize,
-    ) -> bool {
-        substs1.iter(&Interner).zip(substs2.iter(&Interner)).all(|(t1, t2)| {
-            self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth)
-        })
+    /// Unify two types and register new trait goals that arise from that.
+    pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
+        let result = if let Ok(r) = self.try_unify(ty1, ty2) {
+            r
+        } else {
+            return false;
+        };
+        self.register_infer_ok(result);
+        true
     }
 
-    fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
-        if depth > 1000 {
-            // prevent stackoverflows
-            panic!("infinite recursion in unification");
-        }
-        if ty1 == ty2 {
-            return true;
-        }
-        // try to resolve type vars first
-        let ty1 = self.resolve_ty_shallow(ty1);
-        let ty2 = self.resolve_ty_shallow(ty2);
-        if ty1.equals_ctor(&ty2) {
-            match (ty1.kind(&Interner), ty2.kind(&Interner)) {
-                (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2))
-                | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2))
-                | (
-                    TyKind::Function(FnPointer { substitution: FnSubst(substs1), .. }),
-                    TyKind::Function(FnPointer { substitution: FnSubst(substs2), .. }),
-                )
-                | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2))
-                | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2))
-                | (TyKind::AssociatedType(_, substs1), TyKind::AssociatedType(_, substs2))
-                | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => {
-                    self.unify_substs(substs1, substs2, depth + 1)
-                }
-                (TyKind::Array(ty1, c1), TyKind::Array(ty2, c2)) if c1 == c2 => {
-                    self.unify_inner(ty1, ty2, depth + 1)
-                }
-                (TyKind::Ref(_, _, ty1), TyKind::Ref(_, _, ty2))
-                | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2))
-                | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1),
-                _ => true, /* we checked equals_ctor already */
-            }
-        } else if let (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) =
-            (ty1.kind(&Interner), ty2.kind(&Interner))
-        {
-            self.unify_substs(substs1, substs2, depth + 1)
-        } else {
-            self.unify_inner_trivial(&ty1, &ty2, depth)
+    /// Unify two types and return new trait goals arising from it, so the
+    /// caller needs to deal with them.
+    pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult {
+        match self.var_unification_table.relate(
+            &Interner,
+            &self.db,
+            &self.trait_env.env,
+            chalk_ir::Variance::Invariant,
+            t1,
+            t2,
+        ) {
+            Ok(result) => Ok(InferOk { goals: result.goals }),
+            Err(chalk_ir::NoSolution) => Err(TypeError),
         }
     }
 
-    pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
-        match (ty1.kind(&Interner), ty2.kind(&Interner)) {
-            (TyKind::Error, _) | (_, TyKind::Error) => true,
+    /// If `ty` is a type variable with known type, returns that type;
+    /// otherwise, return ty.
+    pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
+        self.var_unification_table.normalize_ty_shallow(&Interner, ty).unwrap_or_else(|| ty.clone())
+    }
 
-            (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true,
+    pub(crate) fn register_obligation(&mut self, goal: Goal) {
+        let in_env = InEnvironment::new(&self.trait_env.env, goal);
+        self.register_obligation_in_env(in_env)
+    }
 
-            (TyKind::Dyn(dyn1), TyKind::Dyn(dyn2))
-                if dyn1.bounds.skip_binders().interned().len()
-                    == dyn2.bounds.skip_binders().interned().len() =>
-            {
-                for (pred1, pred2) in dyn1
-                    .bounds
-                    .skip_binders()
-                    .interned()
-                    .iter()
-                    .zip(dyn2.bounds.skip_binders().interned().iter())
-                {
-                    if !self.unify_preds(pred1.skip_binders(), pred2.skip_binders(), depth + 1) {
-                        return false;
-                    }
-                }
-                true
-            }
+    fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
+        let canonicalized = self.canonicalize(goal);
+        if !self.try_resolve_obligation(&canonicalized) {
+            self.pending_obligations.push(canonicalized);
+        }
+    }
 
-            (
-                TyKind::InferenceVar(tv1, TyVariableKind::General),
-                TyKind::InferenceVar(tv2, TyVariableKind::General),
-            )
-            | (
-                TyKind::InferenceVar(tv1, TyVariableKind::Integer),
-                TyKind::InferenceVar(tv2, TyVariableKind::Integer),
-            )
-            | (
-                TyKind::InferenceVar(tv1, TyVariableKind::Float),
-                TyKind::InferenceVar(tv2, TyVariableKind::Float),
-            ) if self.type_variable_table.is_diverging(*tv1)
-                == self.type_variable_table.is_diverging(*tv2) =>
-            {
-                // both type vars are unknown since we tried to resolve them
-                if !self
-                    .var_unification_table
-                    .unioned(from_inference_var(*tv1), from_inference_var(*tv2))
-                {
-                    self.var_unification_table
-                        .union(from_inference_var(*tv1), from_inference_var(*tv2));
-                    self.revision += 1;
-                }
-                true
-            }
+    pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) {
+        infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal));
+    }
 
-            // The order of MaybeNeverTypeVar matters here.
-            // Unifying MaybeNeverTypeVar and TypeVar will let the latter become MaybeNeverTypeVar.
-            // Unifying MaybeNeverTypeVar and other concrete type will let the former become it.
-            (TyKind::InferenceVar(tv, TyVariableKind::General), other)
-            | (other, TyKind::InferenceVar(tv, TyVariableKind::General))
-            | (
-                TyKind::InferenceVar(tv, TyVariableKind::Integer),
-                other @ TyKind::Scalar(Scalar::Int(_)),
-            )
-            | (
-                other @ TyKind::Scalar(Scalar::Int(_)),
-                TyKind::InferenceVar(tv, TyVariableKind::Integer),
-            )
-            | (
-                TyKind::InferenceVar(tv, TyVariableKind::Integer),
-                other @ TyKind::Scalar(Scalar::Uint(_)),
-            )
-            | (
-                other @ TyKind::Scalar(Scalar::Uint(_)),
-                TyKind::InferenceVar(tv, TyVariableKind::Integer),
-            )
-            | (
-                TyKind::InferenceVar(tv, TyVariableKind::Float),
-                other @ TyKind::Scalar(Scalar::Float(_)),
-            )
-            | (
-                other @ TyKind::Scalar(Scalar::Float(_)),
-                TyKind::InferenceVar(tv, TyVariableKind::Float),
-            ) => {
-                // the type var is unknown since we tried to resolve it
-                self.var_unification_table.union_value(
-                    from_inference_var(*tv),
-                    TypeVarValue::Known(other.clone().intern(&Interner)),
+    pub(crate) fn resolve_obligations_as_possible(&mut self) {
+        let _span = profile::span("resolve_obligations_as_possible");
+        let mut changed = true;
+        let mut obligations = Vec::new();
+        while changed {
+            changed = false;
+            mem::swap(&mut self.pending_obligations, &mut obligations);
+            for canonicalized in obligations.drain(..) {
+                if !self.check_changed(&canonicalized) {
+                    self.pending_obligations.push(canonicalized);
+                    continue;
+                }
+                changed = true;
+                let uncanonical = chalk_ir::Substitute::apply(
+                    &canonicalized.free_vars,
+                    canonicalized.value.value,
+                    &Interner,
                 );
-                self.revision += 1;
-                true
+                self.register_obligation_in_env(uncanonical);
             }
-
-            _ => false,
         }
     }
 
-    fn unify_preds(&mut self, pred1: &WhereClause, pred2: &WhereClause, depth: usize) -> bool {
-        match (pred1, pred2) {
-            (WhereClause::Implemented(tr1), WhereClause::Implemented(tr2))
-                if tr1.trait_id == tr2.trait_id =>
-            {
-                self.unify_substs(&tr1.substitution, &tr2.substitution, depth + 1)
+    /// This checks whether any of the free variables in the `canonicalized`
+    /// have changed (either been unified with another variable, or with a
+    /// value). If this is not the case, we don't need to try to solve the goal
+    /// again -- it'll give the same result as last time.
+    fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
+        canonicalized.free_vars.iter().any(|var| {
+            let iv = match var.data(&Interner) {
+                chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(&Interner),
+                chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(&Interner),
+                chalk_ir::GenericArgData::Const(c) => c.inference_var(&Interner),
             }
-            (
-                WhereClause::AliasEq(AliasEq { alias: alias1, ty: ty1 }),
-                WhereClause::AliasEq(AliasEq { alias: alias2, ty: ty2 }),
-            ) => {
-                let (substitution1, substitution2) = match (alias1, alias2) {
-                    (AliasTy::Projection(projection_ty1), AliasTy::Projection(projection_ty2))
-                        if projection_ty1.associated_ty_id == projection_ty2.associated_ty_id =>
-                    {
-                        (&projection_ty1.substitution, &projection_ty2.substitution)
-                    }
-                    (AliasTy::Opaque(opaque1), AliasTy::Opaque(opaque2))
-                        if opaque1.opaque_ty_id == opaque2.opaque_ty_id =>
-                    {
-                        (&opaque1.substitution, &opaque2.substitution)
-                    }
-                    _ => return false,
-                };
-                self.unify_substs(&substitution1, &substitution2, depth + 1)
-                    && self.unify_inner(&ty1, &ty2, depth + 1)
+            .expect("free var is not inference var");
+            if self.var_unification_table.probe_var(iv).is_some() {
+                return true;
             }
-            _ => false,
-        }
+            let root = self.var_unification_table.inference_var_root(iv);
+            iv != root
+        })
     }
 
-    /// If `ty` is a type variable with known type, returns that type;
-    /// otherwise, return ty.
-    pub(crate) fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
-        let mut ty = Cow::Borrowed(ty);
-        // The type variable could resolve to a int/float variable. Hence try
-        // resolving up to three times; each type of variable shouldn't occur
-        // more than once
-        for i in 0..3 {
-            if i > 0 {
-                cov_mark::hit!(type_var_resolves_to_int_var);
+    fn try_resolve_obligation(
+        &mut self,
+        canonicalized: &Canonicalized<InEnvironment<Goal>>,
+    ) -> bool {
+        let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone());
+
+        match solution {
+            Some(Solution::Unique(canonical_subst)) => {
+                canonicalized.apply_solution(
+                    self,
+                    Canonical {
+                        binders: canonical_subst.binders,
+                        // FIXME: handle constraints
+                        value: canonical_subst.value.subst,
+                    },
+                );
+                true
             }
-            match ty.kind(&Interner) {
-                TyKind::InferenceVar(tv, _) => {
-                    let inner = from_inference_var(*tv);
-                    match self.var_unification_table.inlined_probe_value(inner).known() {
-                        Some(known_ty) => {
-                            // The known_ty can't be a type var itself
-                            ty = Cow::Owned(known_ty.clone());
-                        }
-                        _ => return ty,
-                    }
-                }
-                _ => return ty,
+            Some(Solution::Ambig(Guidance::Definite(substs))) => {
+                canonicalized.apply_solution(self, substs);
+                false
+            }
+            Some(_) => {
+                // FIXME use this when trying to resolve everything at the end
+                false
+            }
+            None => {
+                // FIXME obligation cannot be fulfilled => diagnostic
+                true
             }
         }
-        log::error!("Inference variable still not resolved: {:?}", ty);
-        ty
-    }
-
-    /// Resolves the type as far as currently possible, replacing type variables
-    /// by their known types. All types returned by the infer_* functions should
-    /// be resolved as far as possible, i.e. contain no type variables with
-    /// known type.
-    fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
-        fold_tys(
-            ty,
-            |ty, _| match ty.kind(&Interner) {
-                &TyKind::InferenceVar(tv, kind) => {
-                    let inner = from_inference_var(tv);
-                    if tv_stack.contains(&inner) {
-                        cov_mark::hit!(type_var_cycles_resolve_as_possible);
-                        // recursive type
-                        return self.type_variable_table.fallback_value(tv, kind);
-                    }
-                    if let Some(known_ty) =
-                        self.var_unification_table.inlined_probe_value(inner).known()
-                    {
-                        // known_ty may contain other variables that are known by now
-                        tv_stack.push(inner);
-                        let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone());
-                        tv_stack.pop();
-                        result
-                    } else {
-                        ty
-                    }
-                }
-                _ => ty,
-            },
-            DebruijnIndex::INNERMOST,
-        )
-    }
-
-    /// Resolves the type completely; type variables without known type are
-    /// replaced by TyKind::Unknown.
-    fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
-        fold_tys(
-            ty,
-            |ty, _| match ty.kind(&Interner) {
-                &TyKind::InferenceVar(tv, kind) => {
-                    let inner = from_inference_var(tv);
-                    if tv_stack.contains(&inner) {
-                        cov_mark::hit!(type_var_cycles_resolve_completely);
-                        // recursive type
-                        return self.type_variable_table.fallback_value(tv, kind);
-                    }
-                    if let Some(known_ty) =
-                        self.var_unification_table.inlined_probe_value(inner).known()
-                    {
-                        // known_ty may contain other variables that are known by now
-                        tv_stack.push(inner);
-                        let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone());
-                        tv_stack.pop();
-                        result
-                    } else {
-                        self.type_variable_table.fallback_value(tv, kind)
-                    }
-                }
-                _ => ty,
-            },
-            DebruijnIndex::INNERMOST,
-        )
     }
 }
 
-/// The ID of a type variable.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub(super) struct TypeVarId(pub(super) u32);
-
-impl UnifyKey for TypeVarId {
-    type Value = TypeVarValue;
-
-    fn index(&self) -> u32 {
-        self.0
-    }
-
-    fn from_index(i: u32) -> Self {
-        TypeVarId(i)
+impl<'a> fmt::Debug for InferenceTable<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish()
     }
-
-    fn tag() -> &'static str {
-        "TypeVarId"
-    }
-}
-
-fn from_inference_var(var: InferenceVar) -> TypeVarId {
-    TypeVarId(var.index())
-}
-
-fn to_inference_var(TypeVarId(index): TypeVarId) -> InferenceVar {
-    index.into()
-}
-
-/// The value of a type variable: either we already know the type, or we don't
-/// know it yet.
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub(super) enum TypeVarValue {
-    Known(Ty),
-    Unknown,
 }
 
-impl TypeVarValue {
-    fn known(&self) -> Option<&Ty> {
-        match self {
-            TypeVarValue::Known(ty) => Some(ty),
-            TypeVarValue::Unknown => None,
+mod resolve {
+    use super::InferenceTable;
+    use crate::{
+        ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar,
+        Interner, Ty, TyVariableKind, VariableKind,
+    };
+    use chalk_ir::{
+        cast::Cast,
+        fold::{Fold, Folder},
+        Fallible,
+    };
+    use hir_def::type_ref::ConstScalar;
+
+    pub(super) struct Resolver<'a, 'b, F> {
+        pub(super) table: &'a mut InferenceTable<'b>,
+        pub(super) var_stack: &'a mut Vec<InferenceVar>,
+        pub(super) fallback: F,
+    }
+    impl<'a, 'b, 'i, F> Folder<'i, Interner> for Resolver<'a, 'b, F>
+    where
+        F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i,
+    {
+        fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> {
+            self
         }
-    }
-}
-
-impl UnifyValue for TypeVarValue {
-    type Error = NoError;
 
-    fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
-        match (value1, value2) {
-            // We should never equate two type variables, both of which have
-            // known types. Instead, we recursively equate those types.
-            (TypeVarValue::Known(t1), TypeVarValue::Known(t2)) => panic!(
-                "equating two type variables, both of which have known types: {:?} and {:?}",
-                t1, t2
-            ),
+        fn interner(&self) -> &'i Interner {
+            &Interner
+        }
 
-            // If one side is known, prefer that one.
-            (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()),
-            (TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()),
+        fn fold_inference_ty(
+            &mut self,
+            var: InferenceVar,
+            kind: TyVariableKind,
+            outer_binder: DebruijnIndex,
+        ) -> Fallible<Ty> {
+            let var = self.table.var_unification_table.inference_var_root(var);
+            if self.var_stack.contains(&var) {
+                // recursive type
+                let default = self.table.fallback_value(var, kind).cast(&Interner);
+                return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+                    .assert_ty_ref(&Interner)
+                    .clone());
+            }
+            let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
+                // known_ty may contain other variables that are known by now
+                self.var_stack.push(var);
+                let result =
+                    known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+                self.var_stack.pop();
+                result.assert_ty_ref(&Interner).clone()
+            } else {
+                let default = self.table.fallback_value(var, kind).cast(&Interner);
+                (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+                    .assert_ty_ref(&Interner)
+                    .clone()
+            };
+            Ok(result)
+        }
 
-            (TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown),
+        fn fold_inference_const(
+            &mut self,
+            ty: Ty,
+            var: InferenceVar,
+            outer_binder: DebruijnIndex,
+        ) -> Fallible<Const> {
+            let var = self.table.var_unification_table.inference_var_root(var);
+            let default = ConstData {
+                ty: ty.clone(),
+                value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }),
+            }
+            .intern(&Interner)
+            .cast(&Interner);
+            if self.var_stack.contains(&var) {
+                // recursive
+                return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+                    .assert_const_ref(&Interner)
+                    .clone());
+            }
+            let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
+                // known_ty may contain other variables that are known by now
+                self.var_stack.push(var);
+                let result =
+                    known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+                self.var_stack.pop();
+                result.assert_const_ref(&Interner).clone()
+            } else {
+                (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+                    .assert_const_ref(&Interner)
+                    .clone()
+            };
+            Ok(result)
         }
     }
 }
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index 7b41197477..29ffdd9b7f 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -15,9 +15,15 @@ use std::{fmt, sync::Arc};
 #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
 pub struct Interner;
 
-#[derive(PartialEq, Eq, Hash, Debug)]
+#[derive(PartialEq, Eq, Hash)]
 pub struct InternedWrapper<T>(T);
 
+impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&self.0, f)
+    }
+}
+
 impl<T> std::ops::Deref for InternedWrapper<T> {
     type Target = T;
 
@@ -101,66 +107,65 @@ impl chalk_ir::interner::Interner for Interner {
         opaque_ty: &chalk_ir::OpaqueTy<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
+        Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
     }
 
     fn debug_opaque_ty_id(
         opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
+        Some(fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish())
     }
 
     fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
+        Some(write!(fmt, "{:?}", ty.data(&Interner)))
     }
 
     fn debug_lifetime(
         lifetime: &chalk_ir::Lifetime<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_lifetime(lifetime, fmt)))
+        Some(write!(fmt, "{:?}", lifetime.data(&Interner)))
     }
 
     fn debug_generic_arg(
         parameter: &GenericArg,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
+        Some(write!(fmt, "{:?}", parameter.data(&Interner).inner_debug()))
     }
 
     fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_goal(goal, fmt)))
+        let goal_data = goal.data(&Interner);
+        Some(write!(fmt, "{:?}", goal_data))
     }
 
     fn debug_goals(
         goals: &chalk_ir::Goals<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_goals(goals, fmt)))
+        Some(write!(fmt, "{:?}", goals.debug(&Interner)))
     }
 
     fn debug_program_clause_implication(
         pci: &chalk_ir::ProgramClauseImplication<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_program_clause_implication(pci, fmt)))
+        Some(write!(fmt, "{:?}", pci.debug(&Interner)))
     }
 
     fn debug_substitution(
         substitution: &chalk_ir::Substitution<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_substitution(substitution, fmt)))
+        Some(write!(fmt, "{:?}", substitution.debug(&Interner)))
     }
 
     fn debug_separator_trait_ref(
         separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| {
-            Some(prog?.debug_separator_trait_ref(separator_trait_ref, fmt))
-        })
+        Some(write!(fmt, "{:?}", separator_trait_ref.debug(&Interner)))
     }
 
     fn debug_fn_def_id(
@@ -173,47 +178,43 @@ impl chalk_ir::interner::Interner for Interner {
         constant: &chalk_ir::Const<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_const(constant, fmt)))
+        Some(write!(fmt, "{:?}", constant.data(&Interner)))
     }
     fn debug_variable_kinds(
         variable_kinds: &chalk_ir::VariableKinds<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_variable_kinds(variable_kinds, fmt)))
+        Some(write!(fmt, "{:?}", variable_kinds.as_slice(&Interner)))
     }
     fn debug_variable_kinds_with_angles(
         variable_kinds: &chalk_ir::VariableKinds<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| {
-            Some(prog?.debug_variable_kinds_with_angles(variable_kinds, fmt))
-        })
+        Some(write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner)))
     }
     fn debug_canonical_var_kinds(
         canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| {
-            Some(prog?.debug_canonical_var_kinds(canonical_var_kinds, fmt))
-        })
+        Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner)))
     }
     fn debug_program_clause(
         clause: &chalk_ir::ProgramClause<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_program_clause(clause, fmt)))
+        Some(write!(fmt, "{:?}", clause.data(&Interner)))
     }
     fn debug_program_clauses(
         clauses: &chalk_ir::ProgramClauses<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_program_clauses(clauses, fmt)))
+        Some(write!(fmt, "{:?}", clauses.as_slice(&Interner)))
     }
     fn debug_quantified_where_clauses(
         clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Option<fmt::Result> {
-        tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
+        Some(write!(fmt, "{:?}", clauses.as_slice(&Interner)))
     }
 
     fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType {
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 15b61bedc1..72093d75a2 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -43,8 +43,9 @@ use hir_def::{
     type_ref::{ConstScalar, Rawness},
     TypeParamId,
 };
+use stdx::always;
 
-use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
+use crate::{db::HirDatabase, utils::generics};
 
 pub use autoderef::autoderef;
 pub use builder::TyBuilder;
@@ -113,6 +114,7 @@ pub type FnSig = chalk_ir::FnSig<Interner>;
 
 pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
 pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
+pub type Goal = chalk_ir::Goal<Interner>;
 pub type AliasEq = chalk_ir::AliasEq<Interner>;
 pub type Solution = chalk_solve::Solution<Interner>;
 pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
@@ -167,6 +169,7 @@ pub fn make_canonical<T: HasInterner<Interner = Interner>>(
     Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
 }
 
+// FIXME: get rid of this, just replace it by FnPointer
 /// A function signature as seen by type inference: Several parameter types and
 /// one return type.
 #[derive(Clone, PartialEq, Eq, Debug)]
@@ -203,6 +206,17 @@ impl CallableSig {
         }
     }
 
+    pub fn to_fn_ptr(&self) -> FnPointer {
+        FnPointer {
+            num_binders: 0,
+            sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs },
+            substitution: FnSubst(Substitution::from_iter(
+                &Interner,
+                self.params_and_return.iter().cloned(),
+            )),
+        }
+    }
+
     pub fn params(&self) -> &[Ty] {
         &self.params_and_return[0..self.params_and_return.len() - 1]
     }
@@ -314,3 +328,58 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
     }
     t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
 }
+
+pub fn replace_errors_with_variables<T>(t: T) -> Canonical<T::Result>
+where
+    T: HasInterner<Interner = Interner> + Fold<Interner>,
+    T::Result: HasInterner<Interner = Interner>,
+{
+    use chalk_ir::{
+        fold::{Folder, SuperFold},
+        Fallible,
+    };
+    struct ErrorReplacer {
+        vars: usize,
+    }
+    impl<'i> Folder<'i, Interner> for ErrorReplacer {
+        fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> {
+            self
+        }
+
+        fn interner(&self) -> &'i Interner {
+            &Interner
+        }
+
+        fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
+            if let TyKind::Error = ty.kind(&Interner) {
+                let index = self.vars;
+                self.vars += 1;
+                Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(&Interner))
+            } else {
+                let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
+                Ok(ty)
+            }
+        }
+
+        fn fold_inference_ty(
+            &mut self,
+            var: InferenceVar,
+            kind: TyVariableKind,
+            _outer_binder: DebruijnIndex,
+        ) -> Fallible<Ty> {
+            always!(false);
+            Ok(TyKind::InferenceVar(var, kind).intern(&Interner))
+        }
+    }
+    let mut error_replacer = ErrorReplacer { vars: 0 };
+    let value = t
+        .fold_with(&mut error_replacer, DebruijnIndex::INNERMOST)
+        .expect("fold failed unexpectedly");
+    let kinds = (0..error_replacer.vars).map(|_| {
+        chalk_ir::CanonicalVarKind::new(
+            chalk_ir::VariableKind::Ty(TyVariableKind::General),
+            chalk_ir::UniverseIndex::ROOT,
+        )
+    });
+    Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
+}
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index bd8bb60286..8a375b9734 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -1035,9 +1035,11 @@ pub(crate) fn trait_environment_query(
         clauses.push(program_clause.into_from_env_clause(&Interner));
     }
 
+    let krate = def.module(db.upcast()).krate();
+
     let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses);
 
-    Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env })
+    Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
 }
 
 /// Resolve the where clause(s) of an item with generics.
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 48bbcfd9ff..08e385a42c 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -577,6 +577,7 @@ fn iterate_method_candidates_by_receiver(
         if iterate_inherent_methods(
             self_ty,
             db,
+            env.clone(),
             name,
             Some(receiver_ty),
             krate,
@@ -613,8 +614,16 @@ fn iterate_method_candidates_for_self_ty(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
 ) -> bool {
-    if iterate_inherent_methods(self_ty, db, name, None, krate, visible_from_module, &mut callback)
-    {
+    if iterate_inherent_methods(
+        self_ty,
+        db,
+        env.clone(),
+        name,
+        None,
+        krate,
+        visible_from_module,
+        &mut callback,
+    ) {
         return true;
     }
     iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
@@ -653,12 +662,12 @@ fn iterate_trait_method_candidates(
         for (_name, item) in data.items.iter() {
             // Don't pass a `visible_from_module` down to `is_valid_candidate`,
             // since only inherent methods should be included into visibility checking.
-            if !is_valid_candidate(db, name, receiver_ty, *item, self_ty, None) {
+            if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) {
                 continue;
             }
             if !known_implemented {
                 let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
-                if db.trait_solve(krate, goal).is_none() {
+                if db.trait_solve(krate, goal.cast(&Interner)).is_none() {
                     continue 'traits;
                 }
             }
@@ -675,6 +684,7 @@ fn iterate_trait_method_candidates(
 fn iterate_inherent_methods(
     self_ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     name: Option<&Name>,
     receiver_ty: Option<&Canonical<Ty>>,
     krate: CrateId,
@@ -690,14 +700,24 @@ fn iterate_inherent_methods(
 
         for &impl_def in impls.for_self_ty(&self_ty.value) {
             for &item in db.impl_data(impl_def).items.iter() {
-                if !is_valid_candidate(db, name, receiver_ty, item, self_ty, visible_from_module) {
+                if !is_valid_candidate(
+                    db,
+                    env.clone(),
+                    name,
+                    receiver_ty,
+                    item,
+                    self_ty,
+                    visible_from_module,
+                ) {
                     continue;
                 }
                 // we have to check whether the self type unifies with the type
                 // that the impl is for. If we have a receiver type, this
                 // already happens in `is_valid_candidate` above; if not, we
                 // check it here
-                if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() {
+                if receiver_ty.is_none()
+                    && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none()
+                {
                     cov_mark::hit!(impl_self_type_match_without_receiver);
                     continue;
                 }
@@ -722,7 +742,7 @@ pub fn resolve_indexing_op(
     let deref_chain = autoderef_method_receiver(db, krate, ty);
     for ty in deref_chain {
         let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone());
-        if db.trait_solve(krate, goal).is_some() {
+        if db.trait_solve(krate, goal.cast(&Interner)).is_some() {
             return Some(ty);
         }
     }
@@ -731,6 +751,7 @@ pub fn resolve_indexing_op(
 
 fn is_valid_candidate(
     db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     name: Option<&Name>,
     receiver_ty: Option<&Canonical<Ty>>,
     item: AssocItemId,
@@ -749,7 +770,7 @@ fn is_valid_candidate(
                 if !data.has_self_param() {
                     return false;
                 }
-                let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
+                let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) {
                     Some(ty) => ty,
                     None => return false,
                 };
@@ -776,6 +797,7 @@ fn is_valid_candidate(
 
 pub(crate) fn inherent_impl_substs(
     db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     impl_id: ImplId,
     self_ty: &Canonical<Ty>,
 ) -> Option<Substitution> {
@@ -798,7 +820,7 @@ pub(crate) fn inherent_impl_substs(
         binders: CanonicalVarKinds::from_iter(&Interner, kinds),
         value: (self_ty_with_vars, self_ty.value.clone()),
     };
-    let substs = super::infer::unify(&tys)?;
+    let substs = super::infer::unify(db, env, &tys)?;
     // We only want the substs for the vars we added, not the ones from self_ty.
     // Also, if any of the vars we added are still in there, we replace them by
     // Unknown. I think this can only really happen if self_ty contained
@@ -823,6 +845,7 @@ fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution
 
 fn transform_receiver_ty(
     db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
     function_id: FunctionId,
     self_ty: &Canonical<Ty>,
 ) -> Option<Ty> {
@@ -832,7 +855,7 @@ fn transform_receiver_ty(
             .fill_with_unknown()
             .build(),
         AssocContainerId::ImplId(impl_id) => {
-            let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?;
+            let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?;
             TyBuilder::subst_for_def(db, function_id)
                 .use_parent_substs(&impl_substs)
                 .fill_with_unknown()
@@ -852,7 +875,7 @@ pub fn implements_trait(
     trait_: TraitId,
 ) -> bool {
     let goal = generic_implements_goal(db, env, trait_, ty.clone());
-    let solution = db.trait_solve(krate, goal);
+    let solution = db.trait_solve(krate, goal.cast(&Interner));
 
     solution.is_some()
 }
@@ -865,7 +888,7 @@ pub fn implements_trait_unique(
     trait_: TraitId,
 ) -> bool {
     let goal = generic_implements_goal(db, env, trait_, ty.clone());
-    let solution = db.trait_solve(krate, goal);
+    let solution = db.trait_solve(krate, goal.cast(&Interner));
 
     matches!(solution, Some(crate::Solution::Unique(_)))
 }
diff --git a/crates/hir_ty/src/tls.rs b/crates/hir_ty/src/tls.rs
index 87c671a422..708797c477 100644
--- a/crates/hir_ty/src/tls.rs
+++ b/crates/hir_ty/src/tls.rs
@@ -1,7 +1,7 @@
 //! Implementation of Chalk debug helper functions using TLS.
-use std::fmt;
+use std::fmt::{self, Debug};
 
-use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication};
+use chalk_ir::AliasTy;
 use itertools::Itertools;
 
 use crate::{
@@ -53,14 +53,6 @@ impl DebugContext<'_> {
         write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
     }
 
-    pub(crate) fn debug_opaque_ty_id(
-        &self,
-        opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()
-    }
-
     pub(crate) fn debug_alias(
         &self,
         alias_ty: &AliasTy<Interner>,
@@ -68,7 +60,7 @@ impl DebugContext<'_> {
     ) -> Result<(), fmt::Error> {
         match alias_ty {
             AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt),
-            AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt),
+            AliasTy::Opaque(opaque_ty) => opaque_ty.fmt(fmt),
         }
     }
 
@@ -96,79 +88,6 @@ impl DebugContext<'_> {
         write!(fmt, ">::{}", type_alias_data.name)
     }
 
-    pub(crate) fn debug_opaque_ty(
-        &self,
-        opaque_ty: &chalk_ir::OpaqueTy<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", opaque_ty.opaque_ty_id)
-    }
-
-    pub(crate) fn debug_ty(
-        &self,
-        ty: &chalk_ir::Ty<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", ty.data(&Interner))
-    }
-
-    pub(crate) fn debug_lifetime(
-        &self,
-        lifetime: &Lifetime<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", lifetime.data(&Interner))
-    }
-
-    pub(crate) fn debug_generic_arg(
-        &self,
-        parameter: &GenericArg<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", parameter.data(&Interner).inner_debug())
-    }
-
-    pub(crate) fn debug_goal(
-        &self,
-        goal: &Goal<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        let goal_data = goal.data(&Interner);
-        write!(fmt, "{:?}", goal_data)
-    }
-
-    pub(crate) fn debug_goals(
-        &self,
-        goals: &Goals<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", goals.debug(&Interner))
-    }
-
-    pub(crate) fn debug_program_clause_implication(
-        &self,
-        pci: &ProgramClauseImplication<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", pci.debug(&Interner))
-    }
-
-    pub(crate) fn debug_substitution(
-        &self,
-        substitution: &chalk_ir::Substitution<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", substitution.debug(&Interner))
-    }
-
-    pub(crate) fn debug_separator_trait_ref(
-        &self,
-        separator_trait_ref: &chalk_ir::SeparatorTraitRef<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> Result<(), fmt::Error> {
-        write!(fmt, "{:?}", separator_trait_ref.debug(&Interner))
-    }
-
     pub(crate) fn debug_fn_def_id(
         &self,
         fn_def_id: chalk_ir::FnDefId<Interner>,
@@ -190,57 +109,6 @@ impl DebugContext<'_> {
             }
         }
     }
-
-    pub(crate) fn debug_const(
-        &self,
-        _constant: &chalk_ir::Const<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "const")
-    }
-
-    pub(crate) fn debug_variable_kinds(
-        &self,
-        variable_kinds: &chalk_ir::VariableKinds<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", variable_kinds.as_slice(&Interner))
-    }
-    pub(crate) fn debug_variable_kinds_with_angles(
-        &self,
-        variable_kinds: &chalk_ir::VariableKinds<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", variable_kinds.inner_debug(&Interner))
-    }
-    pub(crate) fn debug_canonical_var_kinds(
-        &self,
-        canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", canonical_var_kinds.as_slice(&Interner))
-    }
-    pub(crate) fn debug_program_clause(
-        &self,
-        clause: &chalk_ir::ProgramClause<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", clause.data(&Interner))
-    }
-    pub(crate) fn debug_program_clauses(
-        &self,
-        clauses: &chalk_ir::ProgramClauses<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", clauses.as_slice(&Interner))
-    }
-    pub(crate) fn debug_quantified_where_clauses(
-        &self,
-        clauses: &chalk_ir::QuantifiedWhereClauses<Interner>,
-        fmt: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        write!(fmt, "{:?}", clauses.as_slice(&Interner))
-    }
 }
 
 mod unsafe_tls {
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index 9936d08033..294cb531c8 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -2,7 +2,7 @@
 
 use std::env::var;
 
-use chalk_ir::cast::Cast;
+use chalk_ir::GoalData;
 use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
 
 use base_db::CrateId;
@@ -10,7 +10,7 @@ use hir_def::{lang_item::LangItemTarget, TraitId};
 use stdx::panic_context;
 
 use crate::{
-    db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment,
+    db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
     Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
 };
 
@@ -38,6 +38,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
 /// we assume that `T: Default`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct TraitEnvironment {
+    pub krate: CrateId,
     // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord,
     // but for now it's too annoying...
     pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
@@ -45,6 +46,14 @@ pub struct TraitEnvironment {
 }
 
 impl TraitEnvironment {
+    pub fn empty(krate: CrateId) -> Self {
+        TraitEnvironment {
+            krate,
+            traits_from_clauses: Vec::new(),
+            env: chalk_ir::Environment::new(&Interner),
+        }
+    }
+
     pub(crate) fn traits_in_scope_from_clauses<'a>(
         &'a self,
         ty: &'a Ty,
@@ -59,34 +68,25 @@ impl TraitEnvironment {
     }
 }
 
-impl Default for TraitEnvironment {
-    fn default() -> Self {
-        TraitEnvironment {
-            traits_from_clauses: Vec::new(),
-            env: chalk_ir::Environment::new(&Interner),
-        }
-    }
-}
-
 /// Solve a trait goal using Chalk.
 pub(crate) fn trait_solve_query(
     db: &dyn HirDatabase,
     krate: CrateId,
-    goal: Canonical<InEnvironment<DomainGoal>>,
+    goal: Canonical<InEnvironment<Goal>>,
 ) -> Option<Solution> {
-    let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal {
-        DomainGoal::Holds(WhereClause::Implemented(it)) => {
+    let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(&Interner) {
+        GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
             db.trait_data(it.hir_trait_id()).name.to_string()
         }
-        DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(),
+        GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
         _ => "??".to_string(),
     });
-    log::info!("trait_solve_query({})", goal.value.goal.display(db));
+    log::info!("trait_solve_query({:?})", goal.value.goal);
 
-    if let DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
+    if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
         alias: AliasTy::Projection(projection_ty),
         ..
-    })) = &goal.value.goal
+    }))) = &goal.value.goal.data(&Interner)
     {
         if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) {
             // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
@@ -94,11 +94,9 @@ pub(crate) fn trait_solve_query(
         }
     }
 
-    let canonical = goal.cast(&Interner);
-
     // We currently don't deal with universes (I think / hope they're not yet
     // relevant for our use cases?)
-    let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
+    let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 };
     solve(db, krate, &u_canonical)
 }
 
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 1a762d3dc1..6b04ee1648 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -323,7 +323,7 @@ fn compute_type_match(
 
     if completion_ty == expected_type {
         Some(CompletionRelevanceTypeMatch::Exact)
-    } else if expected_type.could_unify_with(completion_ty) {
+    } else if expected_type.could_unify_with(ctx.db, completion_ty) {
         Some(CompletionRelevanceTypeMatch::CouldUnify)
     } else {
         None


Test patch:
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 133b2d8184..04de107f5b 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -735,42 +735,6 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() {
     );
 }
 
-#[test]
-fn unresolved_attrs_extern_block_hang() {
-    check(
-        r#"
-#[unresolved]
-extern "C" {
-    #[unresolved]
-    fn f();
-}
-    "#,
-        expect![[r#"
-        crate
-        f: v
-    "#]],
-    );
-}
-
-#[test]
-fn macros_in_extern_block() {
-    check(
-        r#"
-macro_rules! m {
-    () => { static S: u8; };
-}
-
-extern {
-    m!();
-}
-    "#,
-        expect![[r#"
-            crate
-            S: v
-        "#]],
-    );
-}
-
 #[test]
 fn resolves_derive_helper() {
     cov_mark::check!(resolved_derive_helper);
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 1904710691..bb568ea372 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
 use expect_test::expect;
 
-use super::{check_infer, check_infer_with_mismatches};
+use super::{check_infer, check_infer_with_mismatches, check_types};
 
 #[test]
 fn infer_block_expr_type_mismatch() {
@@ -858,3 +858,57 @@ fn coerce_unsize_generic() {
         "]],
     );
 }
+
+#[test]
+fn infer_two_closures_lub() {
+    check_types(
+        r#"
+fn foo(c: i32) {
+    let add = |a: i32, b: i32| a + b;
+    let sub = |a, b| a - b;
+            //^ |i32, i32| -> i32
+    if c > 42 { add } else { sub };
+  //^ fn(i32, i32) -> i32
+}
+        "#,
+    )
+}
+
+#[test]
+fn infer_match_diverging_branch_1() {
+    check_types(
+        r#"
+enum Result<T> { Ok(T), Err }
+fn parse<T>() -> T { loop {} }
+
+fn test() -> i32 {
+    let a = match parse() {
+        Ok(val) => val,
+        Err => return 0,
+    };
+    a
+  //^ i32
+}
+        "#,
+    )
+}
+
+#[test]
+fn infer_match_diverging_branch_2() {
+    // same as 1 except for order of branches
+    check_types(
+        r#"
+enum Result<T> { Ok(T), Err }
+fn parse<T>() -> T { loop {} }
+
+fn test() -> i32 {
+    let a = match parse() {
+        Err => return 0,
+        Ok(val) => val,
+    };
+    a
+  //^ i32
+}
+        "#,
+    )
+}
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index ddbadbe400..cd08b5c7a5 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -747,7 +747,7 @@ fn foo(tuple: (u8, i16, f32)) {
             209..210 '_': (u8, i16, f32)
             214..216 '{}': ()
             136..142: expected (u8, i16, f32), got (u8, i16)
-            170..182: expected (u8, i16, f32), got (u8, i16, f32, _)
+            170..182: expected (u8, i16, f32), got (u8, i16, f32, {unknown})
         "#]],
     );
 }
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 4318617124..ad9edf11cd 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -86,8 +86,6 @@ fn bug_651() {
 
 #[test]
 fn recursive_vars() {
-    cov_mark::check!(type_var_cycles_resolve_completely);
-    cov_mark::check!(type_var_cycles_resolve_as_possible);
     check_infer(
         r#"
         fn test() {
@@ -97,12 +95,12 @@ fn recursive_vars() {
         "#,
         expect![[r#"
             10..47 '{     ...&y]; }': ()
-            20..21 'y': &{unknown}
-            24..31 'unknown': &{unknown}
-            37..44 '[y, &y]': [&&{unknown}; 2]
-            38..39 'y': &{unknown}
-            41..43 '&y': &&{unknown}
-            42..43 'y': &{unknown}
+            20..21 'y': {unknown}
+            24..31 'unknown': {unknown}
+            37..44 '[y, &y]': [{unknown}; 2]
+            38..39 'y': {unknown}
+            41..43 '&y': &{unknown}
+            42..43 'y': {unknown}
         "#]],
     );
 }
@@ -119,19 +117,19 @@ fn recursive_vars_2() {
         "#,
         expect![[r#"
             10..79 '{     ...x)]; }': ()
-            20..21 'x': &&{unknown}
-            24..31 'unknown': &&{unknown}
-            41..42 'y': &&{unknown}
-            45..52 'unknown': &&{unknown}
-            58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2]
-            59..65 '(x, y)': (&&&{unknown}, &&&{unknown})
-            60..61 'x': &&{unknown}
-            63..64 'y': &&{unknown}
-            67..75 '(&y, &x)': (&&&{unknown}, &&&{unknown})
-            68..70 '&y': &&&{unknown}
-            69..70 'y': &&{unknown}
-            72..74 '&x': &&&{unknown}
-            73..74 'x': &&{unknown}
+            20..21 'x': &{unknown}
+            24..31 'unknown': &{unknown}
+            41..42 'y': {unknown}
+            45..52 'unknown': {unknown}
+            58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2]
+            59..65 '(x, y)': (&{unknown}, {unknown})
+            60..61 'x': &{unknown}
+            63..64 'y': {unknown}
+            67..75 '(&y, &x)': (&{unknown}, {unknown})
+            68..70 '&y': &{unknown}
+            69..70 'y': {unknown}
+            72..74 '&x': &&{unknown}
+            73..74 'x': &{unknown}
         "#]],
     );
 }
@@ -165,7 +163,6 @@ fn infer_std_crash_1() {
 
 #[test]
 fn infer_std_crash_2() {
-    cov_mark::check!(type_var_resolves_to_int_var);
     // caused "equating two type variables, ...", taken from std
     check_infer(
         r#"
@@ -257,27 +254,27 @@ fn infer_std_crash_5() {
         expect![[r#"
             26..322 '{     ...   } }': ()
             32..320 'for co...     }': ()
-            36..43 'content': &{unknown}
+            36..43 'content': {unknown}
             47..60 'doesnt_matter': {unknown}
             61..320 '{     ...     }': ()
-            75..79 'name': &&{unknown}
-            82..166 'if doe...     }': &&{unknown}
+            75..79 'name': &{unknown}
+            82..166 'if doe...     }': &{unknown}
             85..98 'doesnt_matter': bool
-            99..128 '{     ...     }': &&{unknown}
-            113..118 'first': &&{unknown}
-            134..166 '{     ...     }': &&{unknown}
-            148..156 '&content': &&{unknown}
-            149..156 'content': &{unknown}
+            99..128 '{     ...     }': &{unknown}
+            113..118 'first': &{unknown}
+            134..166 '{     ...     }': &{unknown}
+            148..156 '&content': &{unknown}
+            149..156 'content': {unknown}
             181..188 'content': &{unknown}
             191..313 'if ICE...     }': &{unknown}
             194..231 'ICE_RE..._VALUE': {unknown}
             194..247 'ICE_RE...&name)': bool
-            241..246 '&name': &&&{unknown}
-            242..246 'name': &&{unknown}
-            248..276 '{     ...     }': &&{unknown}
-            262..266 'name': &&{unknown}
-            282..313 '{     ...     }': &{unknown}
-            296..303 'content': &{unknown}
+            241..246 '&name': &&{unknown}
+            242..246 'name': &{unknown}
+            248..276 '{     ...     }': &{unknown}
+            262..266 'name': &{unknown}
+            282..313 '{     ...     }': {unknown}
+            296..303 'content': {unknown}
         "#]],
     );
 }
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a9cd42186e..5c70a1fc01 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1039,42 +1039,6 @@ fn infer_in_elseif() {
     )
 }
 
-#[test]
-fn infer_closure_unify() {
-    check_infer(
-        r#"
-        fn foo(f: bool) {
-            let a = |x| x;
-            let b = |x| x;
-            let id = if f { a } else { b };
-            id(123);
-        }
-        "#,
-        expect![[r#"
-            7..8 'f': bool
-            16..106 '{     ...23); }': ()
-            26..27 'a': |i32| -> i32
-            30..35 '|x| x': |i32| -> i32
-            31..32 'x': i32
-            34..35 'x': i32
-            45..46 'b': |i32| -> i32
-            49..54 '|x| x': |i32| -> i32
-            50..51 'x': i32
-            53..54 'x': i32
-            64..66 'id': |i32| -> i32
-            69..90 'if f {... { b }': |i32| -> i32
-            72..73 'f': bool
-            74..79 '{ a }': |i32| -> i32
-            76..77 'a': |i32| -> i32
-            85..90 '{ b }': |i32| -> i32
-            87..88 'b': |i32| -> i32
-            96..98 'id': |i32| -> i32
-            96..103 'id(123)': i32
-            99..102 '123': i32
-        "#]],
-    )
-}
-
 #[test]
 fn infer_if_match_with_return() {
     check_infer(
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index f80cf98798..a5a2df54cb 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3104,7 +3104,7 @@ fn foo() {
             568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
             570..572 '&s': &Option<i32>
             571..572 's': Option<i32>
-            549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|_| -> ()>
+            549..562: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|{unknown}| -> ()>
         "#]],
     );
 }