Repository: rust-analyzer/rust-analyzer
Pull request #8415 (2021-04-08 05:04:23)

Fix faulty assertion when extracting function with macro call
**Reproduction:**
```rust
fn main() {
    let n = 1;
    let k = n * n;
    dbg!(n);
}
```
1. Select the second and third lines of the main function. Use the "Extract into function" code assist.
2. Panic occurs in debug, error is logged in release: "[ERROR ide_assists::handlers::extract_function] assertion failed: matches!(path, ast :: Expr :: PathExpr(_))".
3. Function generates successfully on release where the panic was bypassed.
```rust
fn fun_name(n: i32) {
    let k = n * n;
    dbg!(n);
}
```

**Cause:**
- The generated function will take `n` as a parameter. The extraction logic needs to search the usages of `n` to determine whether it is used mutably or not. The helper `path_element_of_reference` is called for each usage but the second usage is a macro call and fails the `Expr::PathExpr(_)` match assertion.
- The caller of `path_element_of_reference` does implicitly assume it to be a `Expr::PathExpr(_)` in how it looks at its parent node for determining whether it is used mutably. This logic will not work for macros.
- I'm not sure if there are any other cases besides macros where it could be something other than a `Expr::PathExpr(_)`. I tried various examples and could not find any.

**Fix:**
- Update assertion to include the macro case.
- Add a FIXME to properly handle checking if a macro usage requires mutable access. For now, return false instead of running the existing logic that is tailored for `Expr::PathExpr(_)`'s.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index bcb1cbd9f2..e40c12a92e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -797,9 +797,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.93"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
+checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
 
 [[package]]
 name = "libloading"
@@ -912,9 +912,9 @@ dependencies = [
 
 [[package]]
 name = "memmap2"
-version = "0.2.2"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd"
+checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6"
 dependencies = [
  "libc",
 ]
@@ -1391,9 +1391,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "714.0.0"
+version = "710.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a35856f140bed0dc7c7d6ba2134099d337377a3a4e11bfc79bccabf1fd4c9d42"
+checksum = "b0bba1ca6787b6d4af505b7a940eae9ecb084dd03e07f03bf3ddbf78e738b617"
 dependencies = [
  "unicode-xid",
 ]
@@ -1675,9 +1675,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.2.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
+checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -1815,9 +1815,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.5"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
+checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
 dependencies = [
  "matches",
 ]
@@ -1867,7 +1867,6 @@ name = "vfs"
 version = "0.0.0"
 dependencies = [
  "fst",
- "indexmap",
  "paths",
  "rustc-hash",
 ]
diff --git a/PRIVACY.md b/PRIVACY.md
index 27e39ca607..dd165c0e2c 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -14,4 +14,4 @@ Any other editor plugins that integrate with `rust-analyzer` are not under the c
 
 ## Others
 
-If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (on by default).
+If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (off by default).
diff --git a/README.md b/README.md
index 5fbc039644..085c8c0f63 100644
--- a/README.md
+++ b/README.md
@@ -33,9 +33,9 @@ For usage and troubleshooting requests, please use "IDEs and Editors" category o
 
 https://users.rust-lang.org/c/ide/14
 
-For questions about development and implementation, join rust-analyzer working group on Zulip:
+For questions about development and implementation, join rls-2.0 working group on Zulip:
 
-https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
+https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
 
 ## Quick Links
 
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 04e2be3906..8d46413557 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -1,4 +1,62 @@
-//! A set of high-level utility fixture methods to use in tests.
+//! Fixtures are strings containing rust source code with optional metadata.
+//! A fixture without metadata is parsed into a single source file.
+//! Use this to test functionality local to one file.
+//!
+//! Simple Example:
+//! ```
+//! r#"
+//! fn main() {
+//!     println!("Hello World")
+//! }
+//! "#
+//! ```
+//!
+//! Metadata can be added to a fixture after a `//-` comment.
+//! The basic form is specifying filenames,
+//! which is also how to define multiple files in a single test fixture
+//!
+//! Example using two files in the same crate:
+//! ```
+//! "
+//! //- /main.rs
+//! mod foo;
+//! fn main() {
+//!     foo::bar();
+//! }
+//!
+//! //- /foo.rs
+//! pub fn bar() {}
+//! "
+//! ```
+//!
+//! Example using two crates with one file each, with one crate depending on the other:
+//! ```
+//! r#"
+//! //- /main.rs crate:a deps:b
+//! fn main() {
+//!     b::foo();
+//! }
+//! //- /lib.rs crate:b
+//! pub fn b() {
+//!     println!("Hello World")
+//! }
+//! "#
+//! ```
+//!
+//! Metadata allows specifying all settings and variables
+//! that are available in a real rust project:
+//! - crate names via `crate:cratename`
+//! - dependencies via `deps:dep1,dep2`
+//! - configuration settings via `cfg:dbg=false,opt_level=2`
+//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
+//!
+//! Example using all available metadata:
+//! ```
+//! "
+//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
+//! fn insert_source_code_here() {}
+//! "
+//! ```
 use std::{mem, str::FromStr, sync::Arc};
 
 use cfg::CfgOptions;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 0afc069061..0038219818 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -53,14 +53,13 @@ use hir_def::{
 use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
 use hir_ty::{
     autoderef, could_unify,
-    method_resolution::{self, def_crates, TyFingerprint},
+    method_resolution::{self, TyFingerprint},
     primitive::UintTy,
-    subst_prefix,
     traits::FnTrait,
     AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
-    DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution, Substitution,
-    TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind,
-    WhereClause,
+    DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution,
+    SolutionVariables, Substitution, TraitEnvironment, Ty, TyBuilder, TyDefId, TyKind,
+    TyVariableKind, WhereClause,
 };
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
@@ -1504,7 +1503,7 @@ impl TypeParam {
         let krate = self.id.parent.module(db.upcast()).krate();
         let ty = params.get(local_idx)?.clone();
         let subst = TyBuilder::type_params_subst(db, self.id.parent);
-        let ty = ty.substitute(&Interner, &subst_prefix(&subst, local_idx));
+        let ty = ty.substitute(&Interner, &subst.prefix(local_idx));
         Some(Type::new_with_resolver_inner(db, krate, &resolver, ty))
     }
 }
@@ -1569,7 +1568,7 @@ impl Impl {
     }
 
     pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
-        let def_crates = match def_crates(db, &ty, krate) {
+        let def_crates = match ty.def_crates(db, krate) {
             Some(def_crates) => def_crates,
             None => return Vec::new(),
         };
@@ -1791,7 +1790,7 @@ impl Type {
             .build();
 
         let goal = Canonical {
-            value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(&Interner)),
+            value: hir_ty::InEnvironment::new(self.env.env.clone(), trait_ref.cast(&Interner)),
             binders: CanonicalVarKinds::empty(&Interner),
         };
 
@@ -1808,9 +1807,9 @@ impl Type {
             .push(self.ty.clone())
             .fill(args.iter().map(|t| t.ty.clone()))
             .build();
-        let goal = hir_ty::make_canonical(
+        let goal = Canonical::new(
             InEnvironment::new(
-                &self.env.env,
+                self.env.env.clone(),
                 AliasEq {
                     alias: AliasTy::Projection(projection),
                     ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
@@ -1822,9 +1821,8 @@ impl Type {
         );
 
         match db.trait_solve(self.krate, goal)? {
-            Solution::Unique(s) => s
+            Solution::Unique(SolutionVariables(subst)) => subst
                 .value
-                .subst
                 .interned()
                 .first()
                 .map(|ty| self.derived(ty.assert_ty_ref(&Interner).clone())),
@@ -1889,10 +1887,9 @@ impl Type {
                     substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
                 }
 
-                TyKind::Array(ty, _)
-                | TyKind::Slice(ty)
-                | TyKind::Raw(_, ty)
-                | TyKind::Ref(_, _, ty) => go(ty),
+                TyKind::Array(ty) | TyKind::Slice(ty) | TyKind::Raw(_, ty) | TyKind::Ref(_, ty) => {
+                    go(ty)
+                }
 
                 TyKind::Scalar(_)
                 | TyKind::Str
@@ -1956,7 +1953,7 @@ impl Type {
         krate: Crate,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
-        for krate in def_crates(db, &self.ty, krate.id)? {
+        for krate in self.ty.def_crates(db, krate.id)? {
             let impls = db.inherent_impls_in_crate(krate);
 
             for impl_def in impls.for_self_ty(&self.ty) {
@@ -1973,9 +1970,9 @@ impl Type {
     pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
         self.ty
             .strip_references()
-            .as_adt()
+            .substs()
             .into_iter()
-            .flat_map(|(_, substs)| substs.iter(&Interner))
+            .flat_map(|substs| substs.iter(&Interner))
             .filter_map(|arg| arg.ty(&Interner).cloned())
             .map(move |ty| self.derived(ty))
     }
@@ -2116,22 +2113,18 @@ impl Type {
         fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
             let ty = type_.ty.strip_references();
             match ty.kind(&Interner) {
-                TyKind::Adt(_, substs) => {
+                TyKind::Adt(..) => {
                     cb(type_.derived(ty.clone()));
-                    walk_substs(db, type_, &substs, cb);
                 }
-                TyKind::AssociatedType(_, substs) => {
+                TyKind::AssociatedType(..) => {
                     if let Some(_) = ty.associated_type_parent_trait(db) {
                         cb(type_.derived(ty.clone()));
                     }
-                    walk_substs(db, type_, &substs, cb);
                 }
-                TyKind::OpaqueType(_, subst) => {
+                TyKind::OpaqueType(..) => {
                     if let Some(bounds) = ty.impl_trait_bounds(db) {
                         walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
                     }
-
-                    walk_substs(db, type_, subst, cb);
                 }
                 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
                     if let Some(bounds) = ty.impl_trait_bounds(db) {
@@ -2154,24 +2147,15 @@ impl Type {
                     );
                 }
 
-                TyKind::Ref(_, _, ty)
-                | TyKind::Raw(_, ty)
-                | TyKind::Array(ty, _)
-                | TyKind::Slice(ty) => {
+                TyKind::Ref(_, ty) | TyKind::Raw(_, ty) | TyKind::Array(ty) | TyKind::Slice(ty) => {
                     walk_type(db, &type_.derived(ty.clone()), cb);
                 }
 
-                TyKind::FnDef(_, substs)
-                | TyKind::Tuple(_, substs)
-                | TyKind::Closure(.., substs) => {
-                    walk_substs(db, type_, &substs, cb);
-                }
-                TyKind::Function(hir_ty::FnPointer { substitution, .. }) => {
-                    walk_substs(db, type_, &substitution.0, cb);
-                }
-
                 _ => {}
             }
+            if let Some(substs) = ty.substs() {
+                walk_substs(db, type_, &substs, cb);
+            }
         }
 
         walk_type(db, self, &mut cb);
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 847d2537d2..4ce1c20800 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -20,7 +20,7 @@ use hir_def::{
 use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
 use hir_ty::{
     diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
-    InferenceResult, Interner, Substitution, TyExt, TyLoweringContext,
+    InferenceResult, Interner, Substitution, TyLoweringContext,
 };
 use syntax::{
     ast::{self, AstNode},
@@ -161,15 +161,14 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         field: &ast::RecordExprField,
     ) -> Option<(Field, Option<Local>)> {
-        let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
-        let expr = ast::Expr::from(record_expr);
-        let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?;
+        let expr_id =
+            self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?;
 
-        let local_name = field.field_name()?.as_name();
         let local = if field.name_ref().is_some() {
             None
         } else {
-            let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
+            let local_name = field.field_name()?.as_name();
+            let path = ModPath::from_segments(PathKind::Plain, once(local_name));
             match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
                 Some(ValueNs::LocalBinding(pat_id)) => {
                     Some(Local { pat_id, parent: self.resolver.body_owner()? })
@@ -177,24 +176,18 @@ impl SourceAnalyzer {
                 _ => None,
             }
         };
-        let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
-        let variant_data = variant.variant_data(db.upcast());
-        let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
-        Some((field.into(), local))
+        let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
+        Some((struct_field.into(), local))
     }
 
     pub(crate) fn resolve_record_pat_field(
         &self,
-        db: &dyn HirDatabase,
+        _db: &dyn HirDatabase,
         field: &ast::RecordPatField,
     ) -> Option<Field> {
-        let field_name = field.field_name()?.as_name();
-        let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
-        let pat_id = self.pat_id(&record_pat.into())?;
-        let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
-        let variant_data = variant.variant_data(db.upcast());
-        let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
-        Some(field.into())
+        let pat_id = self.pat_id(&field.pat()?)?;
+        let struct_field = self.infer.as_ref()?.record_pat_field_resolution(pat_id)?;
+        Some(struct_field.into())
     }
 
     pub(crate) fn resolve_macro_call(
@@ -306,7 +299,7 @@ impl SourceAnalyzer {
         let infer = self.infer.as_ref()?;
 
         let expr_id = self.expr_id(db, &literal.clone().into())?;
-        let substs = infer.type_of_expr[expr_id].as_adt()?.1;
+        let substs = infer.type_of_expr[expr_id].substs()?;
 
         let (variant, missing_fields, _exhaustive) =
             record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
@@ -324,7 +317,7 @@ impl SourceAnalyzer {
         let infer = self.infer.as_ref()?;
 
         let pat_id = self.pat_id(&pattern.clone().into())?;
-        let substs = infer.type_of_pat[pat_id].as_adt()?.1;
+        let substs = infer.type_of_pat[pat_id].substs()?;
 
         let (variant, missing_fields, _exhaustive) =
             record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index d9df7564d2..ab77d924a4 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -16,7 +16,7 @@ use mbe::ast_to_token_tree;
 use smallvec::{smallvec, SmallVec};
 use syntax::{
     ast::{self, AstNode, AttrsOwner},
-    match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
+    match_ast, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
 };
 use tt::Subtree;
 
@@ -215,11 +215,12 @@ impl Attrs {
         let mut res = ArenaMap::default();
 
         for (id, fld) in src.value.iter() {
-            let owner: &dyn AttrsOwner = match fld {
-                Either::Left(tuple) => tuple,
-                Either::Right(record) => record,
+            let attrs = match fld {
+                Either::Left(_tuple) => Attrs::default(),
+                Either::Right(record) => {
+                    RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
+                }
             };
-            let attrs = RawAttrs::from_attrs_owner(db, src.with_value(owner)).filter(db, krate);
 
             res.insert(id, attrs);
         }
@@ -403,14 +404,10 @@ impl AttrsWithOwner {
                 return AttrSourceMap { attrs };
             }
             AttrDefId::FieldId(id) => {
-                let map = db.fields_attrs_source_map(id.parent);
-                let file_id = id.parent.file_id(db);
-                let root = db.parse_or_expand(file_id).unwrap();
-                let owner = match &map[id.local_id] {
-                    Either::Left(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
-                    Either::Right(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
-                };
-                InFile::new(file_id, owner)
+                id.parent.child_source(db).map(|source| match &source[id.local_id] {
+                    Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()),
+                    Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()),
+                })
             }
             AttrDefId::AdtId(adt) => match adt {
                 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -418,12 +415,10 @@ impl AttrsWithOwner {
                 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
             },
             AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
-            AttrDefId::EnumVariantId(id) => {
-                let map = db.variants_attrs_source_map(id.parent);
-                let file_id = id.parent.lookup(db).id.file_id();
-                let root = db.parse_or_expand(file_id).unwrap();
-                InFile::new(file_id, ast::AttrsOwnerNode::new(map[id.local_id].to_node(&root)))
-            }
+            AttrDefId::EnumVariantId(id) => id
+                .parent
+                .child_source(db)
+                .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())),
             AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
             AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
             AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -752,36 +747,3 @@ fn collect_attrs(
 
     attrs.into_iter().map(|(_, attr)| attr)
 }
-
-pub(crate) fn variants_attrs_source_map(
-    db: &dyn DefDatabase,
-    def: EnumId,
-) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>> {
-    let mut res = ArenaMap::default();
-    let child_source = def.child_source(db);
-
-    for (idx, variant) in child_source.value.iter() {
-        res.insert(idx, AstPtr::new(variant));
-    }
-
-    Arc::new(res)
-}
-
-pub(crate) fn fields_attrs_source_map(
-    db: &dyn DefDatabase,
-    def: VariantId,
-) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>> {
-    let mut res = ArenaMap::default();
-    let child_source = def.child_source(db);
-
-    for (idx, variant) in child_source.value.iter() {
-        res.insert(
-            idx,
-            variant
-                .as_ref()
-                .either(|l| Either::Left(AstPtr::new(l)), |r| Either::Right(AstPtr::new(r))),
-        );
-    }
-
-    Arc::new(res)
-}
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index bfb75a8a59..63e89a1f42 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -30,7 +30,6 @@ use crate::{
         LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
         Statement,
     },
-    intern::Interned,
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
@@ -323,10 +322,8 @@ impl ExprCollector<'_> {
                     Vec::new()
                 };
                 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
-                let generic_args = e
-                    .generic_arg_list()
-                    .and_then(|it| GenericArgs::from_ast(&self.ctx(), it))
-                    .map(Box::new);
+                let generic_args =
+                    e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it));
                 self.alloc_expr(
                     Expr::MethodCall { receiver, method_name, args, generic_args },
                     syntax_ptr,
@@ -388,7 +385,7 @@ impl ExprCollector<'_> {
                 self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
             }
             ast::Expr::RecordExpr(e) => {
-                let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+                let path = e.path().and_then(|path| self.expander.parse_path(path));
                 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
                     let fields = nfl
                         .fields()
@@ -433,7 +430,7 @@ impl ExprCollector<'_> {
             }
             ast::Expr::CastExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
-                let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
+                let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty());
                 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
             }
             ast::Expr::RefExpr(e) => {
@@ -467,16 +464,13 @@ impl ExprCollector<'_> {
                 if let Some(pl) = e.param_list() {
                     for param in pl.params() {
                         let pat = self.collect_pat_opt(param.pat());
-                        let type_ref =
-                            param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                        let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
                         args.push(pat);
                         arg_types.push(type_ref);
                     }
                 }
-                let ret_type = e
-                    .ret_type()
-                    .and_then(|r| r.ty())
-                    .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                let ret_type =
+                    e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it));
                 let body = self.collect_expr_opt(e.body());
                 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
             }
@@ -631,8 +625,7 @@ impl ExprCollector<'_> {
                     return;
                 }
                 let pat = self.collect_pat_opt(stmt.pat());
-                let type_ref =
-                    stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
                 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
                 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
             }
@@ -762,7 +755,7 @@ impl ExprCollector<'_> {
                 }
             }
             ast::Pat::TupleStructPat(p) => {
-                let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+                let path = p.path().and_then(|path| self.expander.parse_path(path));
                 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
                 Pat::TupleStruct { path, args, ellipsis }
             }
@@ -772,7 +765,7 @@ impl ExprCollector<'_> {
                 Pat::Ref { pat, mutability }
             }
             ast::Pat::PathPat(p) => {
-                let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+                let path = p.path().and_then(|path| self.expander.parse_path(path));
                 path.map(Pat::Path).unwrap_or(Pat::Missing)
             }
             ast::Pat::OrPat(p) => {
@@ -786,7 +779,7 @@ impl ExprCollector<'_> {
             }
             ast::Pat::WildcardPat(_) => Pat::Wild,
             ast::Pat::RecordPat(p) => {
-                let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+                let path = p.path().and_then(|path| self.expander.parse_path(path));
                 let args: Vec<_> = p
                     .record_pat_field_list()
                     .expect("every struct should have a field list")
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 7eadc8e0d6..9b7a213a12 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -2,10 +2,9 @@
 use std::sync::Arc;
 
 use base_db::{salsa, CrateId, SourceDatabase, Upcast};
-use either::Either;
 use hir_expand::{db::AstDatabase, HirFileId};
 use la_arena::ArenaMap;
-use syntax::{ast, AstPtr, SmolStr};
+use syntax::SmolStr;
 
 use crate::{
     adt::{EnumData, StructData},
@@ -123,18 +122,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(Attrs::fields_attrs_query)]
     fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
 
-    #[salsa::invoke(crate::attr::variants_attrs_source_map)]
-    fn variants_attrs_source_map(
-        &self,
-        def: EnumId,
-    ) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>>;
-
-    #[salsa::invoke(crate::attr::fields_attrs_source_map)]
-    fn fields_attrs_source_map(
-        &self,
-        def: VariantId,
-    ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
-
     #[salsa::invoke(AttrsWithOwner::attrs_query)]
     fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
 
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index b4ad984bd9..6c7376fad3 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -18,7 +18,6 @@ use syntax::ast::RangeOp;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
-    intern::Interned,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
     BlockId,
@@ -87,7 +86,7 @@ pub enum Expr {
         receiver: ExprId,
         method_name: Name,
         args: Vec<ExprId>,
-        generic_args: Option<Box<GenericArgs>>,
+        generic_args: Option<GenericArgs>,
     },
     Match {
         expr: ExprId,
@@ -107,7 +106,7 @@ pub enum Expr {
         expr: Option<ExprId>,
     },
     RecordLit {
-        path: Option<Box<Path>>,
+        path: Option<Path>,
         fields: Vec<RecordLitField>,
         spread: Option<ExprId>,
     },
@@ -132,7 +131,7 @@ pub enum Expr {
     },
     Cast {
         expr: ExprId,
-        type_ref: Interned<TypeRef>,
+        type_ref: TypeRef,
     },
     Ref {
         expr: ExprId,
@@ -162,8 +161,8 @@ pub enum Expr {
     },
     Lambda {
         args: Vec<PatId>,
-        arg_types: Vec<Option<Interned<TypeRef>>>,
-        ret_type: Option<Interned<TypeRef>>,
+        arg_types: Vec<Option<TypeRef>>,
+        ret_type: Option<TypeRef>,
         body: ExprId,
     },
     Tuple {
@@ -241,7 +240,7 @@ pub struct RecordLitField {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Statement {
-    Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
+    Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> },
     Expr(ExprId),
 }
 
@@ -413,13 +412,13 @@ pub enum Pat {
     Wild,
     Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
     Or(Vec<PatId>),
-    Record { path: Option<Box<Path>>, args: Vec<RecordFieldPat>, ellipsis: bool },
+    Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool },
     Range { start: ExprId, end: ExprId },
     Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> },
-    Path(Box<Path>),
+    Path(Path),
     Lit(ExprId),
     Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
-    TupleStruct { path: Option<Box<Path>>, args: Vec<PatId>, ellipsis: Option<usize> },
+    TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> },
     Ref { pat: PatId, mutability: Mutability },
     Box { inner: PatId },
     ConstBlock(ExprId),
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 9014468ea0..a8ee5eeac9 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::format_to;
 
 use crate::{
-    db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
+    db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId,
     LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
 };
 
@@ -37,7 +37,6 @@ pub struct ItemScope {
 
     defs: Vec<ModuleDefId>,
     impls: Vec<ImplId>,
-    unnamed_consts: Vec<ConstId>,
     /// Traits imported via `use Trait as _;`.
     unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
     /// Macros visible in current module in legacy textual scope
@@ -107,10 +106,6 @@ impl ItemScope {
             .map(|(_, v)| v)
     }
 
-    pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
-        self.unnamed_consts.iter().copied()
-    }
-
     /// Iterate over all module scoped macros
     pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
         self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
@@ -161,10 +156,6 @@ impl ItemScope {
         self.impls.push(imp)
     }
 
-    pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) {
-        self.unnamed_consts.push(konst);
-    }
-
     pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) {
         self.legacy_macros.insert(name, mac);
     }
@@ -304,7 +295,6 @@ impl ItemScope {
             unresolved,
             defs,
             impls,
-            unnamed_consts,
             unnamed_trait_imports,
             legacy_macros,
         } = self;
@@ -314,7 +304,6 @@ impl ItemScope {
         unresolved.shrink_to_fit();
         defs.shrink_to_fit();
         impls.shrink_to_fit();
-        unnamed_consts.shrink_to_fit();
         unnamed_trait_imports.shrink_to_fit();
         legacy_macros.shrink_to_fit();
     }
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index e2af0e5148..be9a5e1a0e 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -56,7 +56,6 @@ use std::{
     sync::Arc,
 };
 
-use adt::VariantData;
 use base_db::{impl_intern_key, salsa, CrateId};
 use hir_expand::{
     ast_id_map::FileAstId,
@@ -443,26 +442,6 @@ pub enum VariantId {
 }
 impl_from!(EnumVariantId, StructId, UnionId for VariantId);
 
-impl VariantId {
-    pub fn variant_data(self, db: &dyn db::DefDatabase) -> Arc<VariantData> {
-        match self {
-            VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
-            VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
-            VariantId::EnumVariantId(it) => {
-                db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
-            }
-        }
-    }
-
-    pub fn file_id(self, db: &dyn db::DefDatabase) -> HirFileId {
-        match self {
-            VariantId::EnumVariantId(it) => it.parent.lookup(db).id.file_id(),
-            VariantId::StructId(it) => it.lookup(db).id.file_id(),
-            VariantId::UnionId(it) => it.lookup(db).id.file_id(),
-        }
-    }
-}
-
 trait Intern {
     type ID;
     fn intern(self, db: &dyn db::DefDatabase) -> Self::ID;
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 492d8c71ff..f42f927023 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1163,27 +1163,19 @@ impl ModCollector<'_, '_> {
                 }
                 ModItem::Const(id) => {
                     let it = &self.item_tree[id];
-                    let const_id = ConstLoc {
-                        container: module.into(),
-                        id: ItemTreeId::new(self.file_id, id),
-                    }
-                    .intern(self.def_collector.db);
-
-                    match &it.name {
-                        Some(name) => {
-                            def = Some(DefData {
-                                id: const_id.into(),
-                                name,
-                                visibility: &self.item_tree[it.visibility],
-                                has_constructor: false,
-                            });
-                        }
-                        None => {
-                            // const _: T = ...;
-                            self.def_collector.def_map.modules[self.module_id]
-                                .scope
-                                .define_unnamed_const(const_id);
-                        }
+
+                    if let Some(name) = &it.name {
+                        def = Some(DefData {
+                            id: ConstLoc {
+                                container: module.into(),
+                                id: ItemTreeId::new(self.file_id, id),
+                            }
+                            .intern(self.def_collector.db)
+                            .into(),
+                            name,
+                            visibility: &self.item_tree[it.visibility],
+                            has_constructor: false,
+                        });
                     }
                 }
                 ModItem::Static(id) => {
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index b528ff8bad..f9c8328f06 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -289,12 +289,6 @@ impl From<Name> for Path {
     }
 }
 
-impl From<Name> for Box<Path> {
-    fn from(name: Name) -> Box<Path> {
-        Box::new(Path::from(name))
-    }
-}
-
 impl From<Name> for ModPath {
     fn from(name: Name) -> ModPath {
         ModPath::from_segments(PathKind::Plain, iter::once(name))
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index a7d0f5b1fc..75ec4196b6 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -490,7 +490,7 @@ fn env_expand(
         // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
         if key == "OUT_DIR" {
             err = Some(mbe::ExpandError::Other(
-                r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
+                r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(),
             ));
         }
 
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index f8e9db9ae5..7ca4af80e4 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -13,7 +13,7 @@ use log::{info, warn};
 
 use crate::{
     db::HirDatabase, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex,
-    InEnvironment, Interner, ProjectionTyExt, Solution, Ty, TyBuilder, TyKind,
+    InEnvironment, Interner, Solution, Ty, TyBuilder, TyKind,
 };
 
 const AUTODEREF_RECURSION_LIMIT: usize = 10;
@@ -35,21 +35,13 @@ pub(crate) fn deref(
     krate: CrateId,
     ty: InEnvironment<&Canonical<Ty>>,
 ) -> Option<Canonical<Ty>> {
-    if let Some(derefed) = builtin_deref(&ty.goal.value) {
+    if let Some(derefed) = ty.goal.value.builtin_deref() {
         Some(Canonical { value: derefed, binders: ty.goal.binders.clone() })
     } else {
         deref_by_trait(db, krate, ty)
     }
 }
 
-fn builtin_deref(ty: &Ty) -> Option<Ty> {
-    match ty.kind(&Interner) {
-        TyKind::Ref(.., ty) => Some(ty.clone()),
-        TyKind::Raw(.., ty) => Some(ty.clone()),
-        _ => None,
-    }
-}
-
 fn deref_by_trait(
     db: &dyn HirDatabase,
     krate: CrateId,
@@ -128,8 +120,8 @@ fn deref_by_trait(
             // assumptions will be broken. We would need to properly introduce
             // new variables in that case
 
-            for i in 1..vars.binders.len(&Interner) {
-                if vars.value.subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner)
+            for i in 1..vars.0.binders.len(&Interner) {
+                if vars.0.value.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner)
                     != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
                 {
                     warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
@@ -138,12 +130,12 @@ fn deref_by_trait(
             }
             Some(Canonical {
                 value: vars
+                    .0
                     .value
-                    .subst
-                    .at(&Interner, vars.value.subst.len(&Interner) - 1)
+                    .at(&Interner, vars.0.value.len(&Interner) - 1)
                     .assert_ty_ref(&Interner)
                     .clone(),
-                binders: vars.binders.clone(),
+                binders: vars.0.binders.clone(),
             })
         }
         Solution::Ambig(_) => {
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 09512d1ce4..9b2c6975a3 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -12,8 +12,8 @@ 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, TypeWalk, ValueTyDefId,
+    CallableSig, FnPointer, FnSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty,
+    TyDefId, TyKind, TypeWalk, ValueTyDefId,
 };
 
 /// This is a builder for `Ty` or anything that needs a `Substitution`.
@@ -78,12 +78,9 @@ impl TyBuilder<()> {
 
     pub fn fn_ptr(sig: CallableSig) -> Ty {
         TyKind::Function(FnPointer {
-            num_binders: 0,
+            num_args: sig.params().len(),
             sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs },
-            substitution: FnSubst(Substitution::from_iter(
-                &Interner,
-                sig.params_and_return.iter().cloned(),
-            )),
+            substs: Substitution::from_iter(&Interner, sig.params_and_return.iter().cloned()),
         })
         .intern(&Interner)
     }
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 28ed3aac6d..b7463366b8 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -1,305 +1,13 @@
 //! Various extensions traits for Chalk types.
 
-use chalk_ir::Mutability;
-use hir_def::{
-    type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId,
-};
-
-use crate::{
-    db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
-    from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
-    CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
-    Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
-};
+use crate::{Interner, Ty, TyKind};
 
 pub trait TyExt {
     fn is_unit(&self) -> bool;
-    fn is_never(&self) -> bool;
-    fn is_unknown(&self) -> bool;
-
-    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
-    fn as_tuple(&self) -> Option<&Substitution>;
-    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
-    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
-    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
-    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
-
-    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
-    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
-
-    fn strip_references(&self) -> &Ty;
-
-    /// If this is a `dyn Trait`, returns that trait.
-    fn dyn_trait(&self) -> Option<TraitId>;
-
-    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
-    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
-
-    /// FIXME: Get rid of this, it's not a good abstraction
-    fn equals_ctor(&self, other: &Ty) -> bool;
 }
 
 impl TyExt for Ty {
     fn is_unit(&self) -> bool {
         matches!(self.kind(&Interner), TyKind::Tuple(0, _))
     }
-
-    fn is_never(&self) -> bool {
-        matches!(self.kind(&Interner), TyKind::Never)
-    }
-
-    fn is_unknown(&self) -> bool {
-        matches!(self.kind(&Interner), TyKind::Error)
-    }
-
-    fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
-        match self.kind(&Interner) {
-            TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
-            _ => None,
-        }
-    }
-
-    fn as_tuple(&self) -> Option<&Substitution> {
-        match self.kind(&Interner) {
-            TyKind::Tuple(_, substs) => Some(substs),
-            _ => None,
-        }
-    }
-
-    fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
-        if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
-            Some(func)
-        } else {
-            None
-        }
-    }
-    fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
-        match self.kind(&Interner) {
-            TyKind::Ref(mutability, lifetime, ty) => Some((ty, *lifetime, *mutability)),
-            _ => None,
-        }
-    }
-
-    fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
-        match self.kind(&Interner) {
-            TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
-            TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
-            _ => None,
-        }
-    }
-
-    fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
-        match *self.kind(&Interner) {
-            TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
-            TyKind::FnDef(callable, ..) => {
-                Some(db.lookup_intern_callable_def(callable.into()).into())
-            }
-            TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
-            TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
-            _ => None,
-        }
-    }
-
-    fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
-        match self.kind(&Interner) {
-            &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
-            _ => None,
-        }
-    }
-
-    fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
-        match self.kind(&Interner) {
-            TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
-            TyKind::FnDef(def, parameters) => {
-                let callable_def = db.lookup_intern_callable_def((*def).into());
-                let sig = db.callable_item_signature(callable_def);
-                Some(sig.substitute(&Interner, &parameters))
-            }
-            TyKind::Closure(.., substs) => {
-                let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
-                sig_param.callable_sig(db)
-            }
-            _ => None,
-        }
-    }
-
-    fn dyn_trait(&self) -> Option<TraitId> {
-        let trait_ref = match self.kind(&Interner) {
-            TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
-                match b.skip_binders() {
-                    WhereClause::Implemented(trait_ref) => Some(trait_ref),
-                    _ => None,
-                }
-            }),
-            _ => None,
-        }?;
-        Some(from_chalk_trait_id(trait_ref.trait_id))
-    }
-
-    fn strip_references(&self) -> &Ty {
-        let mut t: &Ty = self;
-        while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(&Interner) {
-            t = ty;
-        }
-        t
-    }
-
-    fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
-        match self.kind(&Interner) {
-            TyKind::OpaqueType(opaque_ty_id, ..) => {
-                match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
-                    ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
-                        let krate = def.module(db.upcast()).krate();
-                        if let Some(future_trait) = db
-                            .lang_item(krate, "future_trait".into())
-                            .and_then(|item| item.as_trait())
-                        {
-                            // This is only used by type walking.
-                            // Parameters will be walked outside, and projection predicate is not used.
-                            // So just provide the Future trait.
-                            let impl_bound = Binders::empty(
-                                &Interner,
-                                WhereClause::Implemented(TraitRef {
-                                    trait_id: to_chalk_trait_id(future_trait),
-                                    substitution: Substitution::empty(&Interner),
-                                }),
-                            );
-                            Some(vec![impl_bound])
-                        } else {
-                            None
-                        }
-                    }
-                    ImplTraitId::ReturnTypeImplTrait(..) => None,
-                }
-            }
-            TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
-                let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
-                {
-                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                        db.return_type_impl_traits(func).map(|it| {
-                            let data = (*it)
-                                .as_ref()
-                                .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
-                            data.substitute(&Interner, &opaque_ty.substitution)
-                        })
-                    }
-                    // It always has an parameter for Future::Output type.
-                    ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
-                };
-
-                predicates.map(|it| it.into_value_and_skipped_binders().0)
-            }
-            TyKind::Placeholder(idx) => {
-                let id = from_placeholder_idx(db, *idx);
-                let generic_params = db.generic_params(id.parent);
-                let param_data = &generic_params.types[id.local_id];
-                match param_data.provenance {
-                    hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
-                        let substs = TyBuilder::type_params_subst(db, id.parent);
-                        let predicates = db
-                            .generic_predicates(id.parent)
-                            .into_iter()
-                            .map(|pred| pred.clone().substitute(&Interner, &substs))
-                            .filter(|wc| match &wc.skip_binders() {
-                                WhereClause::Implemented(tr) => {
-                                    &tr.self_type_parameter(&Interner) == self
-                                }
-                                WhereClause::AliasEq(AliasEq {
-                                    alias: AliasTy::Projection(proj),
-                                    ty: _,
-                                }) => &proj.self_type_parameter(&Interner) == self,
-                                _ => false,
-                            })
-                            .collect::<Vec<_>>();
-
-                        Some(predicates)
-                    }
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-    }
-
-    fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
-        match self.kind(&Interner) {
-            TyKind::AssociatedType(id, ..) => {
-                match from_assoc_type_id(*id).lookup(db.upcast()).container {
-                    AssocContainerId::TraitId(trait_id) => Some(trait_id),
-                    _ => None,
-                }
-            }
-            TyKind::Alias(AliasTy::Projection(projection_ty)) => {
-                match from_assoc_type_id(projection_ty.associated_ty_id)
-                    .lookup(db.upcast())
-                    .container
-                {
-                    AssocContainerId::TraitId(trait_id) => Some(trait_id),
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-    }
-
-    fn equals_ctor(&self, other: &Ty) -> bool {
-        match (self.kind(&Interner), other.kind(&Interner)) {
-            (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
-            (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => {
-                true
-            }
-            (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
-            (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
-            (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
-                ty_id == ty_id2
-            }
-            (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
-            (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
-            (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
-            | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
-                mutability == mutability2
-            }
-            (
-                TyKind::Function(FnPointer { num_binders, sig, .. }),
-                TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }),
-            ) => num_binders == num_binders2 && sig == sig2,
-            (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
-                cardinality == cardinality2
-            }
-            (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
-            (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
-            _ => false,
-        }
-    }
-}
-
-pub trait ProjectionTyExt {
-    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
-    fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
-}
-
-impl ProjectionTyExt for ProjectionTy {
-    fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
-        TraitRef {
-            trait_id: to_chalk_trait_id(self.trait_(db)),
-            substitution: self.substitution.clone(),
-        }
-    }
-
-    fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
-        match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
-            AssocContainerId::TraitId(it) => it,
-            _ => panic!("projection ty without parent trait"),
-        }
-    }
-}
-
-pub trait TraitRefExt {
-    fn hir_trait_id(&self) -> TraitId;
-}
-
-impl TraitRefExt for TraitRef {
-    fn hir_trait_id(&self) -> TraitId {
-        from_chalk_trait_id(self.trait_id)
-    }
 }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 326c202404..4300680d98 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
 use base_db::{impl_intern_key, salsa, CrateId, Upcast};
 use hir_def::{
     db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
-    LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
+    LocalFieldId, TypeParamId, VariantId,
 };
 use la_arena::ArenaMap;
 
@@ -86,10 +86,6 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::interned]
     fn intern_type_param_id(&self, param_id: TypeParamId) -> InternedTypeParamId;
     #[salsa::interned]
-    fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId;
-    #[salsa::interned]
-    fn intern_const_param_id(&self, param_id: ConstParamId) -> InternedConstParamId;
-    #[salsa::interned]
     fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
     #[salsa::interned]
     fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId;
@@ -159,14 +155,6 @@ fn hir_database_is_object_safe() {
 pub struct InternedTypeParamId(salsa::InternId);
 impl_intern_key!(InternedTypeParamId);
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedLifetimeParamId(salsa::InternId);
-impl_intern_key!(InternedLifetimeParamId);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InternedConstParamId(salsa::InternId);
-impl_intern_key!(InternedConstParamId);
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct InternedOpaqueTyId(salsa::InternId);
 impl_intern_key!(InternedOpaqueTyId);
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 79602c3dde..db278d0db2 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -14,6 +14,7 @@ use crate::{
         MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
         MissingPatFields, RemoveThisSemicolon,
     },
+    utils::variant_data,
     AdtId, InferenceResult, Interner, TyExt, TyKind,
 };
 
@@ -103,7 +104,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             let root = source_ptr.file_syntax(db.upcast());
             if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
                 if let Some(_) = record_expr.record_expr_field_list() {
-                    let variant_data = variant_def.variant_data(db.upcast());
+                    let variant_data = variant_data(db.upcast(), variant_def);
                     let missed_fields = missed_fields
                         .into_iter()
                         .map(|idx| variant_data.fields()[idx].name.clone())
@@ -134,7 +135,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                 let root = source_ptr.file_syntax(db.upcast());
                 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
                     if let Some(_) = record_pat.record_pat_field_list() {
-                        let variant_data = variant_def.variant_data(db.upcast());
+                        let variant_data = variant_data(db.upcast(), variant_def);
                         let missed_fields = missed_fields
                             .into_iter()
                             .map(|idx| variant_data.fields()[idx].name.clone())
@@ -314,7 +315,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                 if pat_ty == match_expr_ty
                     || match_expr_ty
                         .as_reference()
-                        .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
+                        .map(|(match_expr_ty, _)| match_expr_ty == pat_ty)
                         .unwrap_or(false)
                 {
                     // If we had a NotUsefulMatchArm diagnostic, we could
@@ -452,7 +453,7 @@ pub fn record_literal_missing_fields(
         return None;
     }
 
-    let variant_data = variant_def.variant_data(db.upcast());
+    let variant_data = variant_data(db.upcast(), variant_def);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
     let missed_fields: Vec<LocalFieldId> = variant_data
@@ -482,7 +483,7 @@ pub fn record_pattern_missing_fields(
         return None;
     }
 
-    let variant_data = variant_def.variant_data(db.upcast());
+    let variant_data = variant_data(db.upcast(), variant_def);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
     let missed_fields: Vec<LocalFieldId> = variant_data
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index e9762622f3..34291578aa 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -227,7 +227,7 @@ use hir_def::{
 use la_arena::Idx;
 use smallvec::{smallvec, SmallVec};
 
-use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyExt, TyKind};
+use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyKind};
 
 #[derive(Debug, Clone, Copy)]
 /// Either a pattern from the source code being analyzed, represented as
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index ed97dc0e3f..b5efe9df5c 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -11,9 +11,7 @@ use hir_def::{
 };
 use hir_expand::diagnostics::DiagnosticSink;
 
-use crate::{
-    db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind,
-};
+use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyKind};
 
 pub(super) struct UnsafeValidator<'a, 'b: 'a> {
     owner: DefWithBodyId,
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index e0ca96c6d2..f31e6b1087 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1,11 +1,8 @@
 //! FIXME: write short doc here
 
-use std::{
-    array,
-    fmt::{self, Debug},
-};
+use std::{array, fmt};
 
-use chalk_ir::BoundVar;
+use chalk_ir::Mutability;
 use hir_def::{
     db::DefDatabase,
     find_path,
@@ -19,12 +16,10 @@ use hir_def::{
 use hir_expand::name::Name;
 
 use crate::{
-    const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id,
-    from_placeholder_idx, lt_from_placeholder_idx, primitive, subst_prefix, to_assoc_type_id,
-    traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy, CallableDefId,
-    CallableSig, Const, ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime,
-    LifetimeData, LifetimeOutlives, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
-    QuantifiedWhereClause, Scalar, TraitRef, TraitRefExt, Ty, TyExt, TyKind, WhereClause,
+    db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive,
+    to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy,
+    CallableDefId, CallableSig, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, OpaqueTy,
+    ProjectionTy, QuantifiedWhereClause, Scalar, TraitRef, Ty, TyExt, TyKind, WhereClause,
 };
 
 pub struct HirFormatter<'a> {
@@ -51,10 +46,6 @@ pub trait HirDisplay {
     where
         Self: Sized,
     {
-        assert!(
-            !matches!(display_target, DisplayTarget::SourceCode { .. }),
-            "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
-        );
         HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
     }
 
@@ -239,7 +230,7 @@ where
             Err(HirDisplayError::FmtError) => Err(fmt::Error),
             Err(HirDisplayError::DisplaySourceCodeError(_)) => {
                 // This should never happen
-                panic!("HirDisplay::hir_fmt failed with DisplaySourceCodeError when calling Display::fmt!")
+                panic!("HirDisplay failed when calling Display::fmt!")
             }
         }
     }
@@ -260,9 +251,13 @@ impl HirDisplay for ProjectionTy {
         }
 
         let trait_ = f.db.trait_data(self.trait_(f.db));
-        write!(f, "<")?;
-        self.self_type_parameter(&Interner).hir_fmt(f)?;
-        write!(f, " as {}", trait_.name)?;
+        let first_parameter = self.self_type_parameter(&Interner).into_displayable(
+            f.db,
+            f.max_size,
+            f.omit_verbose_types,
+            f.display_target,
+        );
+        write!(f, "<{} as {}", first_parameter, trait_.name)?;
         if self.substitution.len(&Interner) > 1 {
             write!(f, "<")?;
             f.write_joined(&self.substitution.interned()[1..], ", ")?;
@@ -291,29 +286,6 @@ impl HirDisplay for GenericArg {
     }
 }
 
-impl HirDisplay for Const {
-    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        let data = self.interned();
-        match data.value {
-            ConstValue::BoundVar(idx) => idx.hir_fmt(f),
-            ConstValue::InferenceVar(..) => write!(f, "_"),
-            ConstValue::Placeholder(idx) => {
-                let id = const_from_placeholder_idx(f.db, idx);
-                let generics = generics(f.db.upcast(), id.parent);
-                let param_data = &generics.params.consts[id.local_id];
-                write!(f, "{}", param_data.name)
-            }
-            ConstValue::Concrete(_) => write!(f, "_"),
-        }
-    }
-}
-
-impl HirDisplay for BoundVar {
-    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        write!(f, "?{}.{}", self.debruijn.depth(), self.index)
-    }
-}
-
 impl HirDisplay for Ty {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         if f.should_truncate() {
@@ -333,14 +305,15 @@ impl HirDisplay for Ty {
                 t.hir_fmt(f)?;
                 write!(f, "]")?;
             }
-            TyKind::Array(t, c) => {
+            TyKind::Array(t) => {
                 write!(f, "[")?;
                 t.hir_fmt(f)?;
-                write!(f, "; ")?;
-                c.hir_fmt(f)?;
-                write!(f, "]")?;
+                write!(f, "; _]")?;
             }
-            TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => {
+            TyKind::Raw(m, t) | TyKind::Ref(m, t) => {
+                let ty_display =
+                    t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
+
                 if matches!(self.kind(&Interner), TyKind::Raw(..)) {
                     write!(
                         f,
@@ -395,16 +368,16 @@ impl HirDisplay for Ty {
                     if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_)
                         && predicates.len() <= 2
                     {
-                        return t.hir_fmt(f);
+                        return write!(f, "{}", ty_display);
                     }
                 }
 
                 if predicates.len() > 1 {
                     write!(f, "(")?;
-                    t.hir_fmt(f)?;
+                    write!(f, "{}", ty_display)?;
                     write!(f, ")")?;
                 } else {
-                    t.hir_fmt(f)?;
+                    write!(f, "{}", ty_display)?;
                 }
             }
             TyKind::Tuple(_, substs) => {
@@ -451,8 +424,14 @@ impl HirDisplay for Ty {
                 write!(f, ")")?;
                 let ret = sig.ret();
                 if !ret.is_unit() {
-                    write!(f, " -> ")?;
-                    ret.hir_fmt(f)?;
+                    let ret_display = ret.into_displayable(
+                        f.db,
+                        f.max_size,
+                        f.omit_verbose_types,
+                        f.display_target,
+                    );
+
+                    write!(f, " -> {}", ret_display)?;
                 }
             }
             TyKind::Adt(AdtId(def_id), parameters) => {
@@ -501,11 +480,9 @@ impl HirDisplay for Ty {
                                             default_from = i + 1;
                                         }
                                         (_, Some(default_parameter)) => {
-                                            let actual_default =
-                                                default_parameter.clone().substitute(
-                                                    &Interner,
-                                                    &subst_prefix(parameters, i),
-                                                );
+                                            let actual_default = default_parameter
+                                                .clone()
+                                                .substitute(&Interner, &parameters.prefix(i));
                                             if parameter.assert_ty_ref(&Interner) != &actual_default
                                             {
                                                 default_from = i + 1;
@@ -594,8 +571,13 @@ impl HirDisplay for Ty {
                         write!(f, "|")?;
                     };
 
-                    write!(f, " -> ")?;
-                    sig.ret().hir_fmt(f)?;
+                    let ret_display = sig.ret().into_displayable(
+                        f.db,
+                        f.max_size,
+                        f.omit_verbose_types,
+                        f.display_target,
+                    );
+                    write!(f, " -> {}", ret_display)?;
                 } else {
                     write!(f, "{{closure}}")?;
                 }
@@ -616,12 +598,12 @@ impl HirDisplay for Ty {
                                 .map(|pred| pred.clone().substitute(&Interner, &substs))
                                 .filter(|wc| match &wc.skip_binders() {
                                     WhereClause::Implemented(tr) => {
-                                        &tr.self_type_parameter(&Interner) == self
+                                        tr.self_type_parameter(&Interner) == self
                                     }
                                     WhereClause::AliasEq(AliasEq {
                                         alias: AliasTy::Projection(proj),
                                         ty: _,
-                                    }) => &proj.self_type_parameter(&Interner) == self,
+                                    }) => proj.self_type_parameter(&Interner) == self,
                                     _ => false,
                                 })
                                 .collect::<Vec<_>>();
@@ -629,7 +611,7 @@ impl HirDisplay for Ty {
                     }
                 }
             }
-            TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
+            TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
             TyKind::Dyn(dyn_ty) => {
                 write_bounds_like_dyn_trait_with_prefix(
                     "dyn",
@@ -683,8 +665,9 @@ impl HirDisplay for CallableSig {
         write!(f, ")")?;
         let ret = self.ret();
         if !ret.is_unit() {
-            write!(f, " -> ")?;
-            ret.hir_fmt(f)?;
+            let ret_display =
+                ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
+            write!(f, " -> {}", ret_display)?;
         }
         Ok(())
     }
@@ -745,7 +728,7 @@ fn write_bounds_like_dyn_trait(
                 // existential) here, which is the only thing that's
                 // possible in actual Rust, and hence don't print it
                 write!(f, "{}", f.db.trait_data(trait_).name)?;
-                if let [_, params @ ..] = &*trait_ref.substitution.interned().as_slice() {
+                if let [_, params @ ..] = &*trait_ref.substitution.interned() {
                     if is_fn_trait {
                         if let Some(args) =
                             params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple())
@@ -792,29 +775,31 @@ fn write_bounds_like_dyn_trait(
     Ok(())
 }
 
-fn fmt_trait_ref(tr: &TraitRef, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
-    if f.should_truncate() {
-        return write!(f, "{}", TYPE_HINT_TRUNCATION);
-    }
+impl TraitRef {
+    fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
+        if f.should_truncate() {
+            return write!(f, "{}", TYPE_HINT_TRUNCATION);
+        }
 
-    tr.self_type_parameter(&Interner).hir_fmt(f)?;
-    if use_as {
-        write!(f, " as ")?;
-    } else {
-        write!(f, ": ")?;
-    }
-    write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?;
-    if tr.substitution.len(&Interner) > 1 {
-        write!(f, "<")?;
-        f.write_joined(&tr.substitution.interned()[1..], ", ")?;
-        write!(f, ">")?;
+        self.self_type_parameter(&Interner).hir_fmt(f)?;
+        if use_as {
+            write!(f, " as ")?;
+        } else {
+            write!(f, ": ")?;
+        }
+        write!(f, "{}", f.db.trait_data(self.hir_trait_id()).name)?;
+        if self.substitution.len(&Interner) > 1 {
+            write!(f, "<")?;
+            f.write_joined(&self.substitution.interned()[1..], ", ")?;
+            write!(f, ">")?;
+        }
+        Ok(())
     }
-    Ok(())
 }
 
 impl HirDisplay for TraitRef {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        fmt_trait_ref(self, f, false)
+        self.hir_fmt_ext(f, false)
     }
 }
 
@@ -828,7 +813,7 @@ impl HirDisplay for WhereClause {
             WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
             WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
                 write!(f, "<")?;
-                fmt_trait_ref(&projection_ty.trait_ref(f.db), f, true)?;
+                projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
                 write!(
                     f,
                     ">::{} = ",
@@ -842,35 +827,15 @@ impl HirDisplay for WhereClause {
     }
 }
 
-impl HirDisplay for LifetimeOutlives {
-    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        self.a.hir_fmt(f)?;
-        write!(f, ": ")?;
-        self.b.hir_fmt(f)
-    }
-}
-
 impl HirDisplay for Lifetime {
-    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        self.interned().hir_fmt(f)
-    }
-}
-
-impl HirDisplay for LifetimeData {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         match self {
-            LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
-            LifetimeData::InferenceVar(_) => write!(f, "_"),
-            LifetimeData::Placeholder(idx) => {
-                let id = lt_from_placeholder_idx(f.db, *idx);
+            Lifetime::Parameter(id) => {
                 let generics = generics(f.db.upcast(), id.parent);
                 let param_data = &generics.params.lifetimes[id.local_id];
-                write!(f, "{}", param_data.name)
+                write!(f, "{}", &param_data.name)
             }
-            LifetimeData::Static => write!(f, "'static"),
-            LifetimeData::Empty(_) => Ok(()),
-            LifetimeData::Erased => Ok(()),
-            LifetimeData::Phantom(_, _) => Ok(()),
+            Lifetime::Static => write!(f, "'static"),
         }
     }
 }
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 531159e542..1c3faf5cb6 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -42,7 +42,7 @@ use super::{
 };
 use crate::{
     db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
-    to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner, TyBuilder, TyExt, TyKind,
+    to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -131,7 +131,10 @@ pub struct InferenceResult {
     method_resolutions: FxHashMap<ExprId, FunctionId>,
     /// For each field access expr, records the field it resolves to.
     field_resolutions: FxHashMap<ExprId, FieldId>,
-    /// For each struct literal or pattern, records the variant it resolves to.
+    /// For each field in record literal, records the field it resolves to.
+    record_field_resolutions: FxHashMap<ExprId, FieldId>,
+    record_pat_field_resolutions: FxHashMap<PatId, FieldId>,
+    /// For each struct literal, records the variant it resolves to.
     variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
     /// For each associated item record what it resolves to
     assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
@@ -150,6 +153,12 @@ impl InferenceResult {
     pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
         self.field_resolutions.get(&expr).copied()
     }
+    pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> {
+        self.record_field_resolutions.get(&expr).copied()
+    }
+    pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> {
+        self.record_pat_field_resolutions.get(&pat).copied()
+    }
     pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
         self.variant_resolutions.get(&id.into()).copied()
     }
@@ -336,24 +345,17 @@ impl<'a> InferenceContext<'a> {
         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 in_env = InEnvironment::new(self.trait_env.env.clone(), 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::Unique(substs)) => {
+                    canonicalized.apply_solution(self, substs.0);
                 }
                 Some(Solution::Ambig(Guidance::Definite(substs))) => {
-                    canonicalized.apply_solution(self, substs);
+                    canonicalized.apply_solution(self, substs.0);
                     self.obligations.push(obligation);
                 }
                 Some(_) => {
@@ -487,13 +489,36 @@ impl<'a> InferenceContext<'a> {
                 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
                 let substs = generics.type_params_subst(self.db);
                 let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs);
-                self.resolve_variant_on_alias(ty, unresolved, path)
+                match unresolved {
+                    None => {
+                        let variant = ty_variant(&ty);
+                        (ty, variant)
+                    }
+                    Some(1) => {
+                        let segment = path.mod_path().segments().last().unwrap();
+                        // this could be an enum variant or associated type
+                        if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
+                            let enum_data = self.db.enum_data(enum_id);
+                            if let Some(local_id) = enum_data.variant(segment) {
+                                let variant = EnumVariantId { parent: enum_id, local_id };
+                                return (ty, Some(variant.into()));
+                            }
+                        }
+                        // FIXME potentially resolve assoc type
+                        (self.err_ty(), None)
+                    }
+                    Some(_) => {
+                        // FIXME diagnostic
+                        (self.err_ty(), None)
+                    }
+                }
             }
             TypeNs::TypeAliasId(it) => {
                 let ty = TyBuilder::def_ty(self.db, it.into())
                     .fill(std::iter::repeat_with(|| self.table.new_type_var()))
                     .build();
-                self.resolve_variant_on_alias(ty, unresolved, path)
+                let variant = ty_variant(&ty);
+                forbid_unresolved_segments((ty, variant), unresolved)
             }
             TypeNs::AdtSelfType(_) => {
                 // FIXME this could happen in array size expressions, once we're checking them
@@ -520,43 +545,16 @@ impl<'a> InferenceContext<'a> {
                 (TyKind::Error.intern(&Interner), None)
             }
         }
-    }
 
-    fn resolve_variant_on_alias(
-        &mut self,
-        ty: Ty,
-        unresolved: Option<usize>,
-        path: &Path,
-    ) -> (Ty, Option<VariantId>) {
-        match unresolved {
-            None => {
-                let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
-                    AdtId::StructId(s) => Some(VariantId::StructId(s)),
-                    AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
-                    AdtId::EnumId(_) => {
-                        // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
-                        None
-                    }
-                });
-                (ty, variant)
-            }
-            Some(1) => {
-                let segment = path.mod_path().segments().last().unwrap();
-                // this could be an enum variant or associated type
-                if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
-                    let enum_data = self.db.enum_data(enum_id);
-                    if let Some(local_id) = enum_data.variant(segment) {
-                        let variant = EnumVariantId { parent: enum_id, local_id };
-                        return (ty, Some(variant.into()));
-                    }
+        fn ty_variant(ty: &Ty) -> Option<VariantId> {
+            ty.as_adt().and_then(|(adt_id, _)| match adt_id {
+                AdtId::StructId(s) => Some(VariantId::StructId(s)),
+                AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
+                AdtId::EnumId(_) => {
+                    // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
+                    None
                 }
-                // FIXME potentially resolve assoc type
-                (self.err_ty(), None)
-            }
-            Some(_) => {
-                // FIXME diagnostic
-                (self.err_ty(), None)
-            }
+            })
         }
     }
 
@@ -694,6 +692,25 @@ impl<'a> InferenceContext<'a> {
     }
 }
 
+/// The kinds of placeholders we need during type inference. There's separate
+/// values for general types, and for integer and float variables. The latter
+/// two are used for inference of literal values (e.g. `100` could be one of
+/// several integer types).
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub struct InferenceVar {
+    index: u32,
+}
+
+impl InferenceVar {
+    fn to_inner(self) -> unify::TypeVarId {
+        unify::TypeVarId(self.index)
+    }
+
+    fn from_inner(unify::TypeVarId(index): unify::TypeVarId) -> Self {
+        InferenceVar { index }
+    }
+}
+
 /// When inferring an expression, we propagate downward whatever type hint we
 /// are able in the form of an `Expectation`.
 #[derive(Clone, PartialEq, Eq, Debug)]
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index fd679f4446..32c273afcd 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -7,7 +7,7 @@
 use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
 use hir_def::lang_item::LangItemTarget;
 
-use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind};
+use crate::{autoderef, Interner, Solution, Ty, TyBuilder, TyKind};
 
 use super::{InEnvironment, InferenceContext};
 
@@ -81,7 +81,7 @@ impl<'a> InferenceContext<'a> {
             // `&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, ..)) => {
+            | (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => {
                 from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
             }
 
@@ -111,9 +111,7 @@ impl<'a> InferenceContext<'a> {
         // 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)
-            }
+            (TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2),
 
             // Otherwise, normal unify
             _ => self.unify(&from_ty, to_ty),
@@ -139,7 +137,7 @@ impl<'a> InferenceContext<'a> {
             b.push(from_ty.clone()).push(to_ty.clone()).build()
         };
 
-        let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
+        let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner));
 
         let canonicalizer = self.canonicalizer();
         let canonicalized = canonicalizer.canonicalize_obligation(goal);
@@ -148,14 +146,7 @@ impl<'a> InferenceContext<'a> {
 
         match solution {
             Solution::Unique(v) => {
-                canonicalized.apply_solution(
-                    self,
-                    Canonical {
-                        binders: v.binders,
-                        // FIXME handle constraints
-                        value: v.value.subst,
-                    },
-                );
+                canonicalized.apply_solution(self, v.0);
             }
             _ => return None,
         };
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 9841988c55..6966d26e7f 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -15,16 +15,15 @@ use stdx::always;
 use syntax::ast::RangeOp;
 
 use crate::{
-    autoderef, dummy_usize_const,
+    autoderef,
     lower::lower_to_chalk_mutability,
     method_resolution, op,
     primitive::{self, UintTy},
-    static_lifetime, to_chalk_trait_id,
+    to_chalk_trait_id,
     traits::{chalk::from_chalk, FnTrait},
-    utils::{generics, Generics},
-    AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
-    ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
-    TypeWalk,
+    utils::{generics, variant_data, Generics},
+    AdtId, Binders, CallableDefId, FnPointer, FnSig, InEnvironment, Interner, Rawness, Scalar,
+    Substitution, TraitRef, Ty, TyBuilder, TyKind,
 };
 
 use super::{
@@ -181,8 +180,7 @@ impl<'a> InferenceContext<'a> {
                 let inner_ty = self.infer_expr(*body, &Expectation::none());
                 let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
                 let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
-                TyKind::OpaqueType(opaque_ty_id, Substitution::from1(&Interner, inner_ty))
-                    .intern(&Interner)
+                TyKind::OpaqueType(opaque_ty_id, Substitution::single(inner_ty)).intern(&Interner)
             }
             Expr::Loop { body, label } => {
                 self.breakables.push(BreakableContext {
@@ -261,17 +259,14 @@ impl<'a> InferenceContext<'a> {
                 };
                 sig_tys.push(ret_ty.clone());
                 let sig_ty = TyKind::Function(FnPointer {
-                    num_binders: 0,
+                    num_args: sig_tys.len() - 1,
                     sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
-                    substitution: FnSubst(
-                        Substitution::from_iter(&Interner, sig_tys.clone()).shifted_in(&Interner),
-                    ),
+                    substs: Substitution::from_iter(&Interner, sig_tys.clone()),
                 })
                 .intern(&Interner);
                 let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
                 let closure_ty =
-                    TyKind::Closure(closure_id, Substitution::from1(&Interner, sig_ty))
-                        .intern(&Interner);
+                    TyKind::Closure(closure_id, Substitution::single(sig_ty)).intern(&Interner);
 
                 // Eagerly try to relate the closure type with the expected
                 // type, otherwise we often won't have enough information to
@@ -318,13 +313,7 @@ impl<'a> InferenceContext<'a> {
                 self.normalize_associated_types_in(ret_ty)
             }
             Expr::MethodCall { receiver, args, method_name, generic_args } => self
-                .infer_method_call(
-                    tgt_expr,
-                    *receiver,
-                    &args,
-                    &method_name,
-                    generic_args.as_deref(),
-                ),
+                .infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()),
             Expr::Match { expr, arms } => {
                 let input_ty = self.infer_expr(*expr, &Expectation::none());
 
@@ -405,19 +394,16 @@ impl<'a> InferenceContext<'a> {
                 TyKind::Never.intern(&Interner)
             }
             Expr::RecordLit { path, fields, spread } => {
-                let (ty, def_id) = self.resolve_variant(path.as_deref());
+                let (ty, def_id) = self.resolve_variant(path.as_ref());
                 if let Some(variant) = def_id {
                     self.write_variant_resolution(tgt_expr.into(), variant);
                 }
 
                 self.unify(&ty, &expected.ty);
 
-                let substs = ty
-                    .as_adt()
-                    .map(|(_, s)| s.clone())
-                    .unwrap_or_else(|| Substitution::empty(&Interner));
+                let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner));
                 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
-                let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
+                let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it));
                 for field in fields.iter() {
                     let field_def =
                         variant_data.as_ref().and_then(|it| match it.field(&field.name) {
@@ -429,6 +415,9 @@ impl<'a> InferenceContext<'a> {
                                 None
                             }
                         });
+                    if let Some(field_def) = field_def {
+                        self.result.record_field_resolutions.insert(field.expr, field_def);
+                    }
                     let field_ty = field_def.map_or(self.err_ty(), |it| {
                         field_types[it.local_id].clone().substitute(&Interner, &substs)
                     });
@@ -534,7 +523,7 @@ impl<'a> InferenceContext<'a> {
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
                 match rawness {
                     Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
-                    Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty),
+                    Rawness::Ref => TyKind::Ref(mutability, inner_ty),
                 }
                 .intern(&Interner)
             }
@@ -709,7 +698,7 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Array(array) => {
                 let elem_ty = match expected.ty.kind(&Interner) {
-                    TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
+                    TyKind::Array(st) | TyKind::Slice(st) => st.clone(),
                     _ => self.table.new_type_var(),
                 };
 
@@ -733,19 +722,17 @@ impl<'a> InferenceContext<'a> {
                     }
                 }
 
-                TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner)
+                TyKind::Array(elem_ty).intern(&Interner)
             }
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
                 Literal::String(..) => {
-                    TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner))
-                        .intern(&Interner)
+                    TyKind::Ref(Mutability::Not, TyKind::Str.intern(&Interner)).intern(&Interner)
                 }
                 Literal::ByteString(..) => {
                     let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
-                    let array_type =
-                        TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
-                    TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
+                    let array_type = TyKind::Array(byte_type).intern(&Interner);
+                    TyKind::Ref(Mutability::Not, array_type).intern(&Interner)
                 }
                 Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner),
                 Literal::Int(_v, ty) => match ty {
@@ -881,9 +868,7 @@ 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() {
-            Some((_, lifetime, mutability)) => {
-                TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner)
-            }
+            Some((_, mutability)) => TyKind::Ref(mutability, derefed_receiver_ty).intern(&Interner),
             _ => derefed_receiver_ty,
         };
         self.unify(&expected_receiver_ty, &actual_receiver_ty);
@@ -977,10 +962,8 @@ impl<'a> InferenceContext<'a> {
                     if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container
                     {
                         // construct a TraitRef
-                        let substs = crate::subst_prefix(
-                            &*parameters,
-                            generics(self.db.upcast(), trait_.into()).len(),
-                        );
+                        let substs =
+                            parameters.prefix(generics(self.db.upcast(), trait_.into()).len());
                         self.push_obligation(
                             TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
                                 .cast(&Interner),
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index a41e8e1166..252ae914ab 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -7,13 +7,14 @@ use chalk_ir::Mutability;
 use hir_def::{
     expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
     path::Path,
+    FieldId,
 };
 use hir_expand::name::Name;
 
 use super::{BindingMode, Expectation, InferenceContext};
 use crate::{
-    lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
-    TyExt, TyKind,
+    lower::lower_to_chalk_mutability, utils::variant_data, Interner, Substitution, Ty, TyBuilder,
+    TyKind,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -27,14 +28,13 @@ impl<'a> InferenceContext<'a> {
         ellipsis: Option<usize>,
     ) -> Ty {
         let (ty, def) = self.resolve_variant(path);
-        let var_data = def.map(|it| it.variant_data(self.db.upcast()));
+        let var_data = def.map(|it| variant_data(self.db.upcast(), it));
         if let Some(variant) = def {
             self.write_variant_resolution(id.into(), variant);
         }
         self.unify(&ty, expected);
 
-        let substs =
-            ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
+        let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner));
 
         let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
         let (pre, post) = match ellipsis {
@@ -68,19 +68,23 @@ impl<'a> InferenceContext<'a> {
         id: PatId,
     ) -> Ty {
         let (ty, def) = self.resolve_variant(path);
-        let var_data = def.map(|it| it.variant_data(self.db.upcast()));
+        let var_data = def.map(|it| variant_data(self.db.upcast(), it));
         if let Some(variant) = def {
             self.write_variant_resolution(id.into(), variant);
         }
 
         self.unify(&ty, expected);
 
-        let substs =
-            ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
+        let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner));
 
         let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
         for subpat in subpats {
             let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
+            if let Some(local_id) = matching_field {
+                let field_def = FieldId { parent: def.unwrap(), local_id };
+                self.result.record_pat_field_resolutions.insert(subpat.pat, field_def);
+            }
+
             let expected_ty = matching_field.map_or(self.err_ty(), |field| {
                 field_tys[field].clone().substitute(&Interner, &substs)
             });
@@ -100,7 +104,7 @@ impl<'a> InferenceContext<'a> {
         let body = Arc::clone(&self.body); // avoid borrow checker problem
 
         if is_non_ref_pat(&body, pat) {
-            while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
+            while let Some((inner, mutability)) = expected.as_reference() {
                 expected = inner;
                 default_bm = match default_bm {
                     BindingMode::Move => BindingMode::Ref(mutability),
@@ -122,7 +126,7 @@ impl<'a> InferenceContext<'a> {
         let ty = match &body[pat] {
             &Pat::Tuple { ref args, ellipsis } => {
                 let expectations = match expected.as_tuple() {
-                    Some(parameters) => &*parameters.interned().as_slice(),
+                    Some(parameters) => &*parameters.interned(),
                     _ => &[],
                 };
 
@@ -158,7 +162,7 @@ impl<'a> InferenceContext<'a> {
             Pat::Ref { pat, mutability } => {
                 let mutability = lower_to_chalk_mutability(*mutability);
                 let expectation = match expected.as_reference() {
-                    Some((inner_ty, _lifetime, exp_mut)) => {
+                    Some((inner_ty, exp_mut)) => {
                         if mutability != exp_mut {
                             // FIXME: emit type error?
                         }
@@ -167,10 +171,10 @@ impl<'a> InferenceContext<'a> {
                     _ => self.result.standard_types.unknown.clone(),
                 };
                 let subty = self.infer_pat(*pat, &expectation, default_bm);
-                TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner)
+                TyKind::Ref(mutability, subty).intern(&Interner)
             }
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
-                p.as_deref(),
+                p.as_ref(),
                 subpats,
                 expected,
                 default_bm,
@@ -178,7 +182,7 @@ impl<'a> InferenceContext<'a> {
                 *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_ref(), fields, expected, default_bm, pat)
             }
             Pat::Path(path) => {
                 // FIXME use correct resolver for the surrounding expression
@@ -200,8 +204,7 @@ impl<'a> InferenceContext<'a> {
 
                 let bound_ty = match mode {
                     BindingMode::Ref(mutability) => {
-                        TyKind::Ref(mutability, static_lifetime(), inner_ty.clone())
-                            .intern(&Interner)
+                        TyKind::Ref(mutability, inner_ty.clone()).intern(&Interner)
                     }
                     BindingMode::Move => inner_ty.clone(),
                 };
@@ -210,20 +213,17 @@ impl<'a> InferenceContext<'a> {
                 return inner_ty;
             }
             Pat::Slice { prefix, slice, suffix } => {
-                let elem_ty = match expected.kind(&Interner) {
-                    TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
-                    _ => self.err_ty(),
+                let (container_ty, elem_ty): (fn(_) -> _, _) = match expected.kind(&Interner) {
+                    TyKind::Array(st) => (TyKind::Array, st.clone()),
+                    TyKind::Slice(st) => (TyKind::Slice, st.clone()),
+                    _ => (TyKind::Slice, self.err_ty()),
                 };
 
                 for pat_id in prefix.iter().chain(suffix) {
                     self.infer_pat(*pat_id, &elem_ty, default_bm);
                 }
 
-                let pat_ty = match expected.kind(&Interner) {
-                    TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
-                    _ => TyKind::Slice(elem_ty),
-                }
-                .intern(&Interner);
+                let pat_ty = container_ty(elem_ty).intern(&Interner);
                 if let Some(slice_pat_id) = slice {
                     self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
                 }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index f8955aa324..14f7051735 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -10,10 +10,7 @@ use hir_def::{
 };
 use hir_expand::name::Name;
 
-use crate::{
-    method_resolution, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
-    ValueTyDefId,
-};
+use crate::{method_resolution, Interner, Substitution, Ty, TyBuilder, TyKind, ValueTyDefId};
 
 use super::{ExprOrPatId, InferenceContext, TraitRef};
 
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 2ea9dd9209..c90a16720d 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -7,9 +7,8 @@ use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
 
 use super::{DomainGoal, InferenceContext};
 use crate::{
-    AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSubst,
-    InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyExt, TyKind, TypeWalk,
-    WhereClause,
+    AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer,
+    InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk, WhereClause,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -52,7 +51,7 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
         t.fold_binders(
             &mut |ty, binders| match ty.kind(&Interner) {
                 &TyKind::InferenceVar(var, kind) => {
-                    let inner = from_inference_var(var);
+                    let inner = var.to_inner();
                     if self.var_stack.contains(&inner) {
                         // recursive type
                         return self.ctx.table.type_variable_table.fallback_value(var, kind);
@@ -66,7 +65,7 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
                         result
                     } else {
                         let root = self.ctx.table.var_unification_table.find(inner);
-                        let position = self.add(to_inference_var(root), kind);
+                        let position = self.add(InferenceVar::from_inner(root), kind);
                         TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner)
                     }
                 }
@@ -208,16 +207,16 @@ impl TypeVariableTable {
     }
 
     pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
-        self.inner[from_inference_var(iv).0 as usize].diverging = diverging;
+        self.inner[iv.to_inner().0 as usize].diverging = diverging;
     }
 
     fn is_diverging(&mut self, iv: InferenceVar) -> bool {
-        self.inner[from_inference_var(iv).0 as usize].diverging
+        self.inner[iv.to_inner().0 as usize].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.inner[iv.to_inner().0 as usize].diverging => TyKind::Never,
             TyVariableKind::General => TyKind::Error,
             TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
             TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
@@ -251,7 +250,7 @@ impl InferenceTable {
         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)
+        TyKind::InferenceVar(InferenceVar::from_inner(key), kind).intern(&Interner)
     }
 
     pub(crate) fn new_type_var(&mut self) -> Ty {
@@ -309,8 +308,8 @@ impl InferenceTable {
                 (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::Function(FnPointer { substs: substs1, .. }),
+                    TyKind::Function(FnPointer { substs: substs2, .. }),
                 )
                 | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2))
                 | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2))
@@ -318,11 +317,9 @@ impl InferenceTable {
                 | (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::Ref(_, ty1), TyKind::Ref(_, ty2))
                 | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2))
+                | (TyKind::Array(ty1), TyKind::Array(ty2))
                 | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1),
                 _ => true, /* we checked equals_ctor already */
             }
@@ -370,12 +367,8 @@ impl InferenceTable {
                 == 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));
+                if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) {
+                    self.var_unification_table.union(tv1.to_inner(), tv2.to_inner());
                     self.revision += 1;
                 }
                 true
@@ -412,7 +405,7 @@ impl InferenceTable {
             ) => {
                 // the type var is unknown since we tried to resolve it
                 self.var_unification_table.union_value(
-                    from_inference_var(*tv),
+                    tv.to_inner(),
                     TypeVarValue::Known(other.clone().intern(&Interner)),
                 );
                 self.revision += 1;
@@ -467,7 +460,7 @@ impl InferenceTable {
             }
             match ty.kind(&Interner) {
                 TyKind::InferenceVar(tv, _) => {
-                    let inner = from_inference_var(*tv);
+                    let inner = tv.to_inner();
                     match self.var_unification_table.inlined_probe_value(inner).known() {
                         Some(known_ty) => {
                             // The known_ty can't be a type var itself
@@ -490,7 +483,7 @@ impl InferenceTable {
     fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty.kind(&Interner) {
             &TyKind::InferenceVar(tv, kind) => {
-                let inner = from_inference_var(tv);
+                let inner = tv.to_inner();
                 if tv_stack.contains(&inner) {
                     cov_mark::hit!(type_var_cycles_resolve_as_possible);
                     // recursive type
@@ -517,7 +510,7 @@ impl InferenceTable {
     fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty.kind(&Interner) {
             &TyKind::InferenceVar(tv, kind) => {
-                let inner = from_inference_var(tv);
+                let inner = tv.to_inner();
                 if tv_stack.contains(&inner) {
                     cov_mark::hit!(type_var_cycles_resolve_completely);
                     // recursive type
@@ -560,14 +553,6 @@ impl UnifyKey for 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)]
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 87f10e9d5a..daf379ef87 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -30,24 +30,26 @@ mod test_db;
 
 use std::sync::Arc;
 
+use itertools::Itertools;
+use smallvec::SmallVec;
+
 use base_db::salsa;
-use chalk_ir::UintTy;
 use hir_def::{
-    expr::ExprId, type_ref::Rawness, ConstParamId, LifetimeParamId, TraitId, TypeAliasId,
-    TypeParamId,
+    expr::ExprId, type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup,
+    TraitId, TypeAliasId, TypeParamId,
 };
 
 use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
 
 pub use autoderef::autoderef;
 pub use builder::TyBuilder;
-pub use chalk_ext::*;
-pub use infer::{could_unify, InferenceResult};
+pub use chalk_ext::TyExt;
+pub use infer::{could_unify, InferenceResult, InferenceVar};
 pub use lower::{
     associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
     TyDefId, TyLoweringContext, ValueTyDefId,
 };
-pub use traits::{chalk::Interner, TraitEnvironment};
+pub use traits::TraitEnvironment;
 pub use types::*;
 pub use walk::TypeWalk;
 
@@ -55,6 +57,8 @@ pub use chalk_ir::{
     cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
 };
 
+pub use crate::traits::chalk::Interner;
+
 pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
 pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
 pub type FnDefId = chalk_ir::FnDefId<Interner>;
@@ -66,22 +70,48 @@ pub type VariableKind = chalk_ir::VariableKind<Interner>;
 pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
 pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
 
-pub type Lifetime = chalk_ir::Lifetime<Interner>;
-pub type LifetimeData = chalk_ir::LifetimeData<Interner>;
-pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>;
+pub type ChalkTraitId = chalk_ir::TraitId<Interner>;
 
-pub type Const = chalk_ir::Const<Interner>;
-pub type ConstData = chalk_ir::ConstData<Interner>;
-pub type ConstValue = chalk_ir::ConstValue<Interner>;
-pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>;
+impl ProjectionTy {
+    pub fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
+        TraitRef {
+            trait_id: to_chalk_trait_id(self.trait_(db)),
+            substitution: self.substitution.clone(),
+        }
+    }
 
-pub type ChalkTraitId = chalk_ir::TraitId<Interner>;
+    pub fn self_type_parameter(&self, interner: &Interner) -> &Ty {
+        &self.substitution.interned()[0].assert_ty_ref(interner)
+    }
+
+    fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
+        match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
+            AssocContainerId::TraitId(it) => it,
+            _ => panic!("projection ty without parent trait"),
+        }
+    }
+}
 
 pub type FnSig = chalk_ir::FnSig<Interner>;
 
-// FIXME: get rid of this
-pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution {
-    Substitution::intern(s.interned()[..std::cmp::min(s.len(&Interner), n)].into())
+impl Substitution {
+    pub fn single(ty: Ty) -> Substitution {
+        Substitution::intern({
+            let mut v = SmallVec::new();
+            v.push(ty.cast(&Interner));
+            v
+        })
+    }
+
+    pub fn prefix(&self, n: usize) -> Substitution {
+        Substitution::intern(self.interned()[..std::cmp::min(self.len(&Interner), n)].into())
+    }
+
+    pub fn suffix(&self, n: usize) -> Substitution {
+        Substitution::intern(
+            self.interned()[self.len(&Interner) - std::cmp::min(self.len(&Interner), n)..].into(),
+        )
+    }
 }
 
 /// Return an index of a parameter in the generic type parameter list by it's id.
@@ -89,11 +119,22 @@ pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option<usize> {
     generics(db.upcast(), id.parent).param_idx(id)
 }
 
-pub fn wrap_empty_binders<T>(value: T) -> Binders<T>
-where
-    T: TypeWalk,
-{
-    Binders::empty(&Interner, value.shifted_in_from(DebruijnIndex::ONE))
+impl<T> Binders<T> {
+    pub fn wrap_empty(value: T) -> Self
+    where
+        T: TypeWalk,
+    {
+        Binders::empty(&Interner, value.shifted_in_from(DebruijnIndex::ONE))
+    }
+}
+
+impl<T: TypeWalk> Binders<T> {
+    /// Substitutes all variables.
+    pub fn substitute(self, interner: &Interner, subst: &Substitution) -> T {
+        let (value, binders) = self.into_value_and_skipped_binders();
+        assert_eq!(subst.len(interner), binders.len(interner));
+        value.subst_bound_vars(subst)
+    }
 }
 
 pub fn make_only_type_binders<T>(num_vars: usize, value: T) -> Binders<T> {
@@ -107,18 +148,42 @@ pub fn make_only_type_binders<T>(num_vars: usize, value: T) -> Binders<T> {
     )
 }
 
-// FIXME: get rid of this
-pub fn make_canonical<T>(
-    value: T,
-    kinds: impl IntoIterator<Item = TyVariableKind>,
-) -> Canonical<T> {
-    let kinds = kinds.into_iter().map(|tk| {
-        chalk_ir::CanonicalVarKind::new(
-            chalk_ir::VariableKind::Ty(tk),
-            chalk_ir::UniverseIndex::ROOT,
-        )
-    });
-    Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
+impl TraitRef {
+    pub fn self_type_parameter(&self, interner: &Interner) -> &Ty {
+        &self.substitution.at(interner, 0).assert_ty_ref(interner)
+    }
+
+    pub fn hir_trait_id(&self) -> TraitId {
+        from_chalk_trait_id(self.trait_id)
+    }
+}
+
+impl WhereClause {
+    pub fn is_implemented(&self) -> bool {
+        matches!(self, WhereClause::Implemented(_))
+    }
+
+    pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> {
+        match self {
+            WhereClause::Implemented(tr) => Some(tr.clone()),
+            WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => {
+                Some(proj.trait_ref(db))
+            }
+            WhereClause::AliasEq(_) => None,
+        }
+    }
+}
+
+impl<T> Canonical<T> {
+    pub fn new(value: T, kinds: impl IntoIterator<Item = TyVariableKind>) -> Self {
+        let kinds = kinds.into_iter().map(|tk| {
+            chalk_ir::CanonicalVarKind::new(
+                chalk_ir::VariableKind::Ty(tk),
+                chalk_ir::UniverseIndex::ROOT,
+            )
+        });
+        Self { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
+    }
 }
 
 /// A function signature as seen by type inference: Several parameter types and
@@ -142,11 +207,10 @@ impl CallableSig {
         CallableSig {
             // FIXME: what to do about lifetime params? -> return PolyFnSig
             params_and_return: fn_ptr
-                .substitution
+                .substs
                 .clone()
                 .shifted_out_to(DebruijnIndex::ONE)
                 .expect("unexpected lifetime vars in fn ptr")
-                .0
                 .interned()
                 .iter()
                 .map(|arg| arg.assert_ty_ref(&Interner).clone())
@@ -164,7 +228,277 @@ impl CallableSig {
     }
 }
 
-impl Ty {}
+impl Ty {
+    pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
+        match self.kind(&Interner) {
+            TyKind::Ref(mutability, ty) => Some((ty, *mutability)),
+            _ => None,
+        }
+    }
+
+    pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
+        match self.kind(&Interner) {
+            TyKind::Ref(mutability, ty) => Some((ty, Rawness::Ref, *mutability)),
+            TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
+            _ => None,
+        }
+    }
+
+    pub fn strip_references(&self) -> &Ty {
+        let mut t: &Ty = self;
+
+        while let TyKind::Ref(_mutability, ty) = t.kind(&Interner) {
+            t = ty;
+        }
+
+        t
+    }
+
+    pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
+        match self.kind(&Interner) {
+            TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
+            _ => None,
+        }
+    }
+
+    pub fn as_tuple(&self) -> Option<&Substitution> {
+        match self.kind(&Interner) {
+            TyKind::Tuple(_, substs) => Some(substs),
+            _ => None,
+        }
+    }
+
+    pub fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
+        match *self.kind(&Interner) {
+            TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
+            TyKind::FnDef(callable, ..) => {
+                Some(db.lookup_intern_callable_def(callable.into()).into())
+            }
+            TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
+            TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
+            _ => None,
+        }
+    }
+
+    pub fn is_never(&self) -> bool {
+        matches!(self.kind(&Interner), TyKind::Never)
+    }
+
+    pub fn is_unknown(&self) -> bool {
+        matches!(self.kind(&Interner), TyKind::Error)
+    }
+
+    pub fn equals_ctor(&self, other: &Ty) -> bool {
+        match (self.kind(&Interner), other.kind(&Interner)) {
+            (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
+            (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_), TyKind::Array(_)) => true,
+            (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
+            (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
+            (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
+                ty_id == ty_id2
+            }
+            (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
+            (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
+            (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
+            | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
+                mutability == mutability2
+            }
+            (
+                TyKind::Function(FnPointer { num_args, sig, .. }),
+                TyKind::Function(FnPointer { num_args: num_args2, sig: sig2, .. }),
+            ) => num_args == num_args2 && sig == sig2,
+            (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
+                cardinality == cardinality2
+            }
+            (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
+            (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
+            _ => false,
+        }
+    }
+
+    /// If this is a `dyn Trait` type, this returns the `Trait` part.
+    fn dyn_trait_ref(&self) -> Option<&TraitRef> {
+        match self.kind(&Interner) {
+            TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
+                match b.skip_binders() {
+                    WhereClause::Implemented(trait_ref) => Some(trait_ref),
+                    _ => None,
+                }
+            }),
+            _ => None,
+        }
+    }
+
+    /// If this is a `dyn Trait`, returns that trait.
+    pub fn dyn_trait(&self) -> Option<TraitId> {
+        self.dyn_trait_ref().map(|it| it.trait_id).map(from_chalk_trait_id)
+    }
+
+    fn builtin_deref(&self) -> Option<Ty> {
+        match self.kind(&Interner) {
+            TyKind::Ref(.., ty) => Some(ty.clone()),
+            TyKind::Raw(.., ty) => Some(ty.clone()),
+            _ => None,
+        }
+    }
+
+    pub fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
+        match self.kind(&Interner) {
+            &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
+            _ => None,
+        }
+    }
+
+    pub fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
+        if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
+            Some(func)
+        } else {
+            None
+        }
+    }
+
+    pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
+        match self.kind(&Interner) {
+            TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
+            TyKind::FnDef(def, parameters) => {
+                let callable_def = db.lookup_intern_callable_def((*def).into());
+                let sig = db.callable_item_signature(callable_def);
+                Some(sig.substitute(&Interner, &parameters))
+            }
+            TyKind::Closure(.., substs) => {
+                let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
+                sig_param.callable_sig(db)
+            }
+            _ => None,
+        }
+    }
+
+    /// Returns the type parameters of this type if it has some (i.e. is an ADT
+    /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
+    pub fn substs(&self) -> Option<&Substitution> {
+        match self.kind(&Interner) {
+            TyKind::Adt(_, substs)
+            | TyKind::FnDef(_, substs)
+            | TyKind::Function(FnPointer { substs, .. })
+            | TyKind::Tuple(_, substs)
+            | TyKind::OpaqueType(_, substs)
+            | TyKind::AssociatedType(_, substs)
+            | TyKind::Closure(.., substs) => Some(substs),
+            _ => None,
+        }
+    }
+
+    fn substs_mut(&mut self) -> Option<&mut Substitution> {
+        match self.interned_mut() {
+            TyKind::Adt(_, substs)
+            | TyKind::FnDef(_, substs)
+            | TyKind::Function(FnPointer { substs, .. })
+            | TyKind::Tuple(_, substs)
+            | TyKind::OpaqueType(_, substs)
+            | TyKind::AssociatedType(_, substs)
+            | TyKind::Closure(.., substs) => Some(substs),
+            _ => None,
+        }
+    }
+
+    pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
+        match self.kind(&Interner) {
+            TyKind::OpaqueType(opaque_ty_id, ..) => {
+                match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
+                    ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
+                        let krate = def.module(db.upcast()).krate();
+                        if let Some(future_trait) = db
+                            .lang_item(krate, "future_trait".into())
+                            .and_then(|item| item.as_trait())
+                        {
+                            // This is only used by type walking.
+                            // Parameters will be walked outside, and projection predicate is not used.
+                            // So just provide the Future trait.
+                            let impl_bound = Binders::empty(
+                                &Interner,
+                                WhereClause::Implemented(TraitRef {
+                                    trait_id: to_chalk_trait_id(future_trait),
+                                    substitution: Substitution::empty(&Interner),
+                                }),
+                            );
+                            Some(vec![impl_bound])
+                        } else {
+                            None
+                        }
+                    }
+                    ImplTraitId::ReturnTypeImplTrait(..) => None,
+                }
+            }
+            TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
+                let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
+                {
+                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+                        db.return_type_impl_traits(func).map(|it| {
+                            let data = (*it)
+                                .as_ref()
+                                .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
+                            data.substitute(&Interner, &opaque_ty.substitution)
+                        })
+                    }
+                    // It always has an parameter for Future::Output type.
+                    ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
+                };
+
+                predicates.map(|it| it.into_value_and_skipped_binders().0)
+            }
+            TyKind::Placeholder(idx) => {
+                let id = from_placeholder_idx(db, *idx);
+                let generic_params = db.generic_params(id.parent);
+                let param_data = &generic_params.types[id.local_id];
+                match param_data.provenance {
+                    hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
+                        let substs = TyBuilder::type_params_subst(db, id.parent);
+                        let predicates = db
+                            .generic_predicates(id.parent)
+                            .into_iter()
+                            .map(|pred| pred.clone().substitute(&Interner, &substs))
+                            .filter(|wc| match &wc.skip_binders() {
+                                WhereClause::Implemented(tr) => {
+                                    tr.self_type_parameter(&Interner) == self
+                                }
+                                WhereClause::AliasEq(AliasEq {
+                                    alias: AliasTy::Projection(proj),
+                                    ty: _,
+                                }) => proj.self_type_parameter(&Interner) == self,
+                                _ => false,
+                            })
+                            .collect_vec();
+
+                        Some(predicates)
+                    }
+                    _ => None,
+                }
+            }
+            _ => None,
+        }
+    }
+
+    pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
+        match self.kind(&Interner) {
+            TyKind::AssociatedType(id, ..) => {
+                match from_assoc_type_id(*id).lookup(db.upcast()).container {
+                    AssocContainerId::TraitId(trait_id) => Some(trait_id),
+                    _ => None,
+                }
+            }
+            TyKind::Alias(AliasTy::Projection(projection_ty)) => {
+                match from_assoc_type_id(projection_ty.associated_ty_id)
+                    .lookup(db.upcast())
+                    .container
+                {
+                    AssocContainerId::TraitId(trait_id) => Some(trait_id),
+                    _ => None,
+                }
+            }
+            _ => None,
+        }
+    }
+}
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub enum ImplTraitId {
@@ -212,18 +546,6 @@ pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeParamId) -> PlaceholderI
     }
 }
 
-pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId {
-    assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
-    let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
-    db.lookup_intern_lifetime_param_id(interned_id)
-}
-
-pub fn const_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> ConstParamId {
-    assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
-    let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
-    db.lookup_intern_const_param_id(interned_id)
-}
-
 pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
     chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id))
 }
@@ -231,16 +553,3 @@ pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
 pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId {
     salsa::InternKey::from_intern_id(id.0)
 }
-
-pub fn static_lifetime() -> Lifetime {
-    LifetimeData::Static.intern(&Interner)
-}
-
-pub fn dummy_usize_const() -> Const {
-    let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
-    chalk_ir::ConstData {
-        ty: usize_ty,
-        value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }),
-    }
-    .intern(&Interner)
-}
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index e6903e189e..48c26f471f 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -27,15 +27,16 @@ use stdx::impl_from;
 
 use crate::{
     db::HirDatabase,
-    dummy_usize_const, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
+    to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
     traits::chalk::{Interner, ToChalk},
     utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
+        all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
+        variant_data, Generics,
     },
     AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig,
-    FnSubst, ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
-    QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution,
-    TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, TypeWalk, WhereClause,
+    ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses,
+    ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, Ty,
+    TyBuilder, TyKind, TypeWalk, WhereClause,
 };
 
 #[derive(Debug)]
@@ -165,7 +166,7 @@ impl<'a> TyLoweringContext<'a> {
             }
             TypeRef::Array(inner) => {
                 let inner_ty = self.lower_ty(inner);
-                TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner)
+                TyKind::Array(inner_ty).intern(&Interner)
             }
             TypeRef::Slice(inner) => {
                 let inner_ty = self.lower_ty(inner);
@@ -173,19 +174,16 @@ impl<'a> TyLoweringContext<'a> {
             }
             TypeRef::Reference(inner, _, mutability) => {
                 let inner_ty = self.lower_ty(inner);
-                let lifetime = static_lifetime();
-                TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
-                    .intern(&Interner)
+                TyKind::Ref(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner)
             }
             TypeRef::Placeholder => TyKind::Error.intern(&Interner),
             TypeRef::Fn(params, is_varargs) => {
-                let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-                    Substitution::from_iter(&Interner, params.iter().map(|tr| ctx.lower_ty(tr)))
-                });
+                let substs =
+                    Substitution::from_iter(&Interner, params.iter().map(|tr| self.lower_ty(tr)));
                 TyKind::Function(FnPointer {
-                    num_binders: 0, // FIXME lower `for<'a> fn()` correctly
+                    num_args: substs.len(&Interner) - 1,
                     sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
-                    substitution: FnSubst(substs),
+                    substs,
                 })
                 .intern(&Interner)
             }
@@ -199,7 +197,7 @@ impl<'a> TyLoweringContext<'a> {
                     )
                 });
                 let bounds = crate::make_only_type_binders(1, bounds);
-                TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(&Interner)
+                TyKind::Dyn(DynTy { bounds }).intern(&Interner)
             }
             TypeRef::ImplTrait(bounds) => {
                 match self.impl_trait_mode {
@@ -386,12 +384,9 @@ impl<'a> TyLoweringContext<'a> {
                             1,
                             QuantifiedWhereClauses::from_iter(
                                 &Interner,
-                                Some(crate::wrap_empty_binders(WhereClause::Implemented(
-                                    trait_ref,
-                                ))),
+                                Some(Binders::wrap_empty(WhereClause::Implemented(trait_ref))),
                             ),
                         ),
-                        lifetime: static_lifetime(),
                     };
                     TyKind::Dyn(dyn_ty).intern(&Interner)
                 };
@@ -725,7 +720,7 @@ impl<'a> TyLoweringContext<'a> {
         let trait_ref = match bound {
             TypeBound::Path(path) => {
                 bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
-                bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b))
+                bindings.clone().map(WhereClause::Implemented).map(|b| Binders::wrap_empty(b))
             }
             TypeBound::Lifetime(_) => None,
             TypeBound::Error => None,
@@ -772,7 +767,7 @@ impl<'a> TyLoweringContext<'a> {
                     let ty = self.lower_ty(type_ref);
                     let alias_eq =
                         AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                    preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                    preds.push(Binders::wrap_empty(WhereClause::AliasEq(alias_eq)));
                 }
                 for bound in &binding.bounds {
                     preds.extend(self.lower_type_bound(
@@ -878,7 +873,7 @@ pub(crate) fn field_types_query(
     db: &dyn HirDatabase,
     variant_id: VariantId,
 ) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
-    let var_data = variant_id.variant_data(db.upcast());
+    let var_data = variant_data(db.upcast(), variant_id);
     let (resolver, def): (_, GenericDefId) = match variant_id {
         VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
         VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()),
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 7e09a1539d..19a1fa793b 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -8,8 +8,8 @@ use arrayvec::ArrayVec;
 use base_db::CrateId;
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
-    lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
-    GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
+    lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule,
+    ImplId, Lookup, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -19,11 +19,10 @@ use crate::{
     db::HirDatabase,
     from_foreign_def_id,
     primitive::{self, FloatTy, IntTy, UintTy},
-    static_lifetime,
     utils::all_super_traits,
     AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSig, ForeignDefId,
-    InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder,
-    TyExt, TyKind, TypeWalk,
+    InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyBuilder, TyKind,
+    TypeWalk,
 };
 
 /// This is used as a key for indexing impls.
@@ -47,18 +46,18 @@ impl TyFingerprint {
     /// have impls: if we have some `struct S`, we can have an `impl S`, but not
     /// `impl &S`. Hence, this will return `None` for reference types and such.
     pub fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
-        let fp = match ty.kind(&Interner) {
+        let fp = match *ty.kind(&Interner) {
             TyKind::Str => TyFingerprint::Str,
             TyKind::Never => TyFingerprint::Never,
             TyKind::Slice(..) => TyFingerprint::Slice,
             TyKind::Array(..) => TyFingerprint::Array,
-            TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar),
-            TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
-            TyKind::Tuple(cardinality, _) => TyFingerprint::Tuple(*cardinality),
-            TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
-            TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
-            TyKind::Function(FnPointer { sig, substitution: substs, .. }) => {
-                TyFingerprint::FnPtr(substs.0.len(&Interner) - 1, *sig)
+            TyKind::Scalar(scalar) => TyFingerprint::Scalar(scalar),
+            TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(adt),
+            TyKind::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality),
+            TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability),
+            TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(alias_id),
+            TyKind::Function(FnPointer { num_args, sig, .. }) => {
+                TyFingerprint::FnPtr(num_args, sig)
             }
             TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?,
             _ => return None,
@@ -100,38 +99,25 @@ impl TraitImpls {
         let mut impls = Self { map: FxHashMap::default() };
 
         let crate_def_map = db.crate_def_map(krate);
-        collect_def_map(db, &crate_def_map, &mut impls);
-
-        return Arc::new(impls);
-
-        fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) {
-            for (_module_id, module_data) in def_map.modules() {
-                for impl_id in module_data.scope.impls() {
-                    let target_trait = match db.impl_trait(impl_id) {
-                        Some(tr) => tr.skip_binders().hir_trait_id(),
-                        None => continue,
-                    };
-                    let self_ty = db.impl_self_ty(impl_id);
-                    let self_ty_fp = TyFingerprint::for_impl(self_ty.skip_binders());
-                    impls
-                        .map
-                        .entry(target_trait)
-                        .or_default()
-                        .entry(self_ty_fp)
-                        .or_default()
-                        .push(impl_id);
-                }
-
-                // To better support custom derives, collect impls in all unnamed const items.
-                // const _: () = { ... };
-                for konst in module_data.scope.unnamed_consts() {
-                    let body = db.body(konst.into());
-                    for (_, block_def_map) in body.blocks(db.upcast()) {
-                        collect_def_map(db, &block_def_map, impls);
-                    }
-                }
+        for (_module_id, module_data) in crate_def_map.modules() {
+            for impl_id in module_data.scope.impls() {
+                let target_trait = match db.impl_trait(impl_id) {
+                    Some(tr) => tr.skip_binders().hir_trait_id(),
+                    None => continue,
+                };
+                let self_ty = db.impl_self_ty(impl_id);
+                let self_ty_fp = TyFingerprint::for_impl(self_ty.skip_binders());
+                impls
+                    .map
+                    .entry(target_trait)
+                    .or_default()
+                    .entry(self_ty_fp)
+                    .or_default()
+                    .push(impl_id);
             }
         }
+
+        Arc::new(impls)
     }
 
     pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
@@ -221,9 +207,6 @@ impl InherentImpls {
             }
         }
 
-        // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only
-        // support trait impls there.
-
         Arc::new(Self { map })
     }
 
@@ -239,14 +222,15 @@ impl InherentImpls {
     }
 }
 
-pub fn def_crates(
-    db: &dyn HirDatabase,
-    ty: &Ty,
-    cur_crate: CrateId,
-) -> Option<ArrayVec<CrateId, 2>> {
-    // Types like slice can have inherent impls in several crates, (core and alloc).
-    // The corresponding impls are marked with lang items, so we can use them to find the required crates.
-    macro_rules! lang_item_crate {
+impl Ty {
+    pub fn def_crates(
+        &self,
+        db: &dyn HirDatabase,
+        cur_crate: CrateId,
+    ) -> Option<ArrayVec<CrateId, 2>> {
+        // Types like slice can have inherent impls in several crates, (core and alloc).
+        // The corresponding impls are marked with lang items, so we can use them to find the required crates.
+        macro_rules! lang_item_crate {
             ($($name:expr),+ $(,)?) => {{
                 let mut v = ArrayVec::<LangItemTarget, 2>::new();
                 $(
@@ -256,50 +240,51 @@ pub fn def_crates(
             }};
         }
 
-    let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect());
+        let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect());
 
-    let lang_item_targets = match ty.kind(&Interner) {
-        TyKind::Adt(AdtId(def_id), _) => {
-            return mod_to_crate_ids(def_id.module(db.upcast()));
-        }
-        TyKind::Foreign(id) => {
-            return mod_to_crate_ids(
-                from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()),
-            );
-        }
-        TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"),
-        TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"),
-        TyKind::Scalar(Scalar::Float(f)) => match f {
-            // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
-            FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"),
-            FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"),
-        },
-        &TyKind::Scalar(Scalar::Int(t)) => {
-            lang_item_crate!(primitive::int_ty_to_string(t))
-        }
-        &TyKind::Scalar(Scalar::Uint(t)) => {
-            lang_item_crate!(primitive::uint_ty_to_string(t))
-        }
-        TyKind::Str => lang_item_crate!("str_alloc", "str"),
-        TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"),
-        TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"),
-        TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"),
-        TyKind::Dyn(_) => {
-            return ty.dyn_trait().and_then(|trait_| {
-                mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))
-            });
-        }
-        _ => return None,
-    };
-    let res = lang_item_targets
-        .into_iter()
-        .filter_map(|it| match it {
-            LangItemTarget::ImplDefId(it) => Some(it),
-            _ => None,
-        })
-        .map(|it| it.lookup(db.upcast()).container.krate())
-        .collect();
-    Some(res)
+        let lang_item_targets = match self.kind(&Interner) {
+            TyKind::Adt(AdtId(def_id), _) => {
+                return mod_to_crate_ids(def_id.module(db.upcast()));
+            }
+            TyKind::Foreign(id) => {
+                return mod_to_crate_ids(
+                    from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()),
+                );
+            }
+            TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"),
+            TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"),
+            TyKind::Scalar(Scalar::Float(f)) => match f {
+                // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
+                FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"),
+                FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"),
+            },
+            &TyKind::Scalar(Scalar::Int(t)) => {
+                lang_item_crate!(primitive::int_ty_to_string(t))
+            }
+            &TyKind::Scalar(Scalar::Uint(t)) => {
+                lang_item_crate!(primitive::uint_ty_to_string(t))
+            }
+            TyKind::Str => lang_item_crate!("str_alloc", "str"),
+            TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"),
+            TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"),
+            TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"),
+            TyKind::Dyn(_) => {
+                return self.dyn_trait().and_then(|trait_| {
+                    mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))
+                });
+            }
+            _ => return None,
+        };
+        let res = lang_item_targets
+            .into_iter()
+            .filter_map(|it| match it {
+                LangItemTarget::ImplDefId(it) => Some(it),
+                _ => None,
+            })
+            .map(|it| it.lookup(db.upcast()).container.krate())
+            .collect();
+        Some(res)
+    }
 }
 
 /// Look up the method with the given name, returning the actual autoderefed
@@ -468,8 +453,7 @@ fn iterate_method_candidates_with_autoref(
     }
     let refed = Canonical {
         binders: deref_chain[0].binders.clone(),
-        value: TyKind::Ref(Mutability::Not, static_lifetime(), deref_chain[0].value.clone())
-            .intern(&Interner),
+        value: TyKind::Ref(Mutability::Not, deref_chain[0].value.clone()).intern(&Interner),
     };
     if iterate_method_candidates_by_receiver(
         &refed,
@@ -486,8 +470,7 @@ fn iterate_method_candidates_with_autoref(
     }
     let ref_muted = Canonical {
         binders: deref_chain[0].binders.clone(),
-        value: TyKind::Ref(Mutability::Mut, static_lifetime(), deref_chain[0].value.clone())
-            .intern(&Interner),
+        value: TyKind::Ref(Mutability::Mut, deref_chain[0].value.clone()).intern(&Interner),
     };
     if iterate_method_candidates_by_receiver(
         &ref_muted,
@@ -626,7 +609,7 @@ fn iterate_inherent_methods(
     visible_from_module: Option<ModuleId>,
     callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
 ) -> bool {
-    let def_crates = match def_crates(db, &self_ty.value, krate) {
+    let def_crates = match self_ty.value.def_crates(db, krate) {
         Some(k) => k,
         None => return false,
     };
@@ -726,9 +709,8 @@ pub(crate) fn inherent_impl_substs(
 ) -> Option<Substitution> {
     // we create a var for each type parameter of the impl; we need to keep in
     // mind here that `self_ty` might have vars of its own
-    let self_ty_vars = self_ty.binders.len(&Interner);
     let vars = TyBuilder::subst_for_def(db, impl_id)
-        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty_vars)
+        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.binders.len(&Interner))
         .build();
     let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(&Interner, &vars);
     let mut kinds = self_ty.binders.interned().to_vec();
@@ -743,15 +725,14 @@ 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(&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
     // Unknown, and in that case we want the result to contain Unknown in those
     // places again.
-    let suffix =
-        Substitution::from_iter(&Interner, substs.iter(&Interner).cloned().skip(self_ty_vars));
-    Some(fallback_bound_vars(suffix, self_ty_vars))
+    substs
+        .map(|s| fallback_bound_vars(s.suffix(vars.len(&Interner)), self_ty.binders.len(&Interner)))
 }
 
 /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
@@ -845,7 +826,7 @@ fn generic_implements_goal(
     let obligation = trait_ref.cast(&Interner);
     Canonical {
         binders: CanonicalVarKinds::from_iter(&Interner, kinds),
-        value: InEnvironment::new(&env.env, obligation),
+        value: InEnvironment::new(env.env.clone(), obligation),
     }
 }
 
@@ -856,9 +837,7 @@ fn autoderef_method_receiver(
 ) -> Vec<Canonical<Ty>> {
     let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
     // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
-    if let Some(TyKind::Array(parameters, _)) =
-        deref_chain.last().map(|ty| ty.value.kind(&Interner))
-    {
+    if let Some(TyKind::Array(parameters)) = deref_chain.last().map(|ty| ty.value.kind(&Interner)) {
         let kinds = deref_chain.last().unwrap().binders.clone();
         let unsized_ty = TyKind::Slice(parameters.clone()).intern(&Interner);
         deref_chain.push(Canonical { value: unsized_ty, binders: kinds })
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index 7d87741b8e..c8883485c0 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -9,7 +9,7 @@ use stdx::panic_context;
 
 use crate::{
     db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment,
-    Solution, TraitRefExt, Ty, TyKind, WhereClause,
+    Solution, SolutionVariables, Ty, TyKind, WhereClause,
 };
 
 use self::chalk::{from_chalk, Interner, ToChalk};
@@ -173,15 +173,23 @@ fn solution_from_chalk(
     db: &dyn HirDatabase,
     solution: chalk_solve::Solution<Interner>,
 ) -> Solution {
+    let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
+        let result = from_chalk(db, subst);
+        SolutionVariables(result)
+    };
     match solution {
         chalk_solve::Solution::Unique(constr_subst) => {
-            Solution::Unique(from_chalk(db, constr_subst))
+            let subst = chalk_ir::Canonical {
+                value: constr_subst.value.subst,
+                binders: constr_subst.binders,
+            };
+            Solution::Unique(convert_subst(subst))
         }
         chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
-            Solution::Ambig(Guidance::Definite(from_chalk(db, subst)))
+            Solution::Ambig(Guidance::Definite(convert_subst(subst)))
         }
         chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
-            Solution::Ambig(Guidance::Suggested(from_chalk(db, subst)))
+            Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
         }
         chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
             Solution::Ambig(Guidance::Unknown)
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 090f6492b4..dff87ef70e 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -22,7 +22,7 @@ use crate::{
     to_assoc_type_id, to_chalk_trait_id,
     utils::generics,
     AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, ProjectionTy, Substitution,
-    TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
+    TraitRef, Ty, TyBuilder, TyKind, WhereClause,
 };
 use mapping::{
     convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
@@ -220,8 +220,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                     let impl_bound = WhereClause::Implemented(TraitRef {
                         trait_id: to_chalk_trait_id(future_trait),
                         // Self type as the first parameter.
-                        substitution: Substitution::from1(
-                            &Interner,
+                        substitution: Substitution::single(
                             TyKind::BoundVar(BoundVar {
                                 debruijn: DebruijnIndex::INNERMOST,
                                 index: 0,
@@ -233,8 +232,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                         alias: AliasTy::Projection(ProjectionTy {
                             associated_ty_id: to_assoc_type_id(future_output),
                             // Self type as the first parameter.
-                            substitution: Substitution::from1(
-                                &Interner,
+                            substitution: Substitution::single(
                                 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
                                     .intern(&Interner),
                             ),
@@ -246,8 +244,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                     let bound = OpaqueTyDatumBound {
                         bounds: make_binders(
                             vec![
-                                crate::wrap_empty_binders(impl_bound).to_chalk(self.db),
-                                crate::wrap_empty_binders(proj_bound).to_chalk(self.db),
+                                wrap_in_empty_binders(impl_bound).to_chalk(self.db),
+                                wrap_in_empty_binders(proj_bound).to_chalk(self.db),
                             ],
                             1,
                         ),
@@ -723,3 +721,7 @@ impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> {
         chalk_ir::ClosureId(id.as_intern_id())
     }
 }
+
+fn wrap_in_empty_binders<T: crate::TypeWalk>(value: T) -> crate::Binders<T> {
+    crate::Binders::wrap_empty(value)
+}
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index 701359e6f5..c3b148cab7 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -3,16 +3,16 @@
 //! Chalk (in both directions); plus some helper functions for more specialized
 //! conversions.
 
-use chalk_ir::{cast::Cast, interner::HasInterner};
+use chalk_ir::{cast::Cast, fold::shift::Shift, interner::HasInterner, LifetimeData};
 use chalk_solve::rust_ir;
 
 use base_db::salsa::InternKey;
 use hir_def::{GenericDefId, TypeAliasId};
 
 use crate::{
-    db::HirDatabase, static_lifetime, AliasTy, CallableDefId, Canonical, ConstrainedSubst,
-    DomainGoal, FnPointer, GenericArg, InEnvironment, OpaqueTy, ProjectionTy, ProjectionTyExt,
-    QuantifiedWhereClause, Substitution, TraitRef, Ty, TypeWalk, WhereClause,
+    db::HirDatabase, primitive::UintTy, AliasTy, CallableDefId, Canonical, DomainGoal, FnPointer,
+    GenericArg, InEnvironment, OpaqueTy, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution,
+    TraitRef, Ty, TypeWalk, WhereClause,
 };
 
 use super::interner::*;
@@ -22,16 +22,16 @@ impl ToChalk for Ty {
     type Chalk = chalk_ir::Ty<Interner>;
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
         match self.into_inner() {
-            TyKind::Ref(m, lt, ty) => {
-                chalk_ir::TyKind::Ref(m, lt, ty.to_chalk(db)).intern(&Interner)
-            }
-            TyKind::Array(ty, size) => {
-                chalk_ir::TyKind::Array(ty.to_chalk(db), size).intern(&Interner)
-            }
-            TyKind::Function(FnPointer { sig, substitution: substs, num_binders }) => {
-                let substitution = chalk_ir::FnSubst(substs.0.to_chalk(db));
-                chalk_ir::TyKind::Function(chalk_ir::FnPointer { num_binders, sig, substitution })
-                    .intern(&Interner)
+            TyKind::Ref(m, ty) => ref_to_chalk(db, m, ty),
+            TyKind::Array(ty) => array_to_chalk(db, ty),
+            TyKind::Function(FnPointer { sig, substs, .. }) => {
+                let substitution = chalk_ir::FnSubst(substs.to_chalk(db).shifted_in(&Interner));
+                chalk_ir::TyKind::Function(chalk_ir::FnPointer {
+                    num_binders: 0,
+                    sig,
+                    substitution,
+                })
+                .intern(&Interner)
             }
             TyKind::AssociatedType(assoc_type_id, substs) => {
                 let substitution = substs.to_chalk(db);
@@ -73,12 +73,21 @@ impl ToChalk for Ty {
                 chalk_ir::TyKind::Adt(adt_id, substitution).intern(&Interner)
             }
             TyKind::Alias(AliasTy::Projection(proj_ty)) => {
-                chalk_ir::AliasTy::Projection(proj_ty.to_chalk(db))
-                    .cast(&Interner)
-                    .intern(&Interner)
+                let associated_ty_id = proj_ty.associated_ty_id;
+                let substitution = proj_ty.substitution.to_chalk(db);
+                chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
+                    associated_ty_id,
+                    substitution,
+                })
+                .cast(&Interner)
+                .intern(&Interner)
             }
             TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
-                chalk_ir::AliasTy::Opaque(opaque_ty.to_chalk(db)).cast(&Interner).intern(&Interner)
+                let opaque_ty_id = opaque_ty.opaque_ty_id;
+                let substitution = opaque_ty.substitution.to_chalk(db);
+                chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id, substitution })
+                    .cast(&Interner)
+                    .intern(&Interner)
             }
             TyKind::Placeholder(idx) => idx.to_ty::<Interner>(&Interner),
             TyKind::BoundVar(idx) => chalk_ir::TyKind::BoundVar(idx).intern(&Interner),
@@ -91,7 +100,7 @@ impl ToChalk for Ty {
                 );
                 let bounded_ty = chalk_ir::DynTy {
                     bounds: chalk_ir::Binders::new(binders, where_clauses),
-                    lifetime: dyn_ty.lifetime,
+                    lifetime: LifetimeData::Static.intern(&Interner),
                 };
                 chalk_ir::TyKind::Dyn(bounded_ty).intern(&Interner)
             }
@@ -101,13 +110,20 @@ impl ToChalk for Ty {
     fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
         match chalk.data(&Interner).kind.clone() {
             chalk_ir::TyKind::Error => TyKind::Error,
-            chalk_ir::TyKind::Array(ty, size) => TyKind::Array(from_chalk(db, ty), size),
+            chalk_ir::TyKind::Array(ty, _size) => TyKind::Array(from_chalk(db, ty)),
             chalk_ir::TyKind::Placeholder(idx) => TyKind::Placeholder(idx),
             chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Projection(proj)) => {
-                TyKind::Alias(AliasTy::Projection(from_chalk(db, proj)))
+                let associated_ty = proj.associated_ty_id;
+                let parameters = from_chalk(db, proj.substitution);
+                TyKind::Alias(AliasTy::Projection(ProjectionTy {
+                    associated_ty_id: associated_ty,
+                    substitution: parameters,
+                }))
             }
             chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
-                TyKind::Alias(AliasTy::Opaque(from_chalk(db, opaque_ty)))
+                let opaque_ty_id = opaque_ty.opaque_ty_id;
+                let parameters = from_chalk(db, opaque_ty.substitution);
+                TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, substitution: parameters }))
             }
             chalk_ir::TyKind::Function(chalk_ir::FnPointer {
                 num_binders,
@@ -116,25 +132,26 @@ impl ToChalk for Ty {
                 ..
             }) => {
                 assert_eq!(num_binders, 0);
-                let substs = crate::FnSubst(from_chalk(db, substitution.0));
-                TyKind::Function(FnPointer { num_binders, sig, substitution: substs })
+                let substs: Substitution = from_chalk(
+                    db,
+                    substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"),
+                );
+                TyKind::Function(FnPointer { num_args: (substs.len(&Interner) - 1), sig, substs })
             }
             chalk_ir::TyKind::BoundVar(idx) => TyKind::BoundVar(idx),
             chalk_ir::TyKind::InferenceVar(_iv, _kind) => TyKind::Error,
-            chalk_ir::TyKind::Dyn(dyn_ty) => {
-                assert_eq!(dyn_ty.bounds.binders.len(&Interner), 1);
-                let (bounds, binders) = dyn_ty.bounds.into_value_and_skipped_binders();
-                let where_clauses = crate::QuantifiedWhereClauses::from_iter(
-                    &Interner,
-                    bounds.interned().iter().cloned().map(|p| from_chalk(db, p)),
-                );
+            chalk_ir::TyKind::Dyn(where_clauses) => {
+                assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
+                let bounds = where_clauses
+                    .bounds
+                    .skip_binders()
+                    .iter(&Interner)
+                    .map(|c| from_chalk(db, c.clone()));
                 TyKind::Dyn(crate::DynTy {
-                    bounds: crate::Binders::new(binders, where_clauses),
-                    // HACK: we sometimes get lifetime variables back in solutions
-                    // from Chalk, and don't have the infrastructure to substitute
-                    // them yet. So for now we just turn them into 'static right
-                    // when we get them
-                    lifetime: static_lifetime(),
+                    bounds: crate::Binders::new(
+                        where_clauses.bounds.binders.clone(),
+                        crate::QuantifiedWhereClauses::from_iter(&Interner, bounds),
+                    ),
                 })
             }
 
@@ -154,11 +171,7 @@ impl ToChalk for Ty {
             chalk_ir::TyKind::Raw(mutability, ty) => TyKind::Raw(mutability, from_chalk(db, ty)),
             chalk_ir::TyKind::Slice(ty) => TyKind::Slice(from_chalk(db, ty)),
             chalk_ir::TyKind::Ref(mutability, _lifetime, ty) => {
-                // HACK: we sometimes get lifetime variables back in solutions
-                // from Chalk, and don't have the infrastructure to substitute
-                // them yet. So for now we just turn them into 'static right
-                // when we get them
-                TyKind::Ref(mutability, static_lifetime(), from_chalk(db, ty))
+                TyKind::Ref(mutability, from_chalk(db, ty))
             }
             chalk_ir::TyKind::Str => TyKind::Str,
             chalk_ir::TyKind::Never => TyKind::Never,
@@ -177,6 +190,31 @@ impl ToChalk for Ty {
     }
 }
 
+/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
+/// fake lifetime here, because Chalks built-in logic may expect it to be there.
+fn ref_to_chalk(
+    db: &dyn HirDatabase,
+    mutability: chalk_ir::Mutability,
+    ty: Ty,
+) -> chalk_ir::Ty<Interner> {
+    let arg = ty.to_chalk(db);
+    let lifetime = LifetimeData::Static.intern(&Interner);
+    chalk_ir::TyKind::Ref(mutability, lifetime, arg).intern(&Interner)
+}
+
+/// We currently don't model constants, but Chalk does. So, we have to insert a
+/// fake constant here, because Chalks built-in logic may expect it to be there.
+fn array_to_chalk(db: &dyn HirDatabase, ty: Ty) -> chalk_ir::Ty<Interner> {
+    let arg = ty.to_chalk(db);
+    let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
+    let const_ = chalk_ir::ConstData {
+        ty: usize_ty,
+        value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }),
+    }
+    .intern(&Interner);
+    chalk_ir::TyKind::Array(arg, const_).intern(&Interner)
+}
+
 impl ToChalk for GenericArg {
     type Chalk = chalk_ir::GenericArg<Interner>;
 
@@ -459,18 +497,6 @@ where
     }
 }
 
-impl ToChalk for crate::ConstrainedSubst {
-    type Chalk = chalk_ir::ConstrainedSubst<Interner>;
-
-    fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
-        unimplemented!()
-    }
-
-    fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
-        ConstrainedSubst { subst: from_chalk(db, chalk.subst) }
-    }
-}
-
 pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
 where
     T: HasInterner<Interner = Interner>,
@@ -509,7 +535,7 @@ pub(super) fn generic_predicate_to_inline_bound(
     let (pred, binders) = pred.as_ref().into_value_and_skipped_binders();
     match pred {
         WhereClause::Implemented(trait_ref) => {
-            if trait_ref.self_type_parameter(&Interner) != self_ty_shifted_in {
+            if trait_ref.self_type_parameter(&Interner) != &self_ty_shifted_in {
                 // we can only convert predicates back to type bounds if they
                 // have the expected self type
                 return None;
@@ -522,7 +548,7 @@ pub(super) fn generic_predicate_to_inline_bound(
             Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
         }
         WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
-            if projection_ty.self_type_parameter(&Interner) != self_ty_shifted_in {
+            if projection_ty.self_type_parameter(&Interner) != &self_ty_shifted_in {
                 return None;
             }
             let trait_ = projection_ty.trait_(db);
diff --git a/crates/hir_ty/src/types.rs b/crates/hir_ty/src/types.rs
index 89adad1084..46c705a76f 100644
--- a/crates/hir_ty/src/types.rs
+++ b/crates/hir_ty/src/types.rs
@@ -4,16 +4,23 @@
 use std::sync::Arc;
 
 use chalk_ir::{
-    cast::{Cast, CastTo, Caster},
+    cast::{CastTo, Caster},
     BoundVar, Mutability, Scalar, TyVariableKind,
 };
+use hir_def::LifetimeParamId;
 use smallvec::SmallVec;
 
 use crate::{
-    AssocTypeId, CanonicalVarKinds, ChalkTraitId, ClosureId, Const, FnDefId, FnSig, ForeignDefId,
-    Interner, Lifetime, OpaqueTyId, PlaceholderIndex, TypeWalk, VariableKind, VariableKinds,
+    AssocTypeId, CanonicalVarKinds, ChalkTraitId, ClosureId, FnDefId, FnSig, ForeignDefId,
+    InferenceVar, Interner, OpaqueTyId, PlaceholderIndex, TypeWalk, VariableKinds,
 };
 
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub enum Lifetime {
+    Parameter(LifetimeParamId),
+    Static,
+}
+
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct OpaqueTy {
     pub opaque_ty_id: OpaqueTyId,
@@ -29,51 +36,17 @@ pub struct ProjectionTy {
     pub substitution: Substitution,
 }
 
-impl ProjectionTy {
-    pub fn self_type_parameter(&self, interner: &Interner) -> Ty {
-        self.substitution.interned()[0].assert_ty_ref(interner).clone()
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct DynTy {
     /// The unknown self type.
     pub bounds: Binders<QuantifiedWhereClauses>,
-    pub lifetime: Lifetime,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct FnPointer {
-    pub num_binders: usize,
+    pub num_args: usize,
     pub sig: FnSig,
-    pub substitution: FnSubst,
-}
-/// A wrapper for the substs on a Fn.
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub struct FnSubst(pub Substitution);
-
-impl FnPointer {
-    /// Represent the current `Fn` as if it was wrapped in `Binders`
-    pub fn into_binders(self, interner: &Interner) -> Binders<FnSubst> {
-        Binders::new(
-            VariableKinds::from_iter(
-                interner,
-                (0..self.num_binders).map(|_| VariableKind::Lifetime),
-            ),
-            self.substitution,
-        )
-    }
-
-    /// Represent the current `Fn` as if it was wrapped in `Binders`
-    pub fn as_binders(&self, interner: &Interner) -> Binders<&FnSubst> {
-        Binders::new(
-            VariableKinds::from_iter(
-                interner,
-                (0..self.num_binders).map(|_| VariableKind::Lifetime),
-            ),
-            &self.substitution,
-        )
-    }
+    pub substs: Substitution,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -113,7 +86,7 @@ pub enum TyKind {
     Tuple(usize, Substitution),
 
     /// An array with the given length. Written as `[T; n]`.
-    Array(Ty, Const),
+    Array(Ty),
 
     /// The pointee of an array slice.  Written as `[T]`.
     Slice(Ty),
@@ -123,7 +96,7 @@ pub enum TyKind {
 
     /// A reference; a pointer with an associated lifetime. Written as
     /// `&'a mut T` or `&'a T`.
-    Ref(Mutability, Lifetime, Ty),
+    Ref(Mutability, Ty),
 
     /// This represents a placeholder for an opaque type in situations where we
     /// don't know the hidden type (i.e. currently almost always). This is
@@ -282,7 +255,7 @@ impl GenericArg {
 pub struct Substitution(SmallVec<[GenericArg; 2]>);
 
 impl Substitution {
-    pub fn interned(&self) -> &SmallVec<[GenericArg; 2]> {
+    pub fn interned(&self) -> &[GenericArg] {
         &self.0
     }
 
@@ -306,14 +279,6 @@ impl Substitution {
         self.0.iter()
     }
 
-    pub fn from1(_interner: &Interner, ty: Ty) -> Substitution {
-        Substitution::intern({
-            let mut v = SmallVec::new();
-            v.push(ty.cast(&Interner));
-            v
-        })
-    }
-
     pub fn from_iter(
         interner: &Interner,
         elements: impl IntoIterator<Item = impl CastTo<GenericArg>>,
@@ -388,15 +353,6 @@ impl<T: Clone> Binders<&T> {
     }
 }
 
-impl<T: TypeWalk> Binders<T> {
-    /// Substitutes all variables.
-    pub fn substitute(self, interner: &Interner, subst: &Substitution) -> T {
-        let (value, binders) = self.into_value_and_skipped_binders();
-        assert_eq!(subst.len(interner), binders.len(interner));
-        value.subst_bound_vars(subst)
-    }
-}
-
 impl<T: std::fmt::Debug> std::fmt::Debug for Binders<T> {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         let Binders { ref binders, ref value } = *self;
@@ -412,12 +368,6 @@ pub struct TraitRef {
     pub substitution: Substitution,
 }
 
-impl TraitRef {
-    pub fn self_type_parameter(&self, interner: &Interner) -> Ty {
-        self.substitution.at(interner, 0).assert_ty_ref(interner).clone()
-    }
-}
-
 /// Like `generics::WherePredicate`, but with resolved types: A condition on the
 /// parameters of a generic item.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -470,8 +420,8 @@ pub struct InEnvironment<T> {
 }
 
 impl<T> InEnvironment<T> {
-    pub fn new(environment: &chalk_ir::Environment<Interner>, value: T) -> InEnvironment<T> {
-        InEnvironment { environment: environment.clone(), goal: value }
+    pub fn new(environment: chalk_ir::Environment<Interner>, value: T) -> InEnvironment<T> {
+        InEnvironment { environment, goal: value }
     }
 }
 
@@ -490,16 +440,14 @@ pub struct AliasEq {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct ConstrainedSubst {
-    pub subst: Substitution,
-}
+pub struct SolutionVariables(pub Canonical<Substitution>);
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 /// A (possible) solution for a proposed goal.
 pub enum Solution {
     /// The goal indeed holds, and there is a unique value for all existential
     /// variables.
-    Unique(Canonical<ConstrainedSubst>),
+    Unique(SolutionVariables),
 
     /// The goal may be provable in multiple ways, but regardless we may have some guidance
     /// for type inference. In this case, we don't return any lifetime
@@ -515,35 +463,13 @@ pub enum Guidance {
     /// The existential variables *must* have the given values if the goal is
     /// ever to hold, but that alone isn't enough to guarantee the goal will
     /// actually hold.
-    Definite(Canonical<Substitution>),
+    Definite(SolutionVariables),
 
     /// There are multiple plausible values for the existentials, but the ones
     /// here are suggested as the preferred choice heuristically. These should
     /// be used for inference fallback only.
-    Suggested(Canonical<Substitution>),
+    Suggested(SolutionVariables),
 
     /// There's no useful information to feed back to type inference
     Unknown,
 }
-
-/// The kinds of placeholders we need during type inference. There's separate
-/// values for general types, and for integer and float variables. The latter
-/// two are used for inference of literal values (e.g. `100` could be one of
-/// several integer types).
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub struct InferenceVar {
-    index: u32,
-}
-
-impl From<u32> for InferenceVar {
-    fn from(index: u32) -> InferenceVar {
-        InferenceVar { index }
-    }
-}
-
-impl InferenceVar {
-    /// Gets the underlying index value.
-    pub fn index(self) -> u32 {
-        self.index
-    }
-}
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 8d5d5cd733..d11708299d 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
 
 use chalk_ir::{BoundVar, DebruijnIndex};
 use hir_def::{
+    adt::VariantData,
     db::DefDatabase,
     generics::{
         GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
@@ -12,13 +13,11 @@ use hir_def::{
     path::Path,
     resolver::{HasResolver, TypeNs},
     type_ref::TypeRef,
-    AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId,
+    AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId,
 };
 use hir_expand::name::{name, Name};
 
-use crate::{
-    db::HirDatabase, Interner, Substitution, TraitRef, TraitRefExt, TyKind, TypeWalk, WhereClause,
-};
+use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TyKind, TypeWalk, WhereClause};
 
 fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
     let resolver = trait_.resolver(db);
@@ -137,6 +136,16 @@ pub(super) fn associated_type_by_name_including_super_traits(
     })
 }
 
+pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> {
+    match var {
+        VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
+        VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
+        VariantId::EnumVariantId(it) => {
+            db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
+        }
+    }
+}
+
 /// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices).
 /// The underlying values are cloned if there are other strong references.
 pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] {
diff --git a/crates/hir_ty/src/walk.rs b/crates/hir_ty/src/walk.rs
index 91116dcda3..b85e6ab4d2 100644
--- a/crates/hir_ty/src/walk.rs
+++ b/crates/hir_ty/src/walk.rs
@@ -6,9 +6,8 @@ use std::mem;
 use chalk_ir::DebruijnIndex;
 
 use crate::{
-    utils::make_mut_slice, AliasEq, AliasTy, Binders, CallableSig, FnSubst, GenericArg,
-    GenericArgData, Interner, OpaqueTy, ProjectionTy, Substitution, TraitRef, Ty, TyKind,
-    WhereClause,
+    utils::make_mut_slice, AliasEq, AliasTy, Binders, CallableSig, GenericArg, GenericArgData,
+    Interner, OpaqueTy, ProjectionTy, Substitution, TraitRef, Ty, TyKind, WhereClause,
 };
 
 /// This allows walking structures that contain types to do something with those
@@ -92,13 +91,6 @@ pub trait TypeWalk {
         self
     }
 
-    fn shifted_in(self, _interner: &Interner) -> Self
-    where
-        Self: Sized,
-    {
-        self.shifted_in_from(DebruijnIndex::ONE)
-    }
-
     /// Shifts up debruijn indices of `TyKind::Bound` vars by `n`.
     fn shifted_in_from(self, n: DebruijnIndex) -> Self
     where
@@ -153,24 +145,16 @@ impl TypeWalk for Ty {
                     p.walk(f);
                 }
             }
-            TyKind::Slice(ty)
-            | TyKind::Array(ty, _)
-            | TyKind::Ref(_, _, ty)
-            | TyKind::Raw(_, ty) => {
+            TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => {
                 ty.walk(f);
             }
-            TyKind::Function(fn_pointer) => {
-                fn_pointer.substitution.0.walk(f);
-            }
-            TyKind::Adt(_, substs)
-            | TyKind::FnDef(_, substs)
-            | TyKind::Tuple(_, substs)
-            | TyKind::OpaqueType(_, substs)
-            | TyKind::AssociatedType(_, substs)
-            | TyKind::Closure(.., substs) => {
-                substs.walk(f);
+            _ => {
+                if let Some(substs) = self.substs() {
+                    for t in substs.iter(&Interner) {
+                        t.walk(f);
+                    }
+                }
             }
-            _ => {}
         }
         f(self);
     }
@@ -192,24 +176,14 @@ impl TypeWalk for Ty {
             TyKind::Alias(AliasTy::Opaque(o_ty)) => {
                 o_ty.substitution.walk_mut_binders(f, binders);
             }
-            TyKind::Slice(ty)
-            | TyKind::Array(ty, _)
-            | TyKind::Ref(_, _, ty)
-            | TyKind::Raw(_, ty) => {
+            TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => {
                 ty.walk_mut_binders(f, binders);
             }
-            TyKind::Function(fn_pointer) => {
-                fn_pointer.substitution.0.walk_mut_binders(f, binders.shifted_in());
-            }
-            TyKind::Adt(_, substs)
-            | TyKind::FnDef(_, substs)
-            | TyKind::Tuple(_, substs)
-            | TyKind::OpaqueType(_, substs)
-            | TyKind::AssociatedType(_, substs)
-            | TyKind::Closure(.., substs) => {
-                substs.walk_mut_binders(f, binders);
+            _ => {
+                if let Some(substs) = self.substs_mut() {
+                    substs.walk_mut_binders(f, binders);
+                }
             }
-            _ => {}
         }
         f(self, binders);
     }
@@ -407,17 +381,3 @@ impl TypeWalk for AliasEq {
         }
     }
 }
-
-impl TypeWalk for FnSubst {
-    fn walk(&self, f: &mut impl FnMut(&Ty)) {
-        self.0.walk(f)
-    }
-
-    fn walk_mut_binders(
-        &mut self,
-        f: &mut impl FnMut(&mut Ty, DebruijnIndex),
-        binders: DebruijnIndex,
-    ) {
-        self.0.walk_mut_binders(f, binders)
-    }
-}
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index d5628e3df1..9eeabbedaf 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -103,9 +103,6 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
                 format!("\n{}}}", "  ".repeat(indent))
             }
             R_CURLY => format!("}}\n{}", "  ".repeat(indent)),
-            LIFETIME_IDENT if is_next(|it| it == IDENT, true) => {
-                format!("{} ", token.text().to_string())
-            }
             T![;] => format!(";\n{}", "  ".repeat(indent)),
             T![->] => " -> ".to_string(),
             T![=] => " = ".to_string(),
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 2b9ed123c1..153726ce8b 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -19,7 +19,6 @@ pub enum FoldKind {
     Region,
     Consts,
     Statics,
-    Array,
 }
 
 #[derive(Debug)]
@@ -120,7 +119,6 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
     match kind {
         COMMENT => Some(FoldKind::Comment),
         ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
-        ARRAY_EXPR => Some(FoldKind::Array),
         ASSOC_ITEM_LIST
         | RECORD_FIELD_LIST
         | RECORD_PAT_FIELD_LIST
@@ -271,7 +269,6 @@ mod tests {
                 FoldKind::Region => "region",
                 FoldKind::Consts => "consts",
                 FoldKind::Statics => "statics",
-                FoldKind::Array => "array",
             };
             assert_eq!(kind, &attr.unwrap());
         }
@@ -467,20 +464,6 @@ fn foo<fold arglist>(
         )
     }
 
-    #[test]
-    fn fold_multiline_array() {
-        check(
-            r#"
-const FOO: [usize; 4] = <fold array>[
-    1,
-    2,
-    3,
-    4,
-]</fold>;
-"#,
-        )
-    }
-
     #[test]
     fn fold_region() {
         check(
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 1378048e51..11408d445a 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -22,19 +22,18 @@ use ide_db::{
 use syntax::{
     algo::find_node_at_offset,
     ast::{self, edit::IndentLevel, AstToken},
-    AstNode, Parse, SourceFile,
+    AstNode, SourceFile,
     SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
     TextRange, TextSize,
 };
 
-use text_edit::{Indel, TextEdit};
+use text_edit::TextEdit;
 
 use crate::SourceChange;
 
 pub(crate) use on_enter::on_enter;
 
-// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
-pub(crate) const TRIGGER_CHARS: &str = ".=>{";
+pub(crate) const TRIGGER_CHARS: &str = ".=>";
 
 // Feature: On Typing Assists
 //
@@ -42,7 +41,6 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>{";
 //
 // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
 // - typing `.` in a chain method call auto-indents
-// - typing `{` in front of an expression inserts a closing `}` after the expression
 //
 // VS Code::
 //
@@ -59,79 +57,28 @@ pub(crate) fn on_char_typed(
     position: FilePosition,
     char_typed: char,
 ) -> Option<SourceChange> {
-    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
-        return None;
-    }
-    let file = &db.parse(position.file_id);
-    if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
-        return None;
-    }
+    assert!(TRIGGER_CHARS.contains(char_typed));
+    let file = &db.parse(position.file_id).tree();
+    assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
     let edit = on_char_typed_inner(file, position.offset, char_typed)?;
     Some(SourceChange::from_text_edit(position.file_id, edit))
 }
 
-fn on_char_typed_inner(
-    file: &Parse<SourceFile>,
-    offset: TextSize,
-    char_typed: char,
-) -> Option<TextEdit> {
-    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
-        return None;
-    }
+fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
+    assert!(TRIGGER_CHARS.contains(char_typed));
     match char_typed {
-        '.' => on_dot_typed(&file.tree(), offset),
-        '=' => on_eq_typed(&file.tree(), offset),
-        '>' => on_arrow_typed(&file.tree(), offset),
-        '{' => on_opening_brace_typed(file, offset),
+        '.' => on_dot_typed(file, offset),
+        '=' => on_eq_typed(file, offset),
+        '>' => on_arrow_typed(file, offset),
         _ => unreachable!(),
     }
 }
 
-/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
-/// block.
-fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
-    if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
-        return None;
-    }
-
-    let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
-
-    // Remove the `{` to get a better parse tree, and reparse
-    let file = file.reparse(&Indel::delete(brace_token.text_range()));
-
-    let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?;
-    if expr.syntax().text_range().start() != offset {
-        return None;
-    }
-
-    // Enclose the outermost expression starting at `offset`
-    while let Some(parent) = expr.syntax().parent() {
-        if parent.text_range().start() != expr.syntax().text_range().start() {
-            break;
-        }
-
-        match ast::Expr::cast(parent) {
-            Some(parent) => expr = parent,
-            None => break,
-        }
-    }
-
-    // If it's a statement in a block, we don't know how many statements should be included
-    if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
-        return None;
-    }
-
-    // Insert `}` right after the expression.
-    Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string()))
-}
-
 /// Returns an edit which should be applied after `=` was typed. Primarily,
 /// this works when adding `let =`.
 // FIXME: use a snippet completion instead of this hack here.
 fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
-    if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
-        return None;
-    }
+    assert_eq!(file.syntax().text().char_at(offset), Some('='));
     let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
     if let_stmt.semicolon_token().is_some() {
         return None;
@@ -153,9 +100,7 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
 
 /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
 fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
-    if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
-        return None;
-    }
+    assert_eq!(file.syntax().text().char_at(offset), Some('.'));
     let whitespace =
         file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
 
@@ -184,9 +129,7 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
 /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
 fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
     let file_text = file.syntax().text();
-    if !stdx::always!(file_text.char_at(offset) == Some('>')) {
-        return None;
-    }
+    assert_eq!(file_text.char_at(offset), Some('>'));
     let after_arrow = offset + TextSize::of('>');
     if file_text.char_at(after_arrow) != Some('{') {
         return None;
@@ -209,7 +152,7 @@ mod tests {
         let edit = TextEdit::insert(offset, char_typed.to_string());
         edit.apply(&mut before);
         let parse = SourceFile::parse(&before);
-        on_char_typed_inner(&parse, offset, char_typed).map(|it| {
+        on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {
             it.apply(&mut before);
             before.to_string()
         })
@@ -430,85 +373,4 @@ fn main() {
     fn adds_space_after_return_type() {
         type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }")
     }
-
-    #[test]
-    fn adds_closing_brace() {
-        type_char(
-            '{',
-            r#"
-fn f() { match () { _ => $0() } }
-            "#,
-            r#"
-fn f() { match () { _ => {()} } }
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-fn f() { $0() }
-            "#,
-            r#"
-fn f() { {()} }
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-fn f() { let x = $0(); }
-            "#,
-            r#"
-fn f() { let x = {()}; }
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-fn f() { let x = $0a.b(); }
-            "#,
-            r#"
-fn f() { let x = {a.b()}; }
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-const S: () = $0();
-fn f() {}
-            "#,
-            r#"
-const S: () = {()};
-fn f() {}
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-const S: () = $0a.b();
-fn f() {}
-            "#,
-            r#"
-const S: () = {a.b()};
-fn f() {}
-            "#,
-        );
-        type_char(
-            '{',
-            r#"
-fn f() {
-    match x {
-        0 => $0(),
-        1 => (),
-    }
-}
-            "#,
-            r#"
-fn f() {
-    match x {
-        0 => {()},
-        1 => (),
-    }
-}
-            "#,
-        );
-    }
 }
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 5fdc8bf387..719f22053c 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -729,6 +729,14 @@ fn reference_is_exclusive(
 
 /// checks if this expr requires `&mut` access, recurses on field access
 fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> {
+    match expr {
+        ast::Expr::MacroCall(_) => {
+            // FIXME: expand macro and check output for mutable usages of the variable?
+            return None;
+        }
+        _ => (),
+    }
+
     let parent = expr.syntax().parent()?;
 
     if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
@@ -787,7 +795,7 @@ impl HasTokenAtOffset for SyntaxNode {
     }
 }
 
-/// find relevant `ast::PathExpr` for reference
+/// find relevant `ast::Expr` for reference
 ///
 /// # Preconditions
 ///
@@ -804,7 +812,11 @@ fn path_element_of_reference(
         stdx::never!(false, "cannot find path parent of variable usage: {:?}", token);
         None
     })?;
-    stdx::always!(matches!(path, ast::Expr::PathExpr(_)));
+    stdx::always!(
+        matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroCall(_)),
+        "unexpected expression type for variable usage: {:?}",
+        path
+    );
     Some(path)
 }
 
@@ -3372,4 +3384,36 @@ fn foo() -> Result<(), i64> {
 }"##,
         );
     }
+
+    #[test]
+    fn param_usage_in_macro() {
+        check_assist(
+            extract_function,
+            r"
+macro_rules! m {
+    ($val:expr) => { $val };
+}
+
+fn foo() {
+    let n = 1;
+    $0let k = n * m!(n);$0
+    let m = k + 1;
+}",
+            r"
+macro_rules! m {
+    ($val:expr) => { $val };
+}
+
+fn foo() {
+    let n = 1;
+    let k = fun_name(n);
+    let m = k + 1;
+}
+
+fn $0fun_name(n: i32) -> i32 {
+    let k = n * m!(n);
+    k
+}",
+        );
+    }
 }
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 9ace13e411..1ad0171982 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -113,7 +113,6 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     if ctx.use_item_syntax.is_some()
         || ctx.attribute_under_caret.is_some()
         || ctx.mod_declaration_under_caret.is_some()
-        || ctx.record_lit_syntax.is_some()
     {
         return None;
     }
@@ -1035,46 +1034,4 @@ fn main() {
             expect![[]],
         );
     }
-
-    #[test]
-    fn no_fuzzy_during_fields_of_record_lit_syntax() {
-        check(
-            r#"
-mod m {
-    pub fn some_fn() -> i32 {
-        42
-    }
-}
-struct Foo {
-    some_field: i32,
-}
-fn main() {
-    let _ = Foo { so$0 };
-}
-"#,
-            expect![[]],
-        );
-    }
-
-    #[test]
-    fn fuzzy_after_fields_of_record_lit_syntax() {
-        check(
-            r#"
-mod m {
-    pub fn some_fn() -> i32 {
-        42
-    }
-}
-struct Foo {
-    some_field: i32,
-}
-fn main() {
-    let _ = Foo { some_field: so$0 };
-}
-"#,
-            expect![[r#"
-                fn some_fn() (m::some_fn) fn() -> i32
-            "#]],
-        );
-    }
 }
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 6c67425d75..111e9325a4 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -152,10 +152,6 @@ impl RootDatabase {
             hir::db::FileItemTreeQuery
             hir::db::BlockDefMapQuery
             hir::db::CrateDefMapQueryQuery
-            hir::db::FieldsAttrsQuery
-            hir::db::VariantsAttrsQuery
-            hir::db::FieldsAttrsSourceMapQuery
-            hir::db::VariantsAttrsSourceMapQuery
             hir::db::StructDataQuery
             hir::db::UnionDataQuery
             hir::db::EnumDataQuery
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index f09ad37e33..22011cb33e 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -1,7 +1,6 @@
 //! Thin wrappers around `std::path`, distinguishing between absolute and
 //! relative paths.
 use std::{
-    borrow::Borrow,
     convert::{TryFrom, TryInto},
     ops,
     path::{Component, Path, PathBuf},
@@ -36,12 +35,6 @@ impl AsRef<AbsPath> for AbsPathBuf {
     }
 }
 
-impl Borrow<AbsPath> for AbsPathBuf {
-    fn borrow(&self) -> &AbsPath {
-        self.as_path()
-    }
-}
-
 impl TryFrom<PathBuf> for AbsPathBuf {
     type Error = PathBuf;
     fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index 0d4d39feff..f7050be4e7 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -13,12 +13,12 @@ use cargo_metadata::{BuildScript, Message};
 use itertools::Itertools;
 use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::FxHashMap;
-use stdx::{format_to, JodChild};
+use stdx::JodChild;
 
 use crate::{cfg_flag::CfgFlag, CargoConfig};
 
 #[derive(Debug, Clone, Default, PartialEq, Eq)]
-pub(crate) struct PackageBuildData {
+pub(crate) struct BuildData {
     /// List of config flags defined by this package's build script
     pub(crate) cfgs: Vec<CfgFlag>,
     /// List of cargo-related environment variables with their value
@@ -32,17 +32,6 @@ pub(crate) struct PackageBuildData {
     pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
 }
 
-#[derive(Debug, Default, PartialEq, Eq, Clone)]
-pub(crate) struct WorkspaceBuildData {
-    per_package: FxHashMap<String, PackageBuildData>,
-    error: Option<String>,
-}
-
-#[derive(Debug, Default, PartialEq, Eq, Clone)]
-pub struct BuildDataResult {
-    per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
-}
-
 #[derive(Clone, Debug)]
 pub(crate) struct BuildDataConfig {
     cargo_toml: AbsPathBuf,
@@ -63,6 +52,13 @@ pub struct BuildDataCollector {
     configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
 }
 
+#[derive(Debug, Default, PartialEq, Eq)]
+pub struct BuildDataResult {
+    data: FxHashMap<AbsPathBuf, BuildDataMap>,
+}
+
+pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
+
 impl BuildDataCollector {
     pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
         self.configs.insert(workspace_root.to_path_buf(), config);
@@ -71,7 +67,7 @@ impl BuildDataCollector {
     pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
         let mut res = BuildDataResult::default();
         for (path, config) in self.configs.iter() {
-            res.per_workspace.insert(
+            res.data.insert(
                 path.clone(),
                 collect_from_workspace(
                     &config.cargo_toml,
@@ -85,28 +81,9 @@ impl BuildDataCollector {
     }
 }
 
-impl WorkspaceBuildData {
-    pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
-        self.per_package.get(package_id)
-    }
-}
-
 impl BuildDataResult {
-    pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
-        self.per_workspace.get(workspace_root)
-    }
-    pub fn error(&self) -> Option<String> {
-        let mut buf = String::new();
-        for (_workspace_root, build_data) in &self.per_workspace {
-            if let Some(err) = &build_data.error {
-                format_to!(buf, "cargo check failed:\n{}", err);
-            }
-        }
-        if buf.is_empty() {
-            return None;
-        }
-
-        Some(buf)
+    pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> {
+        self.data.get(&root.to_path_buf())
     }
 }
 
@@ -125,7 +102,7 @@ fn collect_from_workspace(
     cargo_features: &CargoConfig,
     packages: &Vec<cargo_metadata::Package>,
     progress: &dyn Fn(String),
-) -> Result<WorkspaceBuildData> {
+) -> Result<BuildDataMap> {
     let mut cmd = Command::new(toolchain::cargo());
     cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
         .arg(cargo_toml.as_ref());
@@ -153,13 +130,13 @@ fn collect_from_workspace(
         }
     }
 
-    cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
+    cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
 
     let mut child = cmd.spawn().map(JodChild)?;
     let child_stdout = child.stdout.take().unwrap();
     let stdout = BufReader::new(child_stdout);
 
-    let mut res = WorkspaceBuildData::default();
+    let mut res = BuildDataMap::default();
     for message in cargo_metadata::Message::parse_stream(stdout).flatten() {
         match message {
             Message::BuildScriptExecuted(BuildScript {
@@ -177,17 +154,16 @@ fn collect_from_workspace(
                     }
                     acc
                 };
-                let package_build_data =
-                    res.per_package.entry(package_id.repr.clone()).or_default();
+                let res = res.entry(package_id.repr.clone()).or_default();
                 // cargo_metadata crate returns default (empty) path for
                 // older cargos, which is not absolute, so work around that.
                 if !out_dir.as_str().is_empty() {
                     let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
-                    package_build_data.out_dir = Some(out_dir);
-                    package_build_data.cfgs = cfgs;
+                    res.out_dir = Some(out_dir);
+                    res.cfgs = cfgs;
                 }
 
-                package_build_data.envs = env;
+                res.envs = env;
             }
             Message::CompilerArtifact(message) => {
                 progress(format!("metadata {}", message.target.name));
@@ -197,9 +173,8 @@ fn collect_from_workspace(
                     // Skip rmeta file
                     if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) {
                         let filename = AbsPathBuf::assert(PathBuf::from(&filename));
-                        let package_build_data =
-                            res.per_package.entry(package_id.repr.clone()).or_default();
-                        package_build_data.proc_macro_dylib_path = Some(filename);
+                        let res = res.entry(package_id.repr.clone()).or_default();
+                        res.proc_macro_dylib_path = Some(filename);
                     }
                 }
             }
@@ -213,25 +188,16 @@ fn collect_from_workspace(
     }
 
     for package in packages {
-        let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
-        inject_cargo_env(package, package_build_data);
-        if let Some(out_dir) = &package_build_data.out_dir {
+        let build_data = res.entry(package.id.repr.clone()).or_default();
+        inject_cargo_env(package, build_data);
+        if let Some(out_dir) = &build_data.out_dir {
             // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
             if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
-                package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
+                build_data.envs.push(("OUT_DIR".to_string(), out_dir));
             }
         }
     }
 
-    let output = child.into_inner().wait_with_output()?;
-    if !output.status.success() {
-        let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
-        if stderr.is_empty() {
-            stderr = "cargo check failed".to_string();
-        }
-        res.error = Some(stderr)
-    }
-
     Ok(res)
 }
 
@@ -246,7 +212,7 @@ fn is_dylib(path: &Utf8Path) -> bool {
 /// Recreates the compile-time environment variables that Cargo sets.
 ///
 /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
-fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
+fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) {
     let env = &mut build_data.envs;
 
     // FIXME: Missing variables:
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 2fcd0f8fac..1b53fcc30a 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient;
 use rustc_hash::{FxHashMap, FxHashSet};
 
 use crate::{
-    build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
+    build_data::{BuildData, BuildDataMap, BuildDataResult},
     cargo_workspace,
     cfg_flag::CfgFlag,
     rustc_cfg,
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph(
     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     cargo: &CargoWorkspace,
-    build_data_map: Option<&WorkspaceBuildData>,
+    build_data_map: Option<&BuildDataMap>,
     sysroot: &Sysroot,
     rustc: &Option<CargoWorkspace>,
-    rustc_build_data_map: Option<&WorkspaceBuildData>,
+    rustc_build_data_map: Option<&BuildDataMap>,
 ) -> CrateGraph {
     let _p = profile::span("cargo_to_crate_graph");
     let mut crate_graph = CrateGraph::default();
@@ -464,7 +464,7 @@ fn handle_rustc_crates(
     rustc_workspace: &CargoWorkspace,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     crate_graph: &mut CrateGraph,
-    rustc_build_data_map: Option<&WorkspaceBuildData>,
+    rustc_build_data_map: Option<&FxHashMap<String, BuildData>>,
     cfg_options: &CfgOptions,
     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
@@ -555,7 +555,7 @@ fn handle_rustc_crates(
 fn add_target_crate_root(
     crate_graph: &mut CrateGraph,
     pkg: &cargo_workspace::PackageData,
-    build_data: Option<&PackageBuildData>,
+    build_data: Option<&BuildData>,
     cfg_options: &CfgOptions,
     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
     file_id: FileId,
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 3c87782f28..7a5bcb8c7f 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -57,7 +57,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
         document_range_formatting_provider: None,
         document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
             first_trigger_character: "=".to_string(),
-            more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
+            more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
         }),
         selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
         folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index fe9f273b0a..18fd7ea749 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -12,7 +12,7 @@ use hir::{
     AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
 };
 use hir_def::FunctionId;
-use hir_ty::{TyExt, TypeWalk};
+use hir_ty::TypeWalk;
 use ide::{AnalysisHost, RootDatabase};
 use ide_db::base_db::{
     salsa::{self, ParallelDatabase},
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e012b4452f..cda272fd48 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -445,8 +445,8 @@ impl Config {
     pub fn hover_actions(&self) -> bool {
         self.experimental("hoverActions")
     }
-    pub fn server_status_notification(&self) -> bool {
-        self.experimental("serverStatusNotification")
+    pub fn status_notification(&self) -> bool {
+        self.experimental("statusNotification")
     }
 
     pub fn publish_diagnostics(&self) -> bool {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index adeb7a97e8..8679c85996 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -23,7 +23,6 @@ use crate::{
     document::DocumentData,
     from_proto,
     line_index::{LineEndings, LineIndex},
-    lsp_ext,
     main_loop::Task,
     op_queue::OpQueue,
     reload::SourceRootConfig,
@@ -33,6 +32,20 @@ use crate::{
     Result,
 };
 
+#[derive(Eq, PartialEq, Copy, Clone)]
+pub(crate) enum Status {
+    Loading,
+    Ready { partial: bool },
+    Invalid,
+    NeedsReload,
+}
+
+impl Default for Status {
+    fn default() -> Self {
+        Status::Loading
+    }
+}
+
 // Enforces drop order
 pub(crate) struct Handle<H, C> {
     pub(crate) handle: H,
@@ -54,36 +67,26 @@ pub(crate) struct GlobalState {
     req_queue: ReqQueue,
     pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
     pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
+    pub(crate) vfs_config_version: u32,
+    pub(crate) flycheck: Vec<FlycheckHandle>,
+    pub(crate) flycheck_sender: Sender<flycheck::Message>,
+    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
     pub(crate) config: Arc<Config>,
     pub(crate) analysis_host: AnalysisHost,
     pub(crate) diagnostics: DiagnosticCollection,
     pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
+    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
     pub(crate) shutdown_requested: bool,
-    pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
+    pub(crate) status: Status,
     pub(crate) source_root_config: SourceRootConfig,
     pub(crate) proc_macro_client: Option<ProcMacroClient>,
 
-    pub(crate) flycheck: Vec<FlycheckHandle>,
-    pub(crate) flycheck_sender: Sender<flycheck::Message>,
-    pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
-
-    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
-    pub(crate) vfs_config_version: u32,
-    pub(crate) vfs_progress_config_version: u32,
-    pub(crate) vfs_progress_n_total: usize,
-    pub(crate) vfs_progress_n_done: usize,
-
-    /// For both `workspaces` and `workspace_build_data`, the field stores the
-    /// data we actually use, while the `OpQueue` stores the result of the last
-    /// fetch.
-    ///
-    /// If the fetch (partially) fails, we do not update the values.
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
-    pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>,
+    pub(crate) fetch_workspaces_queue: OpQueue<(), ()>,
+
     pub(crate) workspace_build_data: Option<BuildDataResult>,
-    pub(crate) fetch_build_data_queue:
-        OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
+    pub(crate) fetch_build_data_queue: OpQueue<BuildDataCollector, ()>,
 
     latest_requests: Arc<RwLock<LatestRequests>>,
 }
@@ -121,32 +124,25 @@ impl GlobalState {
         GlobalState {
             sender,
             req_queue: ReqQueue::default(),
+            vfs_config_version: 0,
             task_pool,
             loader,
+            flycheck: Vec::new(),
+            flycheck_sender,
+            flycheck_receiver,
             config: Arc::new(config),
             analysis_host,
             diagnostics: Default::default(),
             mem_docs: FxHashMap::default(),
             semantic_tokens_cache: Arc::new(Default::default()),
+            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
             shutdown_requested: false,
-            last_reported_status: None,
+            status: Status::default(),
             source_root_config: SourceRootConfig::default(),
             proc_macro_client: None,
-
-            flycheck: Vec::new(),
-            flycheck_sender,
-            flycheck_receiver,
-
-            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
-            vfs_config_version: 0,
-            vfs_progress_config_version: 0,
-            vfs_progress_n_total: 0,
-            vfs_progress_n_done: 0,
-
             workspaces: Arc::new(Vec::new()),
             fetch_workspaces_queue: OpQueue::default(),
             workspace_build_data: None,
-
             fetch_build_data_queue: OpQueue::default(),
             latest_requests: Default::default(),
         }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 31d8c487be..4d10a2eadc 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -231,6 +231,7 @@ pub(crate) fn handle_on_enter(
     Ok(Some(edit))
 }
 
+// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
 pub(crate) fn handle_on_type_formatting(
     snap: GlobalStateSnapshot,
     params: lsp_types::DocumentOnTypeFormattingParams,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 81a6f22f16..0e1fec2095 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -241,26 +241,26 @@ pub struct SsrParams {
     pub selections: Vec<lsp_types::Range>,
 }
 
-pub enum ServerStatusNotification {}
+pub enum StatusNotification {}
 
-impl Notification for ServerStatusNotification {
-    type Params = ServerStatusParams;
-    const METHOD: &'static str = "experimental/serverStatus";
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum Status {
+    Loading,
+    ReadyPartial,
+    Ready,
+    NeedsReload,
+    Invalid,
 }
 
-#[derive(Deserialize, Serialize, PartialEq, Eq, Clone)]
-pub struct ServerStatusParams {
-    pub health: Health,
-    pub quiescent: bool,
-    pub message: Option<String>,
+#[derive(Deserialize, Serialize)]
+pub struct StatusParams {
+    pub status: Status,
 }
 
-#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
-#[serde(rename_all = "camelCase")]
-pub enum Health {
-    Ok,
-    Warning,
-    Error,
+impl Notification for StatusNotification {
+    type Params = StatusParams;
+    const METHOD: &'static str = "rust-analyzer/status";
 }
 
 pub enum CodeActionRequest {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 47c6c6d776..e88f16cc11 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -2,7 +2,6 @@
 //! requests/replies and notifications back to the client.
 use std::{
     env, fmt,
-    sync::Arc,
     time::{Duration, Instant},
 };
 
@@ -13,7 +12,6 @@ use ide::{Canceled, FileId};
 use ide_db::base_db::VfsPath;
 use lsp_server::{Connection, Notification, Request, Response};
 use lsp_types::notification::Notification as _;
-use project_model::BuildDataCollector;
 use vfs::ChangeKind;
 
 use crate::{
@@ -21,7 +19,7 @@ use crate::{
     dispatch::{NotificationDispatcher, RequestDispatcher},
     document::DocumentData,
     from_proto,
-    global_state::{file_id_to_url, url_to_file_id, GlobalState},
+    global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
     handlers, lsp_ext,
     lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
     reload::{BuildDataProgress, ProjectWorkspaceProgress},
@@ -189,7 +187,7 @@ impl GlobalState {
             log::info!("task queue len: {}", task_queue_len);
         }
 
-        let was_quiescent = self.is_quiescent();
+        let mut new_status = self.status;
         match event {
             Event::Lsp(msg) => match msg {
                 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -229,24 +227,11 @@ impl GlobalState {
                                     (Progress::Report, Some(msg))
                                 }
                                 ProjectWorkspaceProgress::End(workspaces) => {
-                                    self.fetch_workspaces_completed(workspaces);
-
-                                    let old = Arc::clone(&self.workspaces);
-                                    self.switch_workspaces();
-                                    let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
-
-                                    if self.config.run_build_scripts() && workspaces_updated {
-                                        let mut collector = BuildDataCollector::default();
-                                        for ws in self.workspaces.iter() {
-                                            ws.collect_build_data_configs(&mut collector);
-                                        }
-                                        self.fetch_build_data_request(collector)
-                                    }
-
+                                    self.fetch_workspaces_completed();
+                                    self.switch_workspaces(workspaces, None);
                                     (Progress::End, None)
                                 }
                             };
-
                             self.report_progress("fetching", state, msg, None);
                         }
                         Task::FetchBuildData(progress) => {
@@ -255,21 +240,19 @@ impl GlobalState {
                                 BuildDataProgress::Report(msg) => {
                                     (Some(Progress::Report), Some(msg))
                                 }
-                                BuildDataProgress::End(build_data_result) => {
-                                    self.fetch_build_data_completed(build_data_result);
-
-                                    self.switch_workspaces();
-
+                                BuildDataProgress::End(collector) => {
+                                    self.fetch_build_data_completed();
+                                    let workspaces =
+                                        (*self.workspaces).clone().into_iter().map(Ok).collect();
+                                    self.switch_workspaces(workspaces, Some(collector));
                                     (Some(Progress::End), None)
                                 }
                             };
-
                             if let Some(state) = state {
                                 self.report_progress("loading", state, msg, None);
                             }
                         }
                     }
-
                     // Coalesce multiple task events into one loop turn
                     task = match self.task_pool.receiver.try_recv() {
                         Ok(task) => task,
@@ -315,25 +298,30 @@ impl GlobalState {
                         }
                         vfs::loader::Message::Progress { n_total, n_done, config_version } => {
                             always!(config_version <= self.vfs_config_version);
-
-                            self.vfs_progress_config_version = config_version;
-                            self.vfs_progress_n_total = n_total;
-                            self.vfs_progress_n_done = n_done;
-
-                            let state = if n_done == 0 {
-                                Progress::Begin
-                            } else if n_done < n_total {
-                                Progress::Report
+                            if n_total == 0 {
+                                new_status = Status::Invalid;
                             } else {
-                                assert_eq!(n_done, n_total);
-                                Progress::End
-                            };
-                            self.report_progress(
-                                "roots scanned",
-                                state,
-                                Some(format!("{}/{}", n_done, n_total)),
-                                Some(Progress::fraction(n_done, n_total)),
-                            )
+                                let state = if n_done == 0 {
+                                    new_status = Status::Loading;
+                                    Progress::Begin
+                                } else if n_done < n_total {
+                                    Progress::Report
+                                } else {
+                                    assert_eq!(n_done, n_total);
+                                    new_status = Status::Ready {
+                                        partial: self.config.run_build_scripts()
+                                            && self.workspace_build_data.is_none()
+                                            || config_version < self.vfs_config_version,
+                                    };
+                                    Progress::End
+                                };
+                                self.report_progress(
+                                    "roots scanned",
+                                    state,
+                                    Some(format!("{}/{}", n_done, n_total)),
+                                    Some(Progress::fraction(n_done, n_total)),
+                                )
+                            }
                         }
                     }
                     // Coalesce many VFS event into a single loop turn
@@ -409,14 +397,18 @@ impl GlobalState {
         }
 
         let state_changed = self.process_changes();
-
-        if self.is_quiescent() && !was_quiescent {
+        let prev_status = self.status;
+        if prev_status != new_status {
+            self.transition(new_status);
+        }
+        let is_ready = matches!(self.status, Status::Ready { .. });
+        if prev_status == Status::Loading && is_ready {
             for flycheck in &self.flycheck {
                 flycheck.update();
             }
         }
 
-        if self.is_quiescent() && (!was_quiescent || state_changed) {
+        if is_ready && (state_changed || prev_status == Status::Loading) {
             self.update_file_notifications_on_threadpool();
 
             // Refresh semantic tokens if the client supports it.
@@ -445,13 +437,9 @@ impl GlobalState {
             }
         }
 
-        if self.config.cargo_autoreload() {
-            self.fetch_workspaces_if_needed();
-        }
+        self.fetch_workspaces_if_needed();
         self.fetch_build_data_if_needed();
 
-        self.report_new_status_if_needed();
-
         let loop_duration = loop_start.elapsed();
         if loop_duration > Duration::from_millis(100) {
             log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -478,23 +466,18 @@ impl GlobalState {
             return Ok(());
         }
 
-        // Avoid flashing a bunch of unresolved references during initial load.
-        if self.workspaces.is_empty() && !self.is_quiescent() {
+        if self.status == Status::Loading && req.method != "shutdown" {
             self.respond(lsp_server::Response::new_err(
                 req.id,
                 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
                 lsp_server::ErrorCode::ContentModified as i32,
-                "waiting for cargo metadata or cargo check".to_owned(),
+                "Rust Analyzer is still loading...".to_owned(),
             ));
             return Ok(());
         }
 
         RequestDispatcher { req: Some(req), global_state: self }
-            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| {
-                s.fetch_workspaces_request();
-                s.fetch_workspaces_if_needed();
-                Ok(())
-            })?
+            .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))?
             .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
             .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
             .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
index 1d612a9337..f71b718bc0 100644
--- a/crates/rust-analyzer/src/op_queue.rs
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -2,27 +2,27 @@
 //! at a time.
 
 pub(crate) struct OpQueue<Args, Output> {
-    op_requested: Option<Args>,
+    op_scheduled: Option<Args>,
     op_in_progress: bool,
     last_op_result: Output,
 }
 
 impl<Args, Output: Default> Default for OpQueue<Args, Output> {
     fn default() -> Self {
-        Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
+        Self { op_scheduled: None, op_in_progress: false, last_op_result: Default::default() }
     }
 }
 
 impl<Args, Output> OpQueue<Args, Output> {
     pub(crate) fn request_op(&mut self, data: Args) {
-        self.op_requested = Some(data);
+        self.op_scheduled = Some(data);
     }
     pub(crate) fn should_start_op(&mut self) -> Option<Args> {
         if self.op_in_progress {
             return None;
         }
-        self.op_in_progress = self.op_requested.is_some();
-        self.op_requested.take()
+        self.op_in_progress = self.op_scheduled.is_some();
+        self.op_scheduled.take()
     }
     pub(crate) fn op_completed(&mut self, result: Output) {
         assert!(self.op_in_progress);
@@ -34,10 +34,4 @@ impl<Args, Output> OpQueue<Args, Output> {
     pub(crate) fn last_op_result(&self) -> &Output {
         &self.last_op_result
     }
-    pub(crate) fn op_in_progress(&self) -> bool {
-        self.op_in_progress
-    }
-    pub(crate) fn op_requested(&self) -> bool {
-        self.op_requested.is_some()
-    }
 }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index d0cc1b61a1..d12f891bbc 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -9,10 +9,11 @@ use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
 
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
-    global_state::GlobalState,
+    global_state::{GlobalState, Status},
     lsp_ext,
     main_loop::Task,
 };
+use lsp_ext::StatusParams;
 
 #[derive(Debug)]
 pub(crate) enum ProjectWorkspaceProgress {
@@ -29,13 +30,6 @@ pub(crate) enum BuildDataProgress {
 }
 
 impl GlobalState {
-    pub(crate) fn is_quiescent(&self) -> bool {
-        !(self.fetch_workspaces_queue.op_in_progress()
-            || self.fetch_build_data_queue.op_in_progress()
-            || self.vfs_progress_config_version < self.vfs_config_version
-            || self.vfs_progress_n_done < self.vfs_progress_n_total)
-    }
-
     pub(crate) fn update_configuration(&mut self, config: Config) {
         let _p = profile::span("GlobalState::update_configuration");
         let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -52,17 +46,25 @@ impl GlobalState {
         if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
             return;
         }
+        match self.status {
+            Status::Loading | Status::NeedsReload => return,
+            Status::Ready { .. } | Status::Invalid => (),
+        }
         log::info!(
-            "Requesting workspace reload because of the following changes: {}",
+            "Reloading workspace because of the following changes: {}",
             itertools::join(
                 changes
                     .iter()
                     .filter(|(path, kind)| is_interesting(path, *kind))
-                    .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
+                    .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)),
                 ", "
             )
         );
-        self.fetch_workspaces_request();
+        if self.config.cargo_autoreload() {
+            self.fetch_workspaces_request();
+        } else {
+            self.transition(Status::NeedsReload);
+        }
 
         fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
             const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
@@ -99,38 +101,46 @@ impl GlobalState {
             false
         }
     }
-    pub(crate) fn report_new_status_if_needed(&mut self) {
-        if !self.config.server_status_notification() {
-            return;
+    pub(crate) fn transition(&mut self, new_status: Status) {
+        self.status = new_status;
+        if self.config.status_notification() {
+            let lsp_status = match new_status {
+                Status::Loading => lsp_ext::Status::Loading,
+                Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
+                Status::Ready { partial: false } => lsp_ext::Status::Ready,
+                Status::Invalid => lsp_ext::Status::Invalid,
+                Status::NeedsReload => lsp_ext::Status::NeedsReload,
+            };
+            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
+                status: lsp_status,
+            });
         }
+    }
 
-        let mut status = lsp_ext::ServerStatusParams {
-            health: lsp_ext::Health::Ok,
-            quiescent: self.is_quiescent(),
-            message: None,
-        };
-
-        if let Some(error) = self.build_data_error() {
-            status.health = lsp_ext::Health::Warning;
-            status.message = Some(error)
-        }
-        if !self.config.cargo_autoreload()
-            && self.is_quiescent()
-            && self.fetch_workspaces_queue.op_requested()
-        {
-            status.health = lsp_ext::Health::Warning;
-            status.message = Some("Workspace reload required".to_string())
-        }
+    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
+        self.fetch_build_data_queue.request_op(build_data_collector);
+    }
 
-        if let Some(error) = self.fetch_workspace_error() {
-            status.health = lsp_ext::Health::Error;
-            status.message = Some(error)
-        }
+    pub(crate) fn fetch_build_data_if_needed(&mut self) {
+        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
+            Some(it) => it,
+            None => return,
+        };
+        self.task_pool.handle.spawn_with_sender(move |sender| {
+            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
 
-        if self.last_reported_status.as_ref() != Some(&status) {
-            self.last_reported_status = Some(status.clone());
-            self.send_notification::<lsp_ext::ServerStatusNotification>(status);
-        }
+            let progress = {
+                let sender = sender.clone();
+                move |msg| {
+                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
+                }
+            };
+            let res = build_data_collector.collect(&progress);
+            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
+        });
+    }
+    pub(crate) fn fetch_build_data_completed(&mut self) {
+        self.fetch_build_data_queue.op_completed(())
     }
 
     pub(crate) fn fetch_workspaces_request(&mut self) {
@@ -184,74 +194,57 @@ impl GlobalState {
             }
         });
     }
-    pub(crate) fn fetch_workspaces_completed(
-        &mut self,
-        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
-    ) {
-        self.fetch_workspaces_queue.op_completed(workspaces)
-    }
-
-    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
-        self.fetch_build_data_queue.request_op(build_data_collector);
+    pub(crate) fn fetch_workspaces_completed(&mut self) {
+        self.fetch_workspaces_queue.op_completed(())
     }
-    pub(crate) fn fetch_build_data_if_needed(&mut self) {
-        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
-            Some(it) => it,
-            None => return,
-        };
-        self.task_pool.handle.spawn_with_sender(move |sender| {
-            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
 
-            let progress = {
-                let sender = sender.clone();
-                move |msg| {
-                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
-                }
-            };
-            let res = build_data_collector.collect(&progress);
-            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
-        });
-    }
-    pub(crate) fn fetch_build_data_completed(
+    pub(crate) fn switch_workspaces(
         &mut self,
-        build_data: anyhow::Result<BuildDataResult>,
+        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
+        workspace_build_data: Option<anyhow::Result<BuildDataResult>>,
     ) {
-        self.fetch_build_data_queue.op_completed(Some(build_data))
-    }
-
-    pub(crate) fn switch_workspaces(&mut self) {
         let _p = profile::span("GlobalState::switch_workspaces");
-        log::info!("will switch workspaces");
+        log::info!("will switch workspaces: {:?}", workspaces);
 
-        if let Some(error_message) = self.fetch_workspace_error() {
-            log::error!("failed to switch workspaces: {}", error_message);
-            self.show_message(lsp_types::MessageType::Error, error_message);
-            if !self.workspaces.is_empty() {
-                return;
-            }
-        }
-
-        if let Some(error_message) = self.build_data_error() {
-            log::error!("failed to switch build data: {}", error_message);
-            self.show_message(lsp_types::MessageType::Error, error_message);
-        }
-
-        let workspaces = self
-            .fetch_workspaces_queue
-            .last_op_result()
-            .iter()
-            .filter_map(|res| res.as_ref().ok().cloned())
+        let mut has_errors = false;
+        let workspaces = workspaces
+            .into_iter()
+            .filter_map(|res| {
+                res.map_err(|err| {
+                    has_errors = true;
+                    log::error!("failed to load workspace: {:#}", err);
+                    if self.workspaces.is_empty() {
+                        self.show_message(
+                            lsp_types::MessageType::Error,
+                            format!("rust-analyzer failed to load workspace: {:#}", err),
+                        );
+                    }
+                })
+                .ok()
+            })
             .collect::<Vec<_>>();
 
-        let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
-            Some(Ok(it)) => Some(it.clone()),
-            None | Some(Err(_)) => None,
+        let workspace_build_data = match workspace_build_data {
+            Some(Ok(it)) => Some(it),
+            Some(Err(err)) => {
+                log::error!("failed to fetch build data: {:#}", err);
+                self.show_message(
+                    lsp_types::MessageType::Error,
+                    format!("rust-analyzer failed to fetch build data: {:#}", err),
+                );
+                return;
+            }
+            None => None,
         };
 
         if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
             return;
         }
 
+        if !self.workspaces.is_empty() && has_errors {
+            return;
+        }
+
         if let FilesWatcher::Client = self.config.files().watcher {
             if self.config.did_change_watched_files_dynamic_registration() {
                 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
@@ -344,6 +337,14 @@ impl GlobalState {
         };
         change.set_crate_graph(crate_graph);
 
+        if self.config.run_build_scripts() && workspace_build_data.is_none() {
+            let mut collector = BuildDataCollector::default();
+            for ws in &workspaces {
+                ws.collect_build_data_configs(&mut collector);
+            }
+            self.fetch_build_data_request(collector)
+        }
+
         self.source_root_config = project_folders.source_root_config;
         self.workspaces = Arc::new(workspaces);
         self.workspace_build_data = workspace_build_data;
@@ -354,32 +355,6 @@ impl GlobalState {
         log::info!("did switch workspaces");
     }
 
-    fn fetch_workspace_error(&self) -> Option<String> {
-        let mut buf = String::new();
-
-        for ws in self.fetch_workspaces_queue.last_op_result() {
-            if let Err(err) = ws {
-                stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
-            }
-        }
-
-        if buf.is_empty() {
-            return None;
-        }
-
-        Some(buf)
-    }
-
-    fn build_data_error(&self) -> Option<String> {
-        match self.fetch_build_data_queue.last_op_result() {
-            Some(Err(err)) => {
-                Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
-            }
-            Some(Ok(data)) => data.error(),
-            None => None,
-        }
-    }
-
     fn reload_flycheck(&mut self) {
         let _p = profile::span("GlobalState::reload_flycheck");
         let config = match self.config.flycheck() {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2ac31d981b..c3820944bc 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -497,8 +497,7 @@ pub(crate) fn folding_range(
         | FoldKind::Block
         | FoldKind::ArgList
         | FoldKind::Consts
-        | FoldKind::Statics
-        | FoldKind::Array => None,
+        | FoldKind::Statics => None,
     };
 
     let range = range(line_index, fold.range);
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index b0a18d58de..d26be48534 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -178,7 +178,6 @@ where
     start..start + len
 }
 
-#[repr(transparent)]
 pub struct JodChild(pub process::Child);
 
 impl ops::Deref for JodChild {
@@ -201,13 +200,6 @@ impl Drop for JodChild {
     }
 }
 
-impl JodChild {
-    pub fn into_inner(self) -> process::Child {
-        // SAFETY: repr transparent
-        unsafe { std::mem::transmute::<JodChild, process::Child>(self) }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index f09cdf28af..10024366e9 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
 cov-mark = { version = "1.1", features = ["thread-local"] }
 itertools = "0.10.0"
 rowan = "0.13.0-pre.3"
-rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.7"
 once_cell = "1.3.1"
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index 894944b18d..c318a68f72 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -14,4 +14,3 @@ rustc-hash = "1.0"
 fst = "0.4"
 
 paths = { path = "../paths", version = "0.0.0" }
-indexmap = "1.6.2"
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
index 6e049f0d40..2189e5e258 100644
--- a/crates/vfs/src/path_interner.rs
+++ b/crates/vfs/src/path_interner.rs
@@ -1,22 +1,15 @@
 //! Maps paths to compact integer ids. We don't care about clearings paths which
 //! no longer exist -- the assumption is total size of paths we ever look at is
 //! not too big.
-use std::hash::BuildHasherDefault;
-
-use indexmap::IndexSet;
-use rustc_hash::FxHasher;
+use rustc_hash::FxHashMap;
 
 use crate::{FileId, VfsPath};
 
 /// Structure to map between [`VfsPath`] and [`FileId`].
+#[derive(Default)]
 pub(crate) struct PathInterner {
-    map: IndexSet<VfsPath, BuildHasherDefault<FxHasher>>,
-}
-
-impl Default for PathInterner {
-    fn default() -> Self {
-        Self { map: IndexSet::default() }
-    }
+    map: FxHashMap<VfsPath, FileId>,
+    vec: Vec<VfsPath>,
 }
 
 impl PathInterner {
@@ -24,7 +17,7 @@ impl PathInterner {
     ///
     /// If `path` does not exists in `self`, returns [`None`].
     pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
-        self.map.get_index_of(path).map(|i| FileId(i as u32))
+        self.map.get(path).copied()
     }
 
     /// Insert `path` in `self`.
@@ -32,9 +25,13 @@ impl PathInterner {
     /// - If `path` already exists in `self`, returns its associated id;
     /// - Else, returns a newly allocated id.
     pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
-        let (id, _added) = self.map.insert_full(path);
-        assert!(id < u32::MAX as usize);
-        FileId(id as u32)
+        if let Some(id) = self.get(&path) {
+            return id;
+        }
+        let id = FileId(self.vec.len() as u32);
+        self.map.insert(path.clone(), id);
+        self.vec.push(path);
+        id
     }
 
     /// Returns the path corresponding to `id`.
@@ -43,6 +40,6 @@ impl PathInterner {
     ///
     /// Panics if `id` does not exists in `self`.
     pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
-        self.map.get_index(id.0 as usize).unwrap()
+        &self.vec[id.0 as usize]
     }
 }
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 9c0eb13582..eab21a7653 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -24,7 +24,7 @@ Rust Analyzer is a part of [RLS-2.0 working
 group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
 Discussion happens in this Zulip stream:
 
-https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
+https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
 
 # Issue Labels
 
@@ -32,8 +32,6 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
   are good issues to get into the project.
 * [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
   issues have links to the code in question and tests.
-* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22)
-  are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt.
 * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
   [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
   [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 3ffd9e8cbb..fb991133aa 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
 ## Entry Points
 
 `crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
-This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
+This is *the* entry point, but it front-loads a lot of complexity, so its fine to just skim through it.
 
 `crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
 
@@ -67,7 +67,7 @@ They are handled by Rust code in the xtask directory.
 
 VS Code plugin.
 
-### `lib/`
+### `libs/`
 
 rust-analyzer independent libraries which we publish to crates.io.
 It's not heavily utilized at the moment.
@@ -139,8 +139,7 @@ If an AST method returns an `Option`, it *can* be `None` at runtime, even if thi
 ### `crates/base_db`
 
 We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation.
-Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions. 
-The `base_db` crate provides basic infrastructure for interacting with salsa.
+Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions. The `base_db` crate provides basic infrastructure for interacting with salsa.
 Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer.
 Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs.
 
@@ -222,7 +221,7 @@ Internally, `ide` is split across several crates. `ide_assists`, `ide_completion
 The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features.
 
 **Architecture Invariant:** `ide` crate strives to provide a _perfect_ API.
-Although at the moment it has only one consumer, the LSP server, LSP *does not* influence its API design.
+Although at the moment it has only one consumer, the LSP server, LSP *does not* influence it's API design.
 Instead, we keep in mind a hypothetical _ideal_ client -- an IDE tailored specifically for rust, every nook and cranny of which is packed with Rust-specific goodies.
 
 ### `crates/rust-analyzer`
@@ -308,7 +307,7 @@ This sections talks about the things which are everywhere and nowhere in particu
 
 ### Code generation
 
-Some ]components in this repository are generated through automatic processes.
+Some of the components of this repository are generated through automatic processes.
 Generated code is updated automatically on `cargo test`.
 Generated code is generally committed to the git repository.
 
@@ -390,7 +389,7 @@ fn spam() {
 ```
 
 To specify input data, we use a single string literal in a special format, which can describe a set of rust files.
-See the `Fixture` its module for fixture examples and documentation.
+See the `Fixture` type.
 
 **Architecture Invariant:** all code invariants are tested by `#[test]` tests.
 There's no additional checks in CI, formatting and tidy tests are run with `cargo test`.
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index a46121bb2e..73be59a82e 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: faae991334a151d0
+lsp_ext.rs hash: e8a7502bd2b2c2f5
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this  issue:
@@ -51,8 +51,8 @@ interface SnippetTextEdit extends TextEdit {
 
 ```typescript
 export interface TextDocumentEdit {
-    textDocument: OptionalVersionedTextDocumentIdentifier;
-    edits: (TextEdit | SnippetTextEdit)[];
+	textDocument: OptionalVersionedTextDocumentIdentifier;
+	edits: (TextEdit | SnippetTextEdit)[];
 }
 ```
 
@@ -145,9 +145,9 @@ mod foo;
 ### Unresolved Question
 
 * An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
-  This is the approach IntelliJ Rust is taking.
+  This is the approach IntelliJ Rust is takeing.
   However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
-  If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
+  If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising.
 
 ## Join Lines
 
@@ -193,7 +193,7 @@ fn main() {
 ### Unresolved Question
 
 * What is the position of the cursor after `joinLines`?
-  Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
+  Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
   However, it then becomes unclear how it works with multi cursor.
 
 ## On Enter
@@ -330,7 +330,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
 
 ### Unresolved Question
 
-* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
+* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
   This is how `SelectionRange` request works.
 * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
 
@@ -419,37 +419,23 @@ Returns internal status message, mostly for debugging purposes.
 
 Reloads project information (that is, re-executes `cargo metadata`).
 
-## Server Status
+## Status Notification
 
-**Experimental Client Capability:** `{ "serverStatus": boolean }`
+**Experimental Client Capability:** `{ "statusNotification": boolean }`
 
-**Method:** `experimental/serverStatus`
+**Method:** `rust-analyzer/status`
 
 **Notification:**
 
 ```typescript
-interface ServerStatusParams {
-    /// `ok` means that the server is completely functional.
-    ///
-    /// `warning` means that the server is partially functional.
-    /// It can server requests, but some results might be wrong due to,
-    /// for example, some missing dependencies.
-    ///
-    /// `error` means that the server is not functional. For example,
-    /// there's a fatal build configuration problem.
-    health: "ok" | "warning" | "error",
-    /// Is there any pending background work which might change the status?
-    /// For example, are dependencies being downloaded?
-    quiescent: bool,
-    /// Explanatory message to show on hover.
-    message?: string,
+interface StatusParams {
+    status: "loading" | "readyPartial" | "ready" | "invalid" | "needsReload",
 }
 ```
 
 This notification is sent from server to client.
-The client can use it to display *persistent* status to the user (in modline).
-It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
-
+The client can use it to display persistent status to the user (in modline).
+For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request.
 
 ## Syntax Tree
 
@@ -511,7 +497,7 @@ Expands macro call at a given position.
 This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
 Generally, the client should re-query inlay hints after every modification.
 Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
-Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
+Upstream issue: https://github.com/microsoft/language-server-protocol/issues/956
 
 **Request:**
 
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 468dedff20..48ce4b92a6 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -53,9 +53,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
 ## Crates.io Dependencies
 
 We try to be very conservative with usage of crates.io dependencies.
-Don't use small "helper" crates (exception: `itertools` and `either` are allowed).
+Don't use small "helper" crates (exception: `itertools` is allowed).
 If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
-A useful exercise is to read Cargo.lock and see if some *transitive* dependencies do not make sense for rust-analyzer.
+A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer.
 
 **Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
 
@@ -330,7 +330,7 @@ When implementing `do_thing`, it might be very useful to create a context object
 
 ```rust
 pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
-    let mut ctx = Ctx { arg1, arg2 };
+    let mut ctx = Ctx { arg1, arg2 }
     ctx.run()
 }
 
@@ -586,7 +586,7 @@ use super::{}
 
 **Rationale:** consistency.
 Reading order is important for new contributors.
-Grouping by crate allows spotting unwanted dependencies easier.
+Grouping by crate allows to spot unwanted dependencies easier.
 
 ## Import Style
 
@@ -779,7 +779,7 @@ assert!(x < y);
 assert!(x > 0);
 
 // BAD
-assert!(x >= lo && x <= hi);
+assert!(x >= lo && x <= hi>);
 assert!(r1 < l2 || l1 > r2);
 assert!(y > x);
 assert!(0 > x);
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index f7a0c09fc1..737cc7a729 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -145,7 +145,7 @@ Another alternative (used by swift and roslyn) is to explicitly divide the set o
 
 ```rust
 struct Token {
-    kind: NonTriviaTokenKind,
+    kind: NonTriviaTokenKind
     text: String,
     leading_trivia: Vec<TriviaToken>,
     trailing_trivia: Vec<TriviaToken>,
@@ -240,7 +240,7 @@ impl SyntaxNode {
             let child_offset = offset;
             offset += green_child.text_len;
             Arc::new(SyntaxData {
-                offset: child_offset,
+                offset: child_offset;
                 parent: Some(Arc::clone(self)),
                 green: Arc::clone(green_child),
             })
@@ -249,7 +249,7 @@ impl SyntaxNode {
 }
 
 impl PartialEq for SyntaxNode {
-    fn eq(&self, other: &SyntaxNode) -> bool {
+    fn eq(&self, other: &SyntaxNode) {
         self.offset == other.offset
             && Arc::ptr_eq(&self.green, &other.green)
     }
@@ -273,7 +273,7 @@ This is OK because trees traversals mostly (always, in case of rust-analyzer) ru
 The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range.
 You can also use the similar trick to store a `SyntaxNode`.
 That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`.
-However, rust-analyzer goes even further.
+However rust-analyzer goes even further.
 It treats trees as semi-transient and instead of storing a `GreenNode`, it generally stores just the id of the file from which the tree originated: `(FileId, Range<usize>)`.
 The `SyntaxNode` is the restored by reparsing the file and traversing it from root.
 With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage.
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 116f41df6e..0771ca3b69 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -159,7 +159,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
         caps.snippetTextEdit = true;
         caps.codeActionGroup = true;
         caps.hoverActions = true;
-        caps.serverStatusNotification = true;
+        caps.statusNotification = true;
         capabilities.experimental = caps;
     }
     initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index bd023f803e..c07583cfa2 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -5,7 +5,7 @@ import * as ra from './lsp_ext';
 import { Config } from './config';
 import { createClient } from './client';
 import { isRustEditor, RustEditor } from './util';
-import { ServerStatusParams } from './lsp_ext';
+import { Status } from './lsp_ext';
 
 export class Ctx {
     private constructor(
@@ -36,7 +36,7 @@ export class Ctx {
 
         res.pushCleanup(client.start());
         await client.onReady();
-        client.onNotification(ra.serverStatus, (params) => res.setServerStatus(params));
+        client.onNotification(ra.status, (params) => res.setStatus(params.status));
         return res;
     }
 
@@ -66,28 +66,39 @@ export class Ctx {
         return this.extCtx.subscriptions;
     }
 
-    setServerStatus(status: ServerStatusParams) {
-        this.statusBar.tooltip = status.message ?? "Ready";
-        let icon = "";
-        switch (status.health) {
-            case "ok":
+    setStatus(status: Status) {
+        switch (status) {
+            case "loading":
+                this.statusBar.text = "$(sync~spin) rust-analyzer";
+                this.statusBar.tooltip = "Loading the project";
+                this.statusBar.command = undefined;
                 this.statusBar.color = undefined;
                 break;
-            case "warning":
-                this.statusBar.tooltip += "\nClick to reload.";
-                this.statusBar.command = "rust-analyzer.reloadWorkspace";
-                this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
-                icon = "$(warning) ";
+            case "readyPartial":
+                this.statusBar.text = "rust-analyzer";
+                this.statusBar.tooltip = "Ready (Partial)";
+                this.statusBar.command = undefined;
+                this.statusBar.color = undefined;
                 break;
-            case "error":
-                this.statusBar.tooltip += "\nClick to reload.";
-                this.statusBar.command = "rust-analyzer.reloadWorkspace";
+            case "ready":
+                this.statusBar.text = "rust-analyzer";
+                this.statusBar.tooltip = "Ready";
+                this.statusBar.command = undefined;
+                this.statusBar.color = undefined;
+                break;
+            case "invalid":
+                this.statusBar.text = "$(error) rust-analyzer";
+                this.statusBar.tooltip = "Failed to load the project";
+                this.statusBar.command = undefined;
                 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
-                icon = "$(error) ";
+                break;
+            case "needsReload":
+                this.statusBar.text = "$(warning) rust-analyzer";
+                this.statusBar.tooltip = "Click to reload";
+                this.statusBar.command = "rust-analyzer.reloadWorkspace";
+                this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
                 break;
         }
-        if (!status.quiescent) icon = "$(sync~spin) ";
-        this.statusBar.text = `${icon} rust-analyzer`;
     }
 
     pushCleanup(d: Disposable) {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index e453bb9e0b..00e128b8c3 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -10,12 +10,11 @@ export interface AnalyzerStatusParams {
 export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
 export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
 
-export interface ServerStatusParams {
-    health: "ok" | "warning" | "error";
-    quiescent: boolean;
-    message?: string;
+export type Status = "loading" | "ready" | "readyPartial" | "invalid" | "needsReload";
+export interface StatusParams {
+    status: Status;
 }
-export const serverStatus = new lc.NotificationType<ServerStatusParams>("experimental/serverStatus");
+export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status");
 
 export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
 


Test patch:
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index c1d3e998f3..faa1332970 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -143,7 +143,7 @@ fn f() {
   //^^^^^^^^^^^^^ could not convert tokens
 
     env!("OUT_DIR");
-  //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
+  //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
 
     compile_error!("compile_error works");
   //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 1ac88fc892..fefdadb225 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -233,7 +233,7 @@ fn good_out_dir_diagnostic() {
         macro_rules! concat { () => {} }
 
         include!(concat!(env!("OUT_DIR"), "/out.rs"));
-      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
+      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
         "#,
     );
 }
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 4b2c82b417..61f18b0d20 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1292,25 +1292,3 @@ mod b {
         "#]],
     )
 }
-
-#[test]
-fn impl_in_unnamed_const() {
-    check_types(
-        r#"
-struct S;
-
-trait Tr {
-    fn method(&self) -> u16;
-}
-
-const _: () = {
-    impl Tr for S {}
-};
-
-fn f() {
-    S.method();
-  //^^^^^^^^^^ u16
-}
-    "#,
-    );
-}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 84c5c05fd3..361cd63026 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2564,36 +2564,3 @@ fn f() {
     "#,
     )
 }
-
-#[test]
-fn infer_type_alias_variant() {
-    check_infer(
-        r#"
-type Qux = Foo;
-enum Foo {
-    Bar(i32),
-    Baz { baz: f32 }
-}
-
-fn f() {
-    match Foo::Bar(3) {
-        Qux::Bar(bar) => (),
-        Qux::Baz { baz } => (),
-    }
-}
-    "#,
-        expect![[r#"
-            72..166 '{     ...   } }': ()
-            78..164 'match ...     }': ()
-            84..92 'Foo::Bar': Bar(i32) -> Foo
-            84..95 'Foo::Bar(3)': Foo
-            93..94 '3': i32
-            106..119 'Qux::Bar(bar)': Foo
-            115..118 'bar': i32
-            123..125 '()': ()
-            135..151 'Qux::B... baz }': Foo
-            146..149 'baz': f32
-            155..157 '()': ()
-        "#]],
-    )
-}
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 8d68f1b7d5..95bf26f016 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -103,7 +103,7 @@ impl<'a> Project<'a> {
                     ..Default::default()
                 }),
                 experimental: Some(json!({
-                    "serverStatusNotification": true,
+                    "statusNotification": true,
                 })),
                 ..Default::default()
             },
@@ -213,12 +213,13 @@ impl Server {
     }
     pub(crate) fn wait_until_workspace_is_loaded(self) -> Server {
         self.wait_for_message_cond(1, &|msg: &Message| match msg {
-            Message::Notification(n) if n.method == "experimental/serverStatus" => {
+            Message::Notification(n) if n.method == "rust-analyzer/status" => {
                 let status = n
                     .clone()
-                    .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
-                    .unwrap();
-                status.quiescent
+                    .extract::<lsp_ext::StatusParams>("rust-analyzer/status")
+                    .unwrap()
+                    .status;
+                matches!(status, lsp_ext::Status::Ready)
             }
             _ => false,
         })
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs
index 099baeca2f..6bc824e943 100644
--- a/crates/test_utils/src/fixture.rs
+++ b/crates/test_utils/src/fixture.rs
@@ -1,65 +1,5 @@
 //! Defines `Fixture` -- a convenient way to describe the initial state of
 //! rust-analyzer database from a single string.
-//!
-//! Fixtures are strings containing rust source code with optional metadata.
-//! A fixture without metadata is parsed into a single source file.
-//! Use this to test functionality local to one file.
-//!
-//! Simple Example:
-//! ```
-//! r#"
-//! fn main() {
-//!     println!("Hello World")
-//! }
-//! "#
-//! ```
-//!
-//! Metadata can be added to a fixture after a `//-` comment.
-//! The basic form is specifying filenames,
-//! which is also how to define multiple files in a single test fixture
-//!
-//! Example using two files in the same crate:
-//! ```
-//! "
-//! //- /main.rs
-//! mod foo;
-//! fn main() {
-//!     foo::bar();
-//! }
-//!
-//! //- /foo.rs
-//! pub fn bar() {}
-//! "
-//! ```
-//!
-//! Example using two crates with one file each, with one crate depending on the other:
-//! ```
-//! r#"
-//! //- /main.rs crate:a deps:b
-//! fn main() {
-//!     b::foo();
-//! }
-//! //- /lib.rs crate:b
-//! pub fn b() {
-//!     println!("Hello World")
-//! }
-//! "#
-//! ```
-//!
-//! Metadata allows specifying all settings and variables
-//! that are available in a real rust project:
-//! - crate names via `crate:cratename`
-//! - dependencies via `deps:dep1,dep2`
-//! - configuration settings via `cfg:dbg=false,opt_level=2`
-//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
-//!
-//! Example using all available metadata:
-//! ```
-//! "
-//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
-//! fn insert_source_code_here() {}
-//! "
-//! ```
 
 use rustc_hash::FxHashMap;
 use stdx::{lines_with_ends, split_once, trim_indent};
@@ -84,7 +24,7 @@ impl Fixture {
     ///  //- some meta
     ///  line 1
     ///  line 2
-    ///  //- other meta
+    ///  // - other meta
     ///  ```
     pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
         let fixture = trim_indent(ra_fixture);