diff --git a/Cargo.lock b/Cargo.lock
index ec197767259..fc77515b63b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -572,6 +572,7 @@ dependencies = [
  "chalk-recursive",
  "chalk-solve",
  "cov-mark",
+ "either",
  "ena",
  "expect-test",
  "hir-def",
@@ -1714,6 +1715,7 @@ name = "syntax"
 version = "0.0.0"
 dependencies = [
  "cov-mark",
+ "either",
  "expect-test",
  "indexmap",
  "itertools",
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 11f7b068ecb..accb14a51de 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -76,7 +76,7 @@ impl fmt::Display for FlycheckConfig {
 #[derive(Debug)]
 pub struct FlycheckHandle {
     // XXX: drop order is significant
-    sender: Sender<Restart>,
+    sender: Sender<StateChange>,
     _thread: jod_thread::JoinHandle,
     id: usize,
 }
@@ -89,7 +89,7 @@ impl FlycheckHandle {
         workspace_root: AbsPathBuf,
     ) -> FlycheckHandle {
         let actor = FlycheckActor::new(id, sender, config, workspace_root);
-        let (sender, receiver) = unbounded::<Restart>();
+        let (sender, receiver) = unbounded::<StateChange>();
         let thread = jod_thread::Builder::new()
             .name("Flycheck".to_owned())
             .spawn(move || actor.run(receiver))
@@ -99,12 +99,12 @@ impl FlycheckHandle {
 
     /// Schedule a re-start of the cargo check worker.
     pub fn restart(&self) {
-        self.sender.send(Restart::Yes).unwrap();
+        self.sender.send(StateChange::Restart).unwrap();
     }
 
     /// Stop this cargo check worker.
     pub fn cancel(&self) {
-        self.sender.send(Restart::No).unwrap();
+        self.sender.send(StateChange::Cancel).unwrap();
     }
 
     pub fn id(&self) -> usize {
@@ -149,9 +149,9 @@ pub enum Progress {
     DidFailToRestart(String),
 }
 
-enum Restart {
-    Yes,
-    No,
+enum StateChange {
+    Restart,
+    Cancel,
 }
 
 /// A [`FlycheckActor`] is a single check instance of a workspace.
@@ -172,7 +172,7 @@ struct FlycheckActor {
 }
 
 enum Event {
-    Restart(Restart),
+    RequestStateChange(StateChange),
     CheckEvent(Option<CargoMessage>),
 }
 
@@ -191,30 +191,31 @@ impl FlycheckActor {
         self.send(Message::Progress { id: self.id, progress });
     }
 
-    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
+    fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
         let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
         if let Ok(msg) = inbox.try_recv() {
             // give restarts a preference so check outputs don't block a restart or stop
-            return Some(Event::Restart(msg));
+            return Some(Event::RequestStateChange(msg));
         }
         select! {
-            recv(inbox) -> msg => msg.ok().map(Event::Restart),
+            recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
             recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
 
-    fn run(mut self, inbox: Receiver<Restart>) {
+    fn run(mut self, inbox: Receiver<StateChange>) {
         'event: while let Some(event) = self.next_event(&inbox) {
             match event {
-                Event::Restart(Restart::No) => {
+                Event::RequestStateChange(StateChange::Cancel) => {
+                    tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
                     self.cancel_check_process();
                 }
-                Event::Restart(Restart::Yes) => {
+                Event::RequestStateChange(StateChange::Restart) => {
                     // Cancel the previously spawned process
                     self.cancel_check_process();
                     while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
                         // restart chained with a stop, so just cancel
-                        if let Restart::No = restart {
+                        if let StateChange::Cancel = restart {
                             continue 'event;
                         }
                     }
@@ -255,10 +256,20 @@ impl FlycheckActor {
                 }
                 Event::CheckEvent(Some(message)) => match message {
                     CargoMessage::CompilerArtifact(msg) => {
+                        tracing::trace!(
+                            flycheck_id = self.id,
+                            artifact = msg.target.name,
+                            "artifact received"
+                        );
                         self.report_progress(Progress::DidCheckCrate(msg.target.name));
                     }
 
                     CargoMessage::Diagnostic(msg) => {
+                        tracing::trace!(
+                            flycheck_id = self.id,
+                            message = msg.message,
+                            "diagnostic received"
+                        );
                         self.send(Message::AddDiagnostic {
                             id: self.id,
                             workspace_root: self.root.clone(),
@@ -445,42 +456,56 @@ impl CargoActor {
         // simply skip a line if it doesn't parse, which just ignores any
         // erroneous output.
 
-        let mut error = String::new();
-        let mut read_at_least_one_message = false;
+        let mut stdout_errors = String::new();
+        let mut stderr_errors = String::new();
+        let mut read_at_least_one_stdout_message = false;
+        let mut read_at_least_one_stderr_message = false;
+        let process_line = |line: &str, error: &mut String| {
+            // Try to deserialize a message from Cargo or Rustc.
+            let mut deserializer = serde_json::Deserializer::from_str(line);
+            deserializer.disable_recursion_limit();
+            if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
+                match message {
+                    // Skip certain kinds of messages to only spend time on what's useful
+                    JsonMessage::Cargo(message) => match message {
+                        cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
+                            self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
+                        }
+                        cargo_metadata::Message::CompilerMessage(msg) => {
+                            self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
+                        }
+                        _ => (),
+                    },
+                    JsonMessage::Rustc(message) => {
+                        self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
+                    }
+                }
+                return true;
+            }
+
+            error.push_str(line);
+            error.push('\n');
+            return false;
+        };
         let output = streaming_output(
             self.stdout,
             self.stderr,
             &mut |line| {
-                read_at_least_one_message = true;
-
-                // Try to deserialize a message from Cargo or Rustc.
-                let mut deserializer = serde_json::Deserializer::from_str(line);
-                deserializer.disable_recursion_limit();
-                if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
-                    match message {
-                        // Skip certain kinds of messages to only spend time on what's useful
-                        JsonMessage::Cargo(message) => match message {
-                            cargo_metadata::Message::CompilerArtifact(artifact)
-                                if !artifact.fresh =>
-                            {
-                                self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
-                            }
-                            cargo_metadata::Message::CompilerMessage(msg) => {
-                                self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
-                            }
-                            _ => (),
-                        },
-                        JsonMessage::Rustc(message) => {
-                            self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
-                        }
-                    }
+                if process_line(line, &mut stdout_errors) {
+                    read_at_least_one_stdout_message = true;
                 }
             },
             &mut |line| {
-                error.push_str(line);
-                error.push('\n');
+                if process_line(line, &mut stderr_errors) {
+                    read_at_least_one_stderr_message = true;
+                }
             },
         );
+
+        let read_at_least_one_message =
+            read_at_least_one_stdout_message || read_at_least_one_stderr_message;
+        let mut error = stdout_errors;
+        error.push_str(&stderr_errors);
         match output {
             Ok(_) => Ok((read_at_least_one_message, error)),
             Err(e) => Err(io::Error::new(e.kind(), format!("{e:?}: {error}"))),
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index fcd92ad3385..200072c172e 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -300,6 +300,7 @@ impl AttrsWithOwner {
                 AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             },
             AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+            AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
             AttrDefId::MacroId(it) => match it {
                 MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
                 MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
@@ -315,26 +316,14 @@ impl AttrsWithOwner {
                     let src = it.parent().child_source(db);
                     RawAttrs::from_attrs_owner(
                         db.upcast(),
-                        src.with_value(src.value[it.local_id()].as_ref().either(
-                            |it| match it {
-                                ast::TypeOrConstParam::Type(it) => it as _,
-                                ast::TypeOrConstParam::Const(it) => it as _,
-                            },
-                            |it| it as _,
-                        )),
+                        src.with_value(&src.value[it.local_id()]),
                     )
                 }
                 GenericParamId::TypeParamId(it) => {
                     let src = it.parent().child_source(db);
                     RawAttrs::from_attrs_owner(
                         db.upcast(),
-                        src.with_value(src.value[it.local_id()].as_ref().either(
-                            |it| match it {
-                                ast::TypeOrConstParam::Type(it) => it as _,
-                                ast::TypeOrConstParam::Const(it) => it as _,
-                            },
-                            |it| it as _,
-                        )),
+                        src.with_value(&src.value[it.local_id()]),
                     )
                 }
                 GenericParamId::LifetimeParamId(it) => {
@@ -404,6 +393,7 @@ impl AttrsWithOwner {
             AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+            AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::MacroId(id) => match id {
                 MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
@@ -412,28 +402,14 @@ impl AttrsWithOwner {
             },
             AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
             AttrDefId::GenericParamId(id) => match id {
-                GenericParamId::ConstParamId(id) => {
-                    id.parent().child_source(db).map(|source| match &source[id.local_id()] {
-                        Either::Left(ast::TypeOrConstParam::Type(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Left(ast::TypeOrConstParam::Const(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Right(id) => ast::AnyHasAttrs::new(id.clone()),
-                    })
-                }
-                GenericParamId::TypeParamId(id) => {
-                    id.parent().child_source(db).map(|source| match &source[id.local_id()] {
-                        Either::Left(ast::TypeOrConstParam::Type(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Left(ast::TypeOrConstParam::Const(id)) => {
-                            ast::AnyHasAttrs::new(id.clone())
-                        }
-                        Either::Right(id) => ast::AnyHasAttrs::new(id.clone()),
-                    })
-                }
+                GenericParamId::ConstParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
+                GenericParamId::TypeParamId(id) => id
+                    .parent()
+                    .child_source(db)
+                    .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())),
                 GenericParamId::LifetimeParamId(id) => id
                     .parent
                     .child_source(db)
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 8fd9255b8b1..3be477d4877 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -24,7 +24,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
 use crate::{
     attr::Attrs,
     db::DefDatabase,
-    expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
+    expr::{dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId},
     item_scope::BuiltinShadowMode,
     macro_id_to_def_id,
     nameres::DefMap,
@@ -270,7 +270,7 @@ pub struct Mark {
 pub struct Body {
     pub exprs: Arena<Expr>,
     pub pats: Arena<Pat>,
-    pub or_pats: FxHashMap<PatId, Arc<[PatId]>>,
+    pub bindings: Arena<Binding>,
     pub labels: Arena<Label>,
     /// The patterns for the function's parameters. While the parameter types are
     /// part of the function signature, the patterns are not (they don't change
@@ -409,18 +409,6 @@ impl Body {
             .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
     }
 
-    pub fn pattern_representative(&self, pat: PatId) -> PatId {
-        self.or_pats.get(&pat).and_then(|pats| pats.first().copied()).unwrap_or(pat)
-    }
-
-    /// Retrieves all ident patterns this pattern shares the ident with.
-    pub fn ident_patterns_for<'slf>(&'slf self, pat: &'slf PatId) -> &'slf [PatId] {
-        match self.or_pats.get(pat) {
-            Some(pats) => pats,
-            None => std::slice::from_ref(pat),
-        }
-    }
-
     pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
         pretty::print_body_hir(db, self, owner)
     }
@@ -435,13 +423,14 @@ impl Body {
     }
 
     fn shrink_to_fit(&mut self) {
-        let Self { _c: _, body_expr: _, block_scopes, or_pats, exprs, labels, params, pats } = self;
+        let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
+            self;
         block_scopes.shrink_to_fit();
-        or_pats.shrink_to_fit();
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
         params.shrink_to_fit();
         pats.shrink_to_fit();
+        bindings.shrink_to_fit();
     }
 }
 
@@ -451,7 +440,7 @@ impl Default for Body {
             body_expr: dummy_expr_id(),
             exprs: Default::default(),
             pats: Default::default(),
-            or_pats: Default::default(),
+            bindings: Default::default(),
             labels: Default::default(),
             params: Default::default(),
             block_scopes: Default::default(),
@@ -484,6 +473,14 @@ impl Index<LabelId> for Body {
     }
 }
 
+impl Index<BindingId> for Body {
+    type Output = Binding;
+
+    fn index(&self, b: BindingId) -> &Binding {
+        &self.bindings[b]
+    }
+}
+
 // FIXME: Change `node_` prefix to something more reasonable.
 // Perhaps `expr_syntax` and `expr_id`?
 impl BodySourceMap {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 04b1c4f01e2..83ce9b6acbb 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -15,6 +15,7 @@ use la_arena::Arena;
 use once_cell::unsync::OnceCell;
 use profile::Count;
 use rustc_hash::FxHashMap;
+use smallvec::SmallVec;
 use syntax::{
     ast::{
         self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
@@ -30,14 +31,14 @@ use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
     db::DefDatabase,
     expr::{
-        dummy_expr_id, Array, BindingAnnotation, ClosureKind, Expr, ExprId, FloatTypeWrapper,
-        Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
-        Statement,
+        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
+        FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
+        RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
-    AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
+    AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
 };
 
 pub struct LowerCtx<'a> {
@@ -87,16 +88,14 @@ pub(super) fn lower(
         body: Body {
             exprs: Arena::default(),
             pats: Arena::default(),
+            bindings: Arena::default(),
             labels: Arena::default(),
             params: Vec::new(),
             body_expr: dummy_expr_id(),
             block_scopes: Vec::new(),
             _c: Count::new(),
-            or_pats: Default::default(),
         },
         expander,
-        name_to_pat_grouping: Default::default(),
-        is_lowering_inside_or_pat: false,
         is_lowering_assignee_expr: false,
         is_lowering_generator: false,
     }
@@ -109,13 +108,26 @@ struct ExprCollector<'a> {
     ast_id_map: Arc<AstIdMap>,
     body: Body,
     source_map: BodySourceMap,
-    // a poor-mans union-find?
-    name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
-    is_lowering_inside_or_pat: bool,
     is_lowering_assignee_expr: bool,
     is_lowering_generator: bool,
 }
 
+#[derive(Debug, Default)]
+struct BindingList {
+    map: FxHashMap<Name, BindingId>,
+}
+
+impl BindingList {
+    fn find(
+        &mut self,
+        ec: &mut ExprCollector<'_>,
+        name: Name,
+        mode: BindingAnnotation,
+    ) -> BindingId {
+        *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+    }
+}
+
 impl ExprCollector<'_> {
     fn collect(
         mut self,
@@ -127,17 +139,16 @@ impl ExprCollector<'_> {
                 param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
             {
                 let ptr = AstPtr::new(&self_param);
-                let param_pat = self.alloc_pat(
-                    Pat::Bind {
-                        name: name![self],
-                        mode: BindingAnnotation::new(
-                            self_param.mut_token().is_some() && self_param.amp_token().is_none(),
-                            false,
-                        ),
-                        subpat: None,
-                    },
-                    Either::Right(ptr),
+                let binding_id = self.alloc_binding(
+                    name![self],
+                    BindingAnnotation::new(
+                        self_param.mut_token().is_some() && self_param.amp_token().is_none(),
+                        false,
+                    ),
                 );
+                let param_pat =
+                    self.alloc_pat(Pat::Bind { id: binding_id, subpat: None }, Either::Right(ptr));
+                self.add_definition_to_binding(binding_id, param_pat);
                 self.body.params.push(param_pat);
             }
 
@@ -179,6 +190,9 @@ impl ExprCollector<'_> {
         id
     }
 
+    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+        self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
+    }
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.to_source(ptr);
         let id = self.make_pat(pat, src.clone());
@@ -238,33 +252,32 @@ impl ExprCollector<'_> {
             }
             ast::Expr::BlockExpr(e) => match e.modifier() {
                 Some(ast::BlockModifier::Try(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
                 Some(ast::BlockModifier::Unsafe(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
+                        id,
+                        statements,
+                        tail,
+                    })
                 }
-                // FIXME: we need to record these effects somewhere...
                 Some(ast::BlockModifier::Label(label)) => {
                     let label = self.collect_label(label);
-                    let res = self.collect_block(e);
-                    match &mut self.body.exprs[res] {
-                        Expr::Block { label: block_label, .. } => {
-                            *block_label = Some(label);
-                        }
-                        _ => unreachable!(),
-                    }
-                    res
-                }
-                Some(ast::BlockModifier::Async(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Async { body }, syntax_ptr)
-                }
-                Some(ast::BlockModifier::Const(_)) => {
-                    let body = self.collect_block(e);
-                    self.alloc_expr(Expr::Const { body }, syntax_ptr)
+                    self.collect_block_(e, |id, statements, tail| Expr::Block {
+                        id,
+                        statements,
+                        tail,
+                        label: Some(label),
+                    })
                 }
+                Some(ast::BlockModifier::Async(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
+                Some(ast::BlockModifier::Const(_)) => self
+                    .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
                 None => self.collect_block(e),
             },
             ast::Expr::LoopExpr(e) => {
@@ -737,6 +750,19 @@ impl ExprCollector<'_> {
     }
 
     fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
+        self.collect_block_(block, |id, statements, tail| Expr::Block {
+            id,
+            statements,
+            tail,
+            label: None,
+        })
+    }
+
+    fn collect_block_(
+        &mut self,
+        block: ast::BlockExpr,
+        mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+    ) -> ExprId {
         let file_local_id = self.ast_id_map.ast_id(&block);
         let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
         let block_loc =
@@ -769,15 +795,8 @@ impl ExprCollector<'_> {
         });
 
         let syntax_node_ptr = AstPtr::new(&block.into());
-        let expr_id = self.alloc_expr(
-            Expr::Block {
-                id: block_id,
-                statements: statements.into_boxed_slice(),
-                tail,
-                label: None,
-            },
-            syntax_node_ptr,
-        );
+        let expr_id = self
+            .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
 
         self.expander.def_map = prev_def_map;
         self.expander.module = prev_local_module;
@@ -799,13 +818,7 @@ impl ExprCollector<'_> {
     }
 
     fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
-        let pat_id = self.collect_pat_(pat);
-        for (_, pats) in self.name_to_pat_grouping.drain() {
-            let pats = Arc::<[_]>::from(pats);
-            self.body.or_pats.extend(pats.iter().map(|&pat| (pat, pats.clone())));
-        }
-        self.is_lowering_inside_or_pat = false;
-        pat_id
+        self.collect_pat_(pat, &mut BindingList::default())
     }
 
     fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
@@ -815,16 +828,18 @@ impl ExprCollector<'_> {
         }
     }
 
-    fn collect_pat_(&mut self, pat: ast::Pat) -> PatId {
+    fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
         let pattern = match &pat {
             ast::Pat::IdentPat(bp) => {
                 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
 
-                let key = self.is_lowering_inside_or_pat.then(|| name.clone());
                 let annotation =
                     BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
-                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat));
-                let pattern = if annotation == BindingAnnotation::Unannotated && subpat.is_none() {
+                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+
+                let is_simple_ident_pat =
+                    annotation == BindingAnnotation::Unannotated && subpat.is_none();
+                let (binding, pattern) = if is_simple_ident_pat {
                     // This could also be a single-segment path pattern. To
                     // decide that, we need to try resolving the name.
                     let (resolved, _) = self.expander.def_map.resolve_path(
@@ -834,12 +849,12 @@ impl ExprCollector<'_> {
                         BuiltinShadowMode::Other,
                     );
                     match resolved.take_values() {
-                        Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()),
+                        Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
                         Some(ModuleDefId::EnumVariantId(_)) => {
                             // this is only really valid for unit variants, but
                             // shadowing other enum variants with a pattern is
                             // an error anyway
-                            Pat::Path(name.into())
+                            (None, Pat::Path(name.into()))
                         }
                         Some(ModuleDefId::AdtId(AdtId::StructId(s)))
                             if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
@@ -847,30 +862,34 @@ impl ExprCollector<'_> {
                             // Funnily enough, record structs *can* be shadowed
                             // by pattern bindings (but unit or tuple structs
                             // can't).
-                            Pat::Path(name.into())
+                            (None, Pat::Path(name.into()))
                         }
                         // shadowing statics is an error as well, so we just ignore that case here
-                        _ => Pat::Bind { name, mode: annotation, subpat },
+                        _ => {
+                            let id = binding_list.find(self, name, annotation);
+                            (Some(id), Pat::Bind { id, subpat })
+                        }
                     }
                 } else {
-                    Pat::Bind { name, mode: annotation, subpat }
+                    let id = binding_list.find(self, name, annotation);
+                    (Some(id), Pat::Bind { id, subpat })
                 };
 
                 let ptr = AstPtr::new(&pat);
                 let pat = self.alloc_pat(pattern, Either::Left(ptr));
-                if let Some(key) = key {
-                    self.name_to_pat_grouping.entry(key).or_default().push(pat);
+                if let Some(binding_id) = binding {
+                    self.add_definition_to_binding(binding_id, pat);
                 }
                 return pat;
             }
             ast::Pat::TupleStructPat(p) => {
                 let path =
                     p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields());
+                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
                 Pat::TupleStruct { path, args, ellipsis }
             }
             ast::Pat::RefPat(p) => {
-                let pat = self.collect_pat_opt(p.pat());
+                let pat = self.collect_pat_opt_(p.pat(), binding_list);
                 let mutability = Mutability::from_mutable(p.mut_token().is_some());
                 Pat::Ref { pat, mutability }
             }
@@ -880,13 +899,12 @@ impl ExprCollector<'_> {
                 path.map(Pat::Path).unwrap_or(Pat::Missing)
             }
             ast::Pat::OrPat(p) => {
-                self.is_lowering_inside_or_pat = true;
-                let pats = p.pats().map(|p| self.collect_pat_(p)).collect();
+                let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
                 Pat::Or(pats)
             }
-            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat()),
+            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
             ast::Pat::TuplePat(p) => {
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields());
+                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
                 Pat::Tuple { args, ellipsis }
             }
             ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -899,7 +917,7 @@ impl ExprCollector<'_> {
                     .fields()
                     .filter_map(|f| {
                         let ast_pat = f.pat()?;
-                        let pat = self.collect_pat_(ast_pat);
+                        let pat = self.collect_pat_(ast_pat, binding_list);
                         let name = f.field_name()?.as_name();
                         Some(RecordFieldPat { name, pat })
                     })
@@ -918,9 +936,15 @@ impl ExprCollector<'_> {
 
                 // FIXME properly handle `RestPat`
                 Pat::Slice {
-                    prefix: prefix.into_iter().map(|p| self.collect_pat_(p)).collect(),
-                    slice: slice.map(|p| self.collect_pat_(p)),
-                    suffix: suffix.into_iter().map(|p| self.collect_pat_(p)).collect(),
+                    prefix: prefix
+                        .into_iter()
+                        .map(|p| self.collect_pat_(p, binding_list))
+                        .collect(),
+                    slice: slice.map(|p| self.collect_pat_(p, binding_list)),
+                    suffix: suffix
+                        .into_iter()
+                        .map(|p| self.collect_pat_(p, binding_list))
+                        .collect(),
                 }
             }
             ast::Pat::LiteralPat(lit) => {
@@ -943,7 +967,7 @@ impl ExprCollector<'_> {
                 Pat::Missing
             }
             ast::Pat::BoxPat(boxpat) => {
-                let inner = self.collect_pat_opt_(boxpat.pat());
+                let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
                 Pat::Box { inner }
             }
             ast::Pat::ConstBlockPat(const_block_pat) => {
@@ -960,7 +984,7 @@ impl ExprCollector<'_> {
                     let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
-                            this.collect_pat_opt_(expanded_pat)
+                            this.collect_pat_opt_(expanded_pat, binding_list)
                         });
                     self.source_map.pat_map.insert(src, pat);
                     return pat;
@@ -974,21 +998,25 @@ impl ExprCollector<'_> {
         self.alloc_pat(pattern, Either::Left(ptr))
     }
 
-    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>) -> PatId {
+    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat_(pat),
+            Some(pat) => self.collect_pat_(pat, binding_list),
             None => self.missing_pat(),
         }
     }
 
-    fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Box<[PatId]>, Option<usize>) {
+    fn collect_tuple_pat(
+        &mut self,
+        args: AstChildren<ast::Pat>,
+        binding_list: &mut BindingList,
+    ) -> (Box<[PatId]>, Option<usize>) {
         // Find the location of the `..`, if there is one. Note that we do not
         // consider the possibility of there being multiple `..` here.
         let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
         // We want to skip the `..` pattern here, since we account for it above.
         let args = args
             .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
-            .map(|p| self.collect_pat_(p))
+            .map(|p| self.collect_pat_(p, binding_list))
             .collect();
 
         (args, ellipsis)
@@ -1017,6 +1045,10 @@ impl ExprCollector<'_> {
             None => Some(()),
         }
     }
+
+    fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
+        self.body.bindings[binding_id].definitions.push(pat_id);
+    }
 }
 
 impl From<ast::LiteralKind> for Literal {
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 4b4664a1cf4..f8b159797e4 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -5,7 +5,7 @@ use std::fmt::{self, Write};
 use syntax::ast::HasName;
 
 use crate::{
-    expr::{Array, BindingAnnotation, ClosureKind, Literal, Movability, Statement},
+    expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
     pretty::{print_generic_args, print_path, print_type_ref},
     type_ref::TypeRef,
 };
@@ -292,18 +292,6 @@ impl<'a> Printer<'a> {
                 self.print_expr(*expr);
                 w!(self, "?");
             }
-            Expr::TryBlock { body } => {
-                w!(self, "try ");
-                self.print_expr(*body);
-            }
-            Expr::Async { body } => {
-                w!(self, "async ");
-                self.print_expr(*body);
-            }
-            Expr::Const { body } => {
-                w!(self, "const ");
-                self.print_expr(*body);
-            }
             Expr::Cast { expr, type_ref } => {
                 self.print_expr(*expr);
                 w!(self, " as ");
@@ -402,10 +390,6 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, ")");
             }
-            Expr::Unsafe { body } => {
-                w!(self, "unsafe ");
-                self.print_expr(*body);
-            }
             Expr::Array(arr) => {
                 w!(self, "[");
                 if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
@@ -428,27 +412,49 @@ impl<'a> Printer<'a> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                self.whitespace();
-                if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name);
-                }
-                w!(self, "{{");
-                if !statements.is_empty() || tail.is_some() {
-                    self.indented(|p| {
-                        for stmt in &**statements {
-                            p.print_stmt(stmt);
-                        }
-                        if let Some(tail) = tail {
-                            p.print_expr(*tail);
-                        }
-                        p.newline();
-                    });
-                }
-                w!(self, "}}");
+                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+                self.print_block(label.as_deref(), statements, tail);
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.print_block(Some("unsafe "), statements, tail);
+            }
+            Expr::TryBlock { id: _, statements, tail } => {
+                self.print_block(Some("try "), statements, tail);
+            }
+            Expr::Async { id: _, statements, tail } => {
+                self.print_block(Some("async "), statements, tail);
+            }
+            Expr::Const { id: _, statements, tail } => {
+                self.print_block(Some("const "), statements, tail);
             }
         }
     }
 
+    fn print_block(
+        &mut self,
+        label: Option<&str>,
+        statements: &Box<[Statement]>,
+        tail: &Option<la_arena::Idx<Expr>>,
+    ) {
+        self.whitespace();
+        if let Some(lbl) = label {
+            w!(self, "{}", lbl);
+        }
+        w!(self, "{{");
+        if !statements.is_empty() || tail.is_some() {
+            self.indented(|p| {
+                for stmt in &**statements {
+                    p.print_stmt(stmt);
+                }
+                if let Some(tail) = tail {
+                    p.print_expr(*tail);
+                }
+                p.newline();
+            });
+        }
+        w!(self, "}}");
+    }
+
     fn print_pat(&mut self, pat: PatId) {
         let pat = &self.body[pat];
 
@@ -518,14 +524,8 @@ impl<'a> Printer<'a> {
             }
             Pat::Path(path) => self.print_path(path),
             Pat::Lit(expr) => self.print_expr(*expr),
-            Pat::Bind { mode, name, subpat } => {
-                let mode = match mode {
-                    BindingAnnotation::Unannotated => "",
-                    BindingAnnotation::Mutable => "mut ",
-                    BindingAnnotation::Ref => "ref ",
-                    BindingAnnotation::RefMut => "ref mut ",
-                };
-                w!(self, "{}{}", mode, name);
+            Pat::Bind { id, subpat } => {
+                self.print_binding(*id);
                 if let Some(pat) = subpat {
                     self.whitespace();
                     self.print_pat(*pat);
@@ -629,4 +629,15 @@ impl<'a> Printer<'a> {
     fn print_path(&mut self, path: &Path) {
         print_path(path, self).unwrap();
     }
+
+    fn print_binding(&mut self, id: BindingId) {
+        let Binding { name, mode, .. } = &self.body.bindings[id];
+        let mode = match mode {
+            BindingAnnotation::Unannotated => "",
+            BindingAnnotation::Mutable => "mut ",
+            BindingAnnotation::Ref => "ref ",
+            BindingAnnotation::RefMut => "ref mut ",
+        };
+        w!(self, "{}{}", mode, name);
+    }
 }
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 2617d4288a3..12fc1f116d7 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
+    expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
@@ -23,7 +23,7 @@ pub struct ExprScopes {
 #[derive(Debug, PartialEq, Eq)]
 pub struct ScopeEntry {
     name: Name,
-    pat: PatId,
+    binding: BindingId,
 }
 
 impl ScopeEntry {
@@ -31,8 +31,8 @@ impl ScopeEntry {
         &self.name
     }
 
-    pub fn pat(&self) -> PatId {
-        self.pat
+    pub fn binding(&self) -> BindingId {
+        self.binding
     }
 }
 
@@ -66,6 +66,7 @@ impl ExprScopes {
         self.scopes[scope].label.clone()
     }
 
+    /// Returns the scopes in ascending order.
     pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
         std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
     }
@@ -125,18 +126,23 @@ impl ExprScopes {
         })
     }
 
-    fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
+    fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
+        let Binding { name, .. } = &body.bindings[binding];
+        let entry = ScopeEntry { name: name.clone(), binding };
+        self.scopes[scope].entries.push(entry);
+    }
+
+    fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
         let pattern = &body[pat];
-        if let Pat::Bind { name, .. } = pattern {
-            let entry = ScopeEntry { name: name.clone(), pat };
-            self.scopes[scope].entries.push(entry);
+        if let Pat::Bind { id, .. } = pattern {
+            self.add_bindings(body, scope, *id);
         }
 
-        pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
+        pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
     }
 
     fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
-        params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
+        params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat));
     }
 
     fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
@@ -169,7 +175,7 @@ fn compute_block_scopes(
                 }
 
                 *scope = scopes.new_scope(*scope);
-                scopes.add_bindings(body, *scope, *pat);
+                scopes.add_pat_bindings(body, *scope, *pat);
             }
             Statement::Expr { expr, .. } => {
                 compute_expr_scopes(*expr, body, scopes, scope);
@@ -194,10 +200,20 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             scopes.set_scope(expr, scope);
             compute_block_scopes(statements, *tail, body, scopes, &mut scope);
         }
+        Expr::Unsafe { id, statements, tail }
+        | Expr::Async { id, statements, tail }
+        | Expr::Const { id, statements, tail }
+        | Expr::TryBlock { id, statements, tail } => {
+            let mut scope = scopes.new_block_scope(*scope, *id, None);
+            // Overwrite the old scope for the block expr, so that every block scope can be found
+            // via the block itself (important for blocks that only contain items, no expressions).
+            scopes.set_scope(expr, scope);
+            compute_block_scopes(statements, *tail, body, scopes, &mut scope);
+        }
         Expr::For { iterable, pat, body: body_expr, label } => {
             compute_expr_scopes(*iterable, body, scopes, scope);
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            scopes.add_bindings(body, scope, *pat);
+            scopes.add_pat_bindings(body, scope, *pat);
             compute_expr_scopes(*body_expr, body, scopes, &mut scope);
         }
         Expr::While { condition, body: body_expr, label } => {
@@ -218,7 +234,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             compute_expr_scopes(*expr, body, scopes, scope);
             for arm in arms.iter() {
                 let mut scope = scopes.new_scope(*scope);
-                scopes.add_bindings(body, scope, arm.pat);
+                scopes.add_pat_bindings(body, scope, arm.pat);
                 if let Some(guard) = arm.guard {
                     scope = scopes.new_scope(scope);
                     compute_expr_scopes(guard, body, scopes, &mut scope);
@@ -237,7 +253,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
         &Expr::Let { pat, expr } => {
             compute_expr_scopes(expr, body, scopes, scope);
             *scope = scopes.new_scope(*scope);
-            scopes.add_bindings(body, *scope, pat);
+            scopes.add_pat_bindings(body, *scope, pat);
         }
         e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
     };
@@ -439,7 +455,7 @@ fn foo() {
         let function = find_function(&db, file_id);
 
         let scopes = db.expr_scopes(function.into());
-        let (_body, source_map) = db.body_with_source_map(function.into());
+        let (body, source_map) = db.body_with_source_map(function.into());
 
         let expr_scope = {
             let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
@@ -449,7 +465,9 @@ fn foo() {
         };
 
         let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
-        let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
+        let pat_src = source_map
+            .pat_syntax(*body.bindings[resolved.binding()].definitions.first().unwrap())
+            .unwrap();
 
         let local_name = pat_src.value.either(
             |it| it.syntax_node_ptr().to_node(file.syntax()),
diff --git a/crates/hir-def/src/body/tests/block.rs b/crates/hir-def/src/body/tests/block.rs
index 3bba08cfcce..77ac221e590 100644
--- a/crates/hir-def/src/body/tests/block.rs
+++ b/crates/hir-def/src/body/tests/block.rs
@@ -395,3 +395,25 @@ fn foo() {
         "#]],
     )
 }
+
+#[test]
+fn trailing_expr_macro_expands_stmts() {
+    check_at(
+        r#"
+macro_rules! foo {
+    () => { const FOO: u32 = 0;const BAR: u32 = 0; };
+}
+fn f() {$0
+    foo!{}
+};
+        "#,
+        expect![[r#"
+            block scope
+            BAR: v
+            FOO: v
+
+            crate
+            f: v
+        "#]],
+    )
+}
diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs
index 19d2fe956f0..68b57acca2a 100644
--- a/crates/hir-def/src/child_by_source.rs
+++ b/crates/hir-def/src/child_by_source.rs
@@ -1,7 +1,7 @@
 //! When *constructing* `hir`, we start at some parent syntax node and recursively
 //! lower the children.
 //!
-//! This modules allows one to go in the opposite direction: start with a syntax
+//! This module allows one to go in the opposite direction: start with a syntax
 //! node for a *child*, and get its hir.
 
 use either::Either;
@@ -145,6 +145,7 @@ impl ChildBySource for ItemScope {
                 ModuleDefId::StaticId(id) => insert!(map[keys::STATIC].insert(id)),
                 ModuleDefId::TypeAliasId(id) => insert!(map[keys::TYPE_ALIAS].insert(id)),
                 ModuleDefId::TraitId(id) => insert!(map[keys::TRAIT].insert(id)),
+                ModuleDefId::TraitAliasId(id) => insert!(map[keys::TRAIT_ALIAS].insert(id)),
                 ModuleDefId::AdtId(adt) => match adt {
                     AdtId::StructId(id) => insert!(map[keys::STRUCT].insert(id)),
                     AdtId::UnionId(id) => insert!(map[keys::UNION].insert(id)),
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index c3c1dfd39ac..ee6e269fe55 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -22,7 +22,7 @@ use crate::{
     visibility::RawVisibility,
     AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
     Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId, ProcMacroId,
-    StaticId, TraitId, TypeAliasId, TypeAliasLoc,
+    StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -245,19 +245,11 @@ impl TraitData {
             attrs.by_key("rustc_skip_array_during_method_dispatch").exists();
         let rustc_has_incoherent_inherent_impls =
             attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
-        let (items, attribute_calls, diagnostics) = match &tr_def.items {
-            Some(items) => {
-                let mut collector = AssocItemCollector::new(
-                    db,
-                    module_id,
-                    tree_id.file_id(),
-                    ItemContainerId::TraitId(tr),
-                );
-                collector.collect(&item_tree, tree_id.tree_id(), items);
-                collector.finish()
-            }
-            None => Default::default(),
-        };
+        let mut collector =
+            AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
+        collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
+        let (items, attribute_calls, diagnostics) = collector.finish();
+
         (
             Arc::new(TraitData {
                 name,
@@ -299,6 +291,23 @@ impl TraitData {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraitAliasData {
+    pub name: Name,
+    pub visibility: RawVisibility,
+}
+
+impl TraitAliasData {
+    pub(crate) fn trait_alias_query(db: &dyn DefDatabase, id: TraitAliasId) -> Arc<TraitAliasData> {
+        let loc = id.lookup(db);
+        let item_tree = loc.id.item_tree(db);
+        let alias = &item_tree[loc.id.value];
+        let visibility = item_tree[alias.visibility].clone();
+
+        Arc::new(TraitAliasData { name: alias.name.clone(), visibility })
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImplData {
     pub target_trait: Option<Interned<TraitRef>>,
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index b23427a73b3..270cfa06e58 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -14,7 +14,7 @@ use crate::{
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
         ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
-        TraitData, TypeAliasData,
+        TraitAliasData, TraitData, TypeAliasData,
     },
     generics::GenericParams,
     import_map::ImportMap,
@@ -25,8 +25,8 @@ use crate::{
     AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
     ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
     LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
-    StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc,
-    UnionId, UnionLoc, VariantId,
+    StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
+    TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
 };
 
 #[salsa::query_group(InternDatabaseStorage)]
@@ -46,6 +46,8 @@ pub trait InternDatabase: SourceDatabase {
     #[salsa::interned]
     fn intern_trait(&self, loc: TraitLoc) -> TraitId;
     #[salsa::interned]
+    fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId;
+    #[salsa::interned]
     fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
     #[salsa::interned]
     fn intern_impl(&self, loc: ImplLoc) -> ImplId;
@@ -125,6 +127,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
     fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, Arc<[DefDiagnostic]>);
 
+    #[salsa::invoke(TraitAliasData::trait_alias_query)]
+    fn trait_alias_data(&self, e: TraitAliasId) -> Arc<TraitAliasData>;
+
     #[salsa::invoke(TypeAliasData::type_alias_data_query)]
     fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
 
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 48028b7c6a8..bbea608c55e 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -17,6 +17,7 @@ use std::fmt;
 use hir_expand::name::Name;
 use intern::Interned;
 use la_arena::{Idx, RawIdx};
+use smallvec::SmallVec;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@@ -29,6 +30,8 @@ pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, Unar
 
 pub type ExprId = Idx<Expr>;
 
+pub type BindingId = Idx<Binding>;
+
 /// FIXME: this is a hacky function which should be removed
 pub(crate) fn dummy_expr_id() -> ExprId {
     ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -52,13 +55,21 @@ pub type LabelId = Idx<Label>;
 // We convert float values into bits and that's how we don't need to deal with f32 and f64.
 // For PartialEq, bits comparison should work, as ordering is not important
 // https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
-#[derive(Default, Debug, Clone, Eq, PartialEq)]
+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
 pub struct FloatTypeWrapper(u64);
 
 impl FloatTypeWrapper {
     pub fn new(value: f64) -> Self {
         Self(value.to_bits())
     }
+
+    pub fn into_f64(self) -> f64 {
+        f64::from_bits(self.0)
+    }
+
+    pub fn into_f32(self) -> f32 {
+        f64::from_bits(self.0) as f32
+    }
 }
 
 impl fmt::Display for FloatTypeWrapper {
@@ -101,6 +112,26 @@ pub enum Expr {
         tail: Option<ExprId>,
         label: Option<LabelId>,
     },
+    TryBlock {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Async {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Const {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
+    Unsafe {
+        id: BlockId,
+        statements: Box<[Statement]>,
+        tail: Option<ExprId>,
+    },
     Loop {
         body: ExprId,
         label: Option<LabelId>,
@@ -164,15 +195,6 @@ pub enum Expr {
     Try {
         expr: ExprId,
     },
-    TryBlock {
-        body: ExprId,
-    },
-    Async {
-        body: ExprId,
-    },
-    Const {
-        body: ExprId,
-    },
     Cast {
         expr: ExprId,
         type_ref: Interned<TypeRef>,
@@ -214,9 +236,6 @@ pub enum Expr {
         exprs: Box<[ExprId]>,
         is_assignee_expr: bool,
     },
-    Unsafe {
-        body: ExprId,
-    },
     Array(Array),
     Literal(Literal),
     Underscore,
@@ -282,13 +301,20 @@ impl Expr {
             Expr::Let { expr, .. } => {
                 f(*expr);
             }
-            Expr::Block { statements, tail, .. } => {
+            Expr::Block { statements, tail, .. }
+            | Expr::TryBlock { statements, tail, .. }
+            | Expr::Unsafe { statements, tail, .. }
+            | Expr::Async { statements, tail, .. }
+            | Expr::Const { statements, tail, .. } => {
                 for stmt in statements.iter() {
                     match stmt {
-                        Statement::Let { initializer, .. } => {
+                        Statement::Let { initializer, else_branch, .. } => {
                             if let &Some(expr) = initializer {
                                 f(expr);
                             }
+                            if let &Some(expr) = else_branch {
+                                f(expr);
+                            }
                         }
                         Statement::Expr { expr: expression, .. } => f(*expression),
                     }
@@ -297,10 +323,6 @@ impl Expr {
                     f(expr);
                 }
             }
-            Expr::TryBlock { body }
-            | Expr::Unsafe { body }
-            | Expr::Async { body }
-            | Expr::Const { body } => f(*body),
             Expr::Loop { body, .. } => f(*body),
             Expr::While { condition, body, .. } => {
                 f(*condition);
@@ -414,6 +436,13 @@ impl BindingAnnotation {
     }
 }
 
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Binding {
+    pub name: Name,
+    pub mode: BindingAnnotation,
+    pub definitions: SmallVec<[PatId; 1]>,
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RecordFieldPat {
     pub name: Name,
@@ -432,7 +461,7 @@ pub enum Pat {
     Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
     Path(Box<Path>),
     Lit(ExprId),
-    Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
+    Bind { id: BindingId, subpat: Option<PatId> },
     TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<usize> },
     Ref { pat: PatId, mutability: Mutability },
     Box { inner: PatId },
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index b2ab0c30e03..e4912fa8a64 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -187,6 +187,7 @@ impl GenericParams {
             GenericDefId::AdtId(AdtId::EnumId(id)) => id_to_generics!(id),
             GenericDefId::AdtId(AdtId::UnionId(id)) => id_to_generics!(id),
             GenericDefId::TraitId(id) => id_to_generics!(id),
+            GenericDefId::TraitAliasId(id) => id_to_generics!(id),
             GenericDefId::TypeAliasId(id) => id_to_generics!(id),
             GenericDefId::ImplId(id) => id_to_generics!(id),
             GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
@@ -207,12 +208,10 @@ impl GenericParams {
     pub(crate) fn fill_bounds(
         &mut self,
         lower_ctx: &LowerCtx<'_>,
-        node: &dyn ast::HasTypeBounds,
+        type_bounds: Option<ast::TypeBoundList>,
         target: Either<TypeRef, LifetimeRef>,
     ) {
-        for bound in
-            node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
-        {
+        for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
             self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
         }
     }
@@ -233,7 +232,11 @@ impl GenericParams {
                     };
                     self.type_or_consts.alloc(param.into());
                     let type_ref = TypeRef::Path(name.into());
-                    self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref));
+                    self.fill_bounds(
+                        lower_ctx,
+                        type_param.type_bound_list(),
+                        Either::Left(type_ref),
+                    );
                 }
                 ast::TypeOrConstParam::Const(const_param) => {
                     let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -255,7 +258,11 @@ impl GenericParams {
             let param = LifetimeParamData { name: name.clone() };
             self.lifetimes.alloc(param);
             let lifetime_ref = LifetimeRef::new_name(name);
-            self.fill_bounds(lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
+            self.fill_bounds(
+                lower_ctx,
+                lifetime_param.type_bound_list(),
+                Either::Right(lifetime_ref),
+            );
         }
     }
 
@@ -421,6 +428,10 @@ fn file_id_and_params_of(
             let src = it.lookup(db).source(db);
             (src.file_id, src.value.generic_param_list())
         }
+        GenericDefId::TraitAliasId(it) => {
+            let src = it.lookup(db).source(db);
+            (src.file_id, src.value.generic_param_list())
+        }
         GenericDefId::TypeAliasId(it) => {
             let src = it.lookup(db).source(db);
             (src.file_id, src.value.generic_param_list())
@@ -435,7 +446,7 @@ fn file_id_and_params_of(
 }
 
 impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
-    type Value = Either<ast::TypeOrConstParam, ast::Trait>;
+    type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
     fn child_source(
         &self,
         db: &dyn DefDatabase,
@@ -447,11 +458,20 @@ impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
 
         let mut params = ArenaMap::default();
 
-        // For traits the first type index is `Self`, we need to add it before the other params.
-        if let GenericDefId::TraitId(id) = *self {
-            let trait_ref = id.lookup(db).source(db).value;
-            let idx = idx_iter.next().unwrap();
-            params.insert(idx, Either::Right(trait_ref));
+        // For traits and trait aliases the first type index is `Self`, we need to add it before
+        // the other params.
+        match *self {
+            GenericDefId::TraitId(id) => {
+                let trait_ref = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref)));
+            }
+            GenericDefId::TraitAliasId(id) => {
+                let alias = id.lookup(db).source(db).value;
+                let idx = idx_iter.next().unwrap();
+                params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias)));
+            }
+            _ => {}
         }
 
         if let Some(generic_params_list) = generic_params_list {
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 1ce191942ec..4f1f6000db0 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -264,6 +264,7 @@ pub enum ImportKind {
     Const,
     Static,
     Trait,
+    TraitAlias,
     TypeAlias,
     BuiltinType,
     AssociatedItem,
@@ -459,6 +460,7 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
         ModuleDefId::ConstId(_) => ImportKind::Const,
         ModuleDefId::StaticId(_) => ImportKind::Static,
         ModuleDefId::TraitId(_) => ImportKind::Trait,
+        ModuleDefId::TraitAliasId(_) => ImportKind::TraitAlias,
         ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
         ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
         ModuleDefId::MacroId(_) => ImportKind::Macro,
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 53a4173ff42..991e447033f 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -431,6 +431,7 @@ impl PerNs {
             ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
             ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
             ModuleDefId::TraitId(_) => PerNs::types(def, v),
+            ModuleDefId::TraitAliasId(_) => PerNs::types(def, v),
             ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
             ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
             ModuleDefId::MacroId(mac) => PerNs::macros(mac, v),
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 19d01630ef0..9da5b2d47c8 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -204,6 +204,7 @@ impl ItemTree {
                 consts,
                 statics,
                 traits,
+                trait_aliases,
                 impls,
                 type_aliases,
                 mods,
@@ -226,6 +227,7 @@ impl ItemTree {
             consts.shrink_to_fit();
             statics.shrink_to_fit();
             traits.shrink_to_fit();
+            trait_aliases.shrink_to_fit();
             impls.shrink_to_fit();
             type_aliases.shrink_to_fit();
             mods.shrink_to_fit();
@@ -276,6 +278,7 @@ struct ItemTreeData {
     consts: Arena<Const>,
     statics: Arena<Static>,
     traits: Arena<Trait>,
+    trait_aliases: Arena<TraitAlias>,
     impls: Arena<Impl>,
     type_aliases: Arena<TypeAlias>,
     mods: Arena<Mod>,
@@ -496,6 +499,7 @@ mod_items! {
     Const in consts -> ast::Const,
     Static in statics -> ast::Static,
     Trait in traits -> ast::Trait,
+    TraitAlias in trait_aliases -> ast::TraitAlias,
     Impl in impls -> ast::Impl,
     TypeAlias in type_aliases -> ast::TypeAlias,
     Mod in mods -> ast::Module,
@@ -672,11 +676,18 @@ pub struct Trait {
     pub generic_params: Interned<GenericParams>,
     pub is_auto: bool,
     pub is_unsafe: bool,
-    /// This is [`None`] if this Trait is a trait alias.
-    pub items: Option<Box<[AssocItem]>>,
+    pub items: Box<[AssocItem]>,
     pub ast_id: FileAstId<ast::Trait>,
 }
 
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct TraitAlias {
+    pub name: Name,
+    pub visibility: RawVisibilityId,
+    pub generic_params: Interned<GenericParams>,
+    pub ast_id: FileAstId<ast::TraitAlias>,
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Impl {
     pub generic_params: Interned<GenericParams>,
@@ -872,6 +883,7 @@ impl ModItem {
             | ModItem::Enum(_)
             | ModItem::Static(_)
             | ModItem::Trait(_)
+            | ModItem::TraitAlias(_)
             | ModItem::Impl(_)
             | ModItem::Mod(_)
             | ModItem::MacroRules(_)
@@ -899,6 +911,7 @@ impl ModItem {
             ModItem::Const(it) => tree[it.index].ast_id().upcast(),
             ModItem::Static(it) => tree[it.index].ast_id().upcast(),
             ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
+            ModItem::TraitAlias(it) => tree[it.index].ast_id().upcast(),
             ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
             ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
             ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index d4d3c5ef19a..77b186f8e3f 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -3,7 +3,7 @@
 use std::{collections::hash_map::Entry, sync::Arc};
 
 use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
-use syntax::ast::{self, HasModuleItem};
+use syntax::ast::{self, HasModuleItem, HasTypeBounds};
 
 use crate::{
     generics::{GenericParams, TypeParamData, TypeParamProvenance},
@@ -90,6 +90,13 @@ impl<'a> Ctx<'a> {
                 _ => None,
             })
             .collect();
+        if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
+            if let Some(call) = expr.macro_call() {
+                if let Some(mod_item) = self.lower_mod_item(&call.into()) {
+                    self.tree.top_level.push(mod_item);
+                }
+            }
+        }
 
         self.tree
     }
@@ -110,6 +117,7 @@ impl<'a> Ctx<'a> {
             ast::Item::Const(ast) => self.lower_const(ast).into(),
             ast::Item::Module(ast) => self.lower_module(ast)?.into(),
             ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
+            ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(),
             ast::Item::Impl(ast) => self.lower_impl(ast)?.into(),
             ast::Item::Use(ast) => self.lower_use(ast)?.into(),
             ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
@@ -147,7 +155,7 @@ impl<'a> Ctx<'a> {
     fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
         let visibility = self.lower_visibility(strukt);
         let name = strukt.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
         let fields = self.lower_fields(&strukt.kind());
         let ast_id = self.source_ast_id_map.ast_id(strukt);
         let res = Struct { name, visibility, generic_params, fields, ast_id };
@@ -211,7 +219,7 @@ impl<'a> Ctx<'a> {
     fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
         let visibility = self.lower_visibility(union);
         let name = union.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
         let fields = match union.record_field_list() {
             Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
             None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
@@ -224,7 +232,7 @@ impl<'a> Ctx<'a> {
     fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> {
         let visibility = self.lower_visibility(enum_);
         let name = enum_.name()?.as_name();
-        let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
         let variants = match &enum_.variant_list() {
             Some(variant_list) => self.lower_variants(variant_list),
             None => IdxRange::new(self.next_variant_idx()..self.next_variant_idx()),
@@ -372,8 +380,7 @@ impl<'a> Ctx<'a> {
             ast_id,
             flags,
         };
-        res.explicit_generic_params =
-            self.lower_generic_params(GenericsOwner::Function(&res), func);
+        res.explicit_generic_params = self.lower_generic_params(HasImplicitSelf::No, func);
 
         Some(id(self.data().functions.alloc(res)))
     }
@@ -386,7 +393,7 @@ impl<'a> Ctx<'a> {
         let type_ref = type_alias.ty().map(|it| self.lower_type_ref(&it));
         let visibility = self.lower_visibility(type_alias);
         let bounds = self.lower_type_bounds(type_alias);
-        let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias);
         let ast_id = self.source_ast_id_map.ast_id(type_alias);
         let res = TypeAlias {
             name,
@@ -442,27 +449,49 @@ impl<'a> Ctx<'a> {
     fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<FileItemTreeId<Trait>> {
         let name = trait_def.name()?.as_name();
         let visibility = self.lower_visibility(trait_def);
-        let generic_params = self.lower_generic_params(GenericsOwner::Trait(trait_def), trait_def);
+        let generic_params =
+            self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
         let is_auto = trait_def.auto_token().is_some();
         let is_unsafe = trait_def.unsafe_token().is_some();
-        let items = trait_def.assoc_item_list().map(|list| {
-            list.assoc_items()
-                .filter_map(|item| {
-                    let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
-                    self.lower_assoc_item(&item).map(|item| {
-                        self.add_attrs(ModItem::from(item).into(), attrs);
-                        item
-                    })
-                })
-                .collect()
-        });
         let ast_id = self.source_ast_id_map.ast_id(trait_def);
-        let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
-        Some(id(self.data().traits.alloc(res)))
+
+        let items = trait_def
+            .assoc_item_list()
+            .into_iter()
+            .flat_map(|list| list.assoc_items())
+            .filter_map(|item| {
+                let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
+                self.lower_assoc_item(&item).map(|item| {
+                    self.add_attrs(ModItem::from(item).into(), attrs);
+                    item
+                })
+            })
+            .collect();
+
+        let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
+        Some(id(self.data().traits.alloc(def)))
+    }
+
+    fn lower_trait_alias(
+        &mut self,
+        trait_alias_def: &ast::TraitAlias,
+    ) -> Option<FileItemTreeId<TraitAlias>> {
+        let name = trait_alias_def.name()?.as_name();
+        let visibility = self.lower_visibility(trait_alias_def);
+        let generic_params = self.lower_generic_params(
+            HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
+            trait_alias_def,
+        );
+        let ast_id = self.source_ast_id_map.ast_id(trait_alias_def);
+
+        let alias = TraitAlias { name, visibility, generic_params, ast_id };
+        Some(id(self.data().trait_aliases.alloc(alias)))
     }
 
     fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
-        let generic_params = self.lower_generic_params(GenericsOwner::Impl, impl_def);
+        // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
+        // type alias rather than a type parameter, so this is handled by the resolver.
+        let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
         // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
         // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
         // equals itself.
@@ -566,42 +595,29 @@ impl<'a> Ctx<'a> {
 
     fn lower_generic_params(
         &mut self,
-        owner: GenericsOwner<'_>,
+        has_implicit_self: HasImplicitSelf,
         node: &dyn ast::HasGenericParams,
     ) -> Interned<GenericParams> {
         let mut generics = GenericParams::default();
-        match owner {
-            GenericsOwner::Function(_)
-            | GenericsOwner::Struct
-            | GenericsOwner::Enum
-            | GenericsOwner::Union
-            | GenericsOwner::TypeAlias => {
-                generics.fill(&self.body_ctx, node);
-            }
-            GenericsOwner::Trait(trait_def) => {
-                // traits get the Self type as an implicit first type parameter
-                generics.type_or_consts.alloc(
-                    TypeParamData {
-                        name: Some(name![Self]),
-                        default: None,
-                        provenance: TypeParamProvenance::TraitSelf,
-                    }
-                    .into(),
-                );
-                // add super traits as bounds on Self
-                // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
-                let self_param = TypeRef::Path(name![Self].into());
-                generics.fill_bounds(&self.body_ctx, trait_def, Either::Left(self_param));
-                generics.fill(&self.body_ctx, node);
-            }
-            GenericsOwner::Impl => {
-                // Note that we don't add `Self` here: in `impl`s, `Self` is not a
-                // type-parameter, but rather is a type-alias for impl's target
-                // type, so this is handled by the resolver.
-                generics.fill(&self.body_ctx, node);
-            }
+
+        if let HasImplicitSelf::Yes(bounds) = has_implicit_self {
+            // Traits and trait aliases get the Self type as an implicit first type parameter.
+            generics.type_or_consts.alloc(
+                TypeParamData {
+                    name: Some(name![Self]),
+                    default: None,
+                    provenance: TypeParamProvenance::TraitSelf,
+                }
+                .into(),
+            );
+            // add super traits as bounds on Self
+            // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar`
+            let self_param = TypeRef::Path(name![Self].into());
+            generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
         }
 
+        generics.fill(&self.body_ctx, node);
+
         generics.shrink_to_fit();
         Interned::new(generics)
     }
@@ -673,17 +689,10 @@ fn desugar_future_path(orig: TypeRef) -> Path {
     Path::from_known_path(path, generic_args)
 }
 
-enum GenericsOwner<'a> {
-    /// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
-    /// position.
-    Function(&'a Function),
-    Struct,
-    Enum,
-    Union,
-    /// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
-    Trait(&'a ast::Trait),
-    TypeAlias,
-    Impl,
+enum HasImplicitSelf {
+    /// Inner list is a type bound list for the implicit `Self`.
+    Yes(Option<ast::TypeBoundList>),
+    No,
 }
 
 fn lower_abi(abi: ast::Abi) -> Interned<str> {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 8f230b87d01..5f29997964b 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -374,23 +374,24 @@ impl<'a> Printer<'a> {
                 }
                 w!(self, "trait {}", name);
                 self.print_generic_params(generic_params);
-                match items {
-                    Some(items) => {
-                        self.print_where_clause_and_opening_brace(generic_params);
-                        self.indented(|this| {
-                            for item in &**items {
-                                this.print_mod_item((*item).into());
-                            }
-                        });
+                self.print_where_clause_and_opening_brace(generic_params);
+                self.indented(|this| {
+                    for item in &**items {
+                        this.print_mod_item((*item).into());
                     }
-                    None => {
-                        w!(self, " = ");
-                        // FIXME: Print the aliased traits
-                        self.print_where_clause_and_opening_brace(generic_params);
-                    }
-                }
+                });
                 wln!(self, "}}");
             }
+            ModItem::TraitAlias(it) => {
+                let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
+                self.print_visibility(*visibility);
+                w!(self, "trait {}", name);
+                self.print_generic_params(generic_params);
+                w!(self, " = ");
+                self.print_where_clause(generic_params);
+                w!(self, ";");
+                wln!(self);
+            }
             ModItem::Impl(it) => {
                 let Impl { target_trait, self_ty, is_negative, items, generic_params, ast_id: _ } =
                     &self.tree[it];
diff --git a/crates/hir-def/src/keys.rs b/crates/hir-def/src/keys.rs
index 72beec8186c..f30be6b64e3 100644
--- a/crates/hir-def/src/keys.rs
+++ b/crates/hir-def/src/keys.rs
@@ -9,8 +9,8 @@ use syntax::{ast, AstNode, AstPtr};
 use crate::{
     dyn_map::{DynMap, Policy},
     ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id,
-    MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
-    UnionId,
+    MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+    TypeOrConstParamId, UnionId,
 };
 
 pub type Key<K, V> = crate::dyn_map::Key<K, V, AstPtrPolicy<K, V>>;
@@ -21,6 +21,7 @@ pub const STATIC: Key<ast::Static, StaticId> = Key::new();
 pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new();
 pub const IMPL: Key<ast::Impl, ImplId> = Key::new();
 pub const TRAIT: Key<ast::Trait, TraitId> = Key::new();
+pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new();
 pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
 pub const UNION: Key<ast::Union, UnionId> = Key::new();
 pub const ENUM: Key<ast::Enum, EnumId> = Key::new();
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index ab9bc615daf..4096e0a3826 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -181,15 +181,15 @@ impl LangItems {
         T: Into<AttrDefId> + Copy,
     {
         let _p = profile::span("collect_lang_item");
-        if let Some(lang_item) = lang_attr(db, item).and_then(|it| LangItem::from_str(&it)) {
+        if let Some(lang_item) = lang_attr(db, item) {
             self.items.entry(lang_item).or_insert_with(|| constructor(item));
         }
     }
 }
 
-pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
+pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<LangItem> {
     let attrs = db.attrs(item.into());
-    attrs.by_key("lang").string_value().cloned()
+    attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it))
 }
 
 pub enum GenericRequirement {
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index d07c5fb67c6..8c2e93f0905 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -86,7 +86,7 @@ use crate::{
     builtin_type::BuiltinType,
     item_tree::{
         Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
-        Static, Struct, Trait, TypeAlias, Union,
+        Static, Struct, Trait, TraitAlias, TypeAlias, Union,
     },
 };
 
@@ -128,7 +128,7 @@ impl ModuleId {
     }
 }
 
-/// An ID of a module, **local** to a specific crate
+/// An ID of a module, **local** to a `DefMap`.
 pub type LocalModuleId = Idx<nameres::ModuleData>;
 
 #[derive(Debug)]
@@ -261,6 +261,11 @@ pub struct TraitId(salsa::InternId);
 pub type TraitLoc = ItemLoc<Trait>;
 impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TraitAliasId(salsa::InternId);
+pub type TraitAliasLoc = ItemLoc<TraitAlias>;
+impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias);
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeAliasId(salsa::InternId);
 type TypeAliasLoc = AssocItemLoc<TypeAlias>;
@@ -453,6 +458,7 @@ pub enum ModuleDefId {
     ConstId(ConstId),
     StaticId(StaticId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     BuiltinType(BuiltinType),
     MacroId(MacroId),
@@ -466,6 +472,7 @@ impl_from!(
     ConstId,
     StaticId,
     TraitId,
+    TraitAliasId,
     TypeAliasId,
     BuiltinType
     for ModuleDefId
@@ -516,6 +523,7 @@ pub enum GenericDefId {
     FunctionId(FunctionId),
     AdtId(AdtId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     ImplId(ImplId),
     // enum variants cannot have generics themselves, but their parent enums
@@ -528,6 +536,7 @@ impl_from!(
     FunctionId,
     AdtId(StructId, EnumId, UnionId),
     TraitId,
+    TraitAliasId,
     TypeAliasId,
     ImplId,
     EnumVariantId,
@@ -555,6 +564,7 @@ pub enum AttrDefId {
     StaticId(StaticId),
     ConstId(ConstId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     TypeAliasId(TypeAliasId),
     MacroId(MacroId),
     ImplId(ImplId),
@@ -714,6 +724,7 @@ impl HasModule for GenericDefId {
             GenericDefId::FunctionId(it) => it.lookup(db).module(db),
             GenericDefId::AdtId(it) => it.module(db),
             GenericDefId::TraitId(it) => it.lookup(db).container,
+            GenericDefId::TraitAliasId(it) => it.lookup(db).container,
             GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
             GenericDefId::ImplId(it) => it.lookup(db).container,
             GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container,
@@ -747,6 +758,7 @@ impl ModuleDefId {
             ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
             ModuleDefId::StaticId(id) => id.lookup(db).module(db),
             ModuleDefId::TraitId(id) => id.lookup(db).container,
+            ModuleDefId::TraitAliasId(id) => id.lookup(db).container,
             ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
             ModuleDefId::MacroId(id) => id.module(db),
             ModuleDefId::BuiltinType(_) => return None,
@@ -765,6 +777,7 @@ impl AttrDefId {
             AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
             AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
             AttrDefId::TraitId(it) => it.lookup(db).container.krate,
+            AttrDefId::TraitAliasId(it) => it.lookup(db).container.krate,
             AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
             AttrDefId::ImplId(it) => it.lookup(db).container.krate,
             AttrDefId::ExternBlockId(it) => it.lookup(db).container.krate,
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index bb45266725c..0b72ca1eec1 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -143,7 +143,7 @@ macro_rules! assert {
 
 fn main() {
      {
-        if !true {
+        if !(true ) {
             $crate::panic!("{} {:?}", arg1(a, b, c), arg2);
         }
     };
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 8358a46f0a9..b663a291789 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -827,6 +827,7 @@ macro_rules! rgb_color {
 /* parse error: expected type */
 /* parse error: expected R_PAREN */
 /* parse error: expected R_ANGLE */
+/* parse error: expected `::` */
 /* parse error: expected COMMA */
 /* parse error: expected R_ANGLE */
 /* parse error: expected SEMICOLON */
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index f42b0079d76..7d7240e7e8c 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -342,7 +342,7 @@ impl DefMap {
     }
 
     pub(crate) fn block_id(&self) -> Option<BlockId> {
-        self.block.as_ref().map(|block| block.block)
+        self.block.map(|block| block.block)
     }
 
     pub(crate) fn prelude(&self) -> Option<ModuleId> {
@@ -354,7 +354,7 @@ impl DefMap {
     }
 
     pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
-        let block = self.block.as_ref().map(|b| b.block);
+        let block = self.block.map(|b| b.block);
         ModuleId { krate: self.krate, local_id, block }
     }
 
@@ -428,9 +428,9 @@ impl DefMap {
     /// Returns the module containing `local_mod`, either the parent `mod`, or the module containing
     /// the block, if `self` corresponds to a block expression.
     pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
-        match &self[local_mod].parent {
-            Some(parent) => Some(self.module_id(*parent)),
-            None => self.block.as_ref().map(|block| block.parent),
+        match self[local_mod].parent {
+            Some(parent) => Some(self.module_id(parent)),
+            None => self.block.map(|block| block.parent),
         }
     }
 
@@ -440,11 +440,11 @@ impl DefMap {
         let mut buf = String::new();
         let mut arc;
         let mut current_map = self;
-        while let Some(block) = &current_map.block {
+        while let Some(block) = current_map.block {
             go(&mut buf, current_map, "block scope", current_map.root);
             buf.push('\n');
             arc = block.parent.def_map(db);
-            current_map = &*arc;
+            current_map = &arc;
         }
         go(&mut buf, current_map, "crate", current_map.root);
         return buf;
@@ -468,10 +468,10 @@ impl DefMap {
         let mut buf = String::new();
         let mut arc;
         let mut current_map = self;
-        while let Some(block) = &current_map.block {
+        while let Some(block) = current_map.block {
             format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
             arc = block.parent.def_map(db);
-            current_map = &*arc;
+            current_map = &arc;
         }
 
         format_to!(buf, "crate scope\n");
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 4b39a20d86c..70acc3442c3 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -51,7 +51,8 @@ use crate::{
     AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
     FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
     MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId,
-    ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro,
+    ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
+    UnresolvedMacro,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -666,8 +667,10 @@ impl DefCollector<'_> {
         macro_: Macro2Id,
         vis: &RawVisibility,
     ) {
-        let vis =
-            self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
+        let vis = self
+            .def_map
+            .resolve_visibility(self.db, module_id, vis, false)
+            .unwrap_or(Visibility::Public);
         self.def_map.modules[module_id].scope.declare(macro_.into());
         self.update(
             module_id,
@@ -831,7 +834,7 @@ impl DefCollector<'_> {
         let mut def = directive.status.namespaces();
         let vis = self
             .def_map
-            .resolve_visibility(self.db, module_id, &directive.import.visibility)
+            .resolve_visibility(self.db, module_id, &directive.import.visibility, false)
             .unwrap_or(Visibility::Public);
 
         match import.kind {
@@ -1547,7 +1550,7 @@ impl ModCollector<'_, '_> {
                 };
             let resolve_vis = |def_map: &DefMap, visibility| {
                 def_map
-                    .resolve_visibility(db, self.module_id, visibility)
+                    .resolve_visibility(db, self.module_id, visibility, false)
                     .unwrap_or(Visibility::Public)
             };
 
@@ -1707,6 +1710,20 @@ impl ModCollector<'_, '_> {
                         false,
                     );
                 }
+                ModItem::TraitAlias(id) => {
+                    let it = &self.item_tree[id];
+
+                    let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
+                    update_def(
+                        self.def_collector,
+                        TraitAliasLoc { container: module, id: ItemTreeId::new(self.tree_id, id) }
+                            .intern(db)
+                            .into(),
+                        &it.name,
+                        vis,
+                        false,
+                    );
+                }
                 ModItem::TypeAlias(id) => {
                     let it = &self.item_tree[id];
 
@@ -1823,7 +1840,7 @@ impl ModCollector<'_, '_> {
     ) -> LocalModuleId {
         let def_map = &mut self.def_collector.def_map;
         let vis = def_map
-            .resolve_visibility(self.def_collector.db, self.module_id, visibility)
+            .resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
             .unwrap_or(Visibility::Public);
         let modules = &mut def_map.modules;
         let origin = match definition {
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 1d9d5cccded..25478481dd0 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -78,6 +78,7 @@ impl DefMap {
         // pub(path)
         //     ^^^^ this
         visibility: &RawVisibility,
+        within_impl: bool,
     ) -> Option<Visibility> {
         let mut vis = match visibility {
             RawVisibility::Module(path) => {
@@ -102,7 +103,8 @@ impl DefMap {
         // `super` to its parent (etc.). However, visibilities must only refer to a module in the
         // DefMap they're written in, so we restrict them when that happens.
         if let Visibility::Module(m) = vis {
-            if self.block_id() != m.block {
+            // ...unless we're resolving visibility for an associated item in an impl.
+            if self.block_id() != m.block && !within_impl {
                 cov_mark::hit!(adjust_vis_in_block_def_map);
                 vis = Visibility::Module(self.module_id(self.root()));
                 tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index f5190b76db0..13e6825f821 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -223,6 +223,7 @@ pub type Ty = ();
                     ModuleDefId::ConstId(it) => drop(db.const_data(it)),
                     ModuleDefId::StaticId(it) => drop(db.static_data(it)),
                     ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
+                    ModuleDefId::TraitAliasId(it) => drop(db.trait_alias_data(it)),
                     ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
                     ModuleDefId::EnumVariantId(_)
                     | ModuleDefId::ModuleId(_)
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 36d4c36a268..f3197d1800f 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -8,7 +8,7 @@ use std::{
 
 use crate::{
     body::LowerCtx,
-    type_ref::{ConstScalarOrPath, LifetimeRef},
+    type_ref::{ConstRefOrPath, LifetimeRef},
 };
 use hir_expand::name::Name;
 use intern::Interned;
@@ -85,7 +85,7 @@ pub struct AssociatedTypeBinding {
 pub enum GenericArg {
     Type(TypeRef),
     Lifetime(LifetimeRef),
-    Const(ConstScalarOrPath),
+    Const(ConstRefOrPath),
 }
 
 impl Path {
diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index c85a11db6d1..b7542bd777d 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -2,7 +2,7 @@
 
 use std::iter;
 
-use crate::type_ref::ConstScalarOrPath;
+use crate::type_ref::ConstRefOrPath;
 
 use either::Either;
 use hir_expand::name::{name, AsName};
@@ -212,7 +212,7 @@ pub(super) fn lower_generic_args(
                 }
             }
             ast::GenericArg::ConstArg(arg) => {
-                let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
+                let arg = ConstRefOrPath::from_expr_opt(arg.expr());
                 args.push(GenericArg::Const(arg))
             }
         }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 86958e3daea..61e64fc1036 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
 //! Name resolution façade.
-use std::{hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault, sync::Arc};
 
 use base_db::CrateId;
 use hir_expand::name::{name, Name};
@@ -12,7 +12,7 @@ use crate::{
     body::scope::{ExprScopes, ScopeId},
     builtin_type::BuiltinType,
     db::DefDatabase,
-    expr::{ExprId, LabelId, PatId},
+    expr::{BindingId, ExprId, LabelId},
     generics::{GenericParams, TypeOrConstParamData},
     item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
     nameres::DefMap,
@@ -22,7 +22,8 @@ use crate::{
     AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
     FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
     LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId,
-    StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, VariantId,
+    StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+    VariantId,
 };
 
 #[derive(Debug, Clone)]
@@ -35,19 +36,34 @@ pub struct Resolver {
     module_scope: ModuleItemMap,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 struct ModuleItemMap {
     def_map: Arc<DefMap>,
     module_id: LocalModuleId,
 }
 
-#[derive(Debug, Clone)]
+impl fmt::Debug for ModuleItemMap {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ModuleItemMap").field("module_id", &self.module_id).finish()
+    }
+}
+
+#[derive(Clone)]
 struct ExprScope {
     owner: DefWithBodyId,
     expr_scopes: Arc<ExprScopes>,
     scope_id: ScopeId,
 }
 
+impl fmt::Debug for ExprScope {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ExprScope")
+            .field("owner", &self.owner)
+            .field("scope_id", &self.scope_id)
+            .finish()
+    }
+}
+
 #[derive(Debug, Clone)]
 enum Scope {
     /// All the items and imported names of a module
@@ -74,6 +90,7 @@ pub enum TypeNs {
     TypeAliasId(TypeAliasId),
     BuiltinType(BuiltinType),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     // Module belong to type ns, but the resolver is used when all module paths
     // are fully resolved.
     // ModuleId(ModuleId)
@@ -85,10 +102,10 @@ pub enum ResolveValueResult {
     Partial(TypeNs, usize),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum ValueNs {
     ImplSelf(ImplId),
-    LocalBinding(PatId),
+    LocalBinding(BindingId),
     FunctionId(FunctionId),
     ConstId(ConstId),
     StaticId(StaticId),
@@ -214,10 +231,12 @@ impl Resolver {
         db: &dyn DefDatabase,
         visibility: &RawVisibility,
     ) -> Option<Visibility> {
+        let within_impl =
+            self.scopes().find(|scope| matches!(scope, Scope::ImplDefScope(_))).is_some();
         match visibility {
             RawVisibility::Module(_) => {
                 let (item_map, module) = self.item_scope();
-                item_map.resolve_visibility(db, module, visibility)
+                item_map.resolve_visibility(db, module, visibility, within_impl)
             }
             RawVisibility::Public => Some(Visibility::Public),
         }
@@ -236,69 +255,81 @@ impl Resolver {
             return self.module_scope.resolve_path_in_value_ns(db, path);
         }
 
-        for scope in self.scopes() {
-            match scope {
-                Scope::ExprScope(_) if n_segments > 1 => continue,
-                Scope::ExprScope(scope) => {
-                    let entry = scope
-                        .expr_scopes
-                        .entries(scope.scope_id)
-                        .iter()
-                        .find(|entry| entry.name() == first_name);
+        if n_segments <= 1 {
+            for scope in self.scopes() {
+                match scope {
+                    Scope::ExprScope(scope) => {
+                        let entry = scope
+                            .expr_scopes
+                            .entries(scope.scope_id)
+                            .iter()
+                            .find(|entry| entry.name() == first_name);
 
-                    if let Some(e) = entry {
-                        return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.pat())));
+                        if let Some(e) = entry {
+                            return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(
+                                e.binding(),
+                            )));
+                        }
+                    }
+                    Scope::GenericParams { params, def } => {
+                        if let Some(id) = params.find_const_by_name(first_name, *def) {
+                            let val = ValueNs::GenericParam(id);
+                            return Some(ResolveValueResult::ValueNs(val));
+                        }
+                    }
+                    &Scope::ImplDefScope(impl_) => {
+                        if first_name == &name![Self] {
+                            return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)));
+                        }
+                    }
+                    // bare `Self` doesn't work in the value namespace in a struct/enum definition
+                    Scope::AdtScope(_) => continue,
+                    Scope::BlockScope(m) => {
+                        if let Some(def) = m.resolve_path_in_value_ns(db, path) {
+                            return Some(def);
+                        }
                     }
                 }
-                Scope::GenericParams { params, def } if n_segments > 1 => {
-                    if let Some(id) = params.find_type_by_name(first_name, *def) {
-                        let ty = TypeNs::GenericParam(id);
-                        return Some(ResolveValueResult::Partial(ty, 1));
+            }
+        } else {
+            for scope in self.scopes() {
+                match scope {
+                    Scope::ExprScope(_) => continue,
+                    Scope::GenericParams { params, def } => {
+                        if let Some(id) = params.find_type_by_name(first_name, *def) {
+                            let ty = TypeNs::GenericParam(id);
+                            return Some(ResolveValueResult::Partial(ty, 1));
+                        }
                     }
-                }
-                Scope::GenericParams { .. } if n_segments != 1 => continue,
-                Scope::GenericParams { params, def } => {
-                    if let Some(id) = params.find_const_by_name(first_name, *def) {
-                        let val = ValueNs::GenericParam(id);
-                        return Some(ResolveValueResult::ValueNs(val));
+                    &Scope::ImplDefScope(impl_) => {
+                        if first_name == &name![Self] {
+                            return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1));
+                        }
                     }
-                }
-
-                &Scope::ImplDefScope(impl_) => {
-                    if first_name == &name![Self] {
-                        return Some(if n_segments > 1 {
-                            ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1)
-                        } else {
-                            ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_))
-                        });
+                    Scope::AdtScope(adt) => {
+                        if first_name == &name![Self] {
+                            let ty = TypeNs::AdtSelfType(*adt);
+                            return Some(ResolveValueResult::Partial(ty, 1));
+                        }
                     }
-                }
-                // bare `Self` doesn't work in the value namespace in a struct/enum definition
-                Scope::AdtScope(_) if n_segments == 1 => continue,
-                Scope::AdtScope(adt) => {
-                    if first_name == &name![Self] {
-                        let ty = TypeNs::AdtSelfType(*adt);
-                        return Some(ResolveValueResult::Partial(ty, 1));
-                    }
-                }
-
-                Scope::BlockScope(m) => {
-                    if let Some(def) = m.resolve_path_in_value_ns(db, path) {
-                        return Some(def);
+                    Scope::BlockScope(m) => {
+                        if let Some(def) = m.resolve_path_in_value_ns(db, path) {
+                            return Some(def);
+                        }
                     }
                 }
             }
         }
 
-        if let res @ Some(_) = self.module_scope.resolve_path_in_value_ns(db, path) {
-            return res;
+        if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
+            return Some(res);
         }
 
         // If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
         // to resolving to the primitive type, to allow this to still work in the presence of
         // `use core::u16;`.
-        if path.kind == PathKind::Plain && path.segments().len() > 1 {
-            if let Some(builtin) = BuiltinType::by_name(&path.segments()[0]) {
+        if path.kind == PathKind::Plain && n_segments > 1 {
+            if let Some(builtin) = BuiltinType::by_name(first_name) {
                 return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1));
             }
         }
@@ -400,6 +431,8 @@ impl Resolver {
     }
 
     pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
+        // FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
+        // aliased traits are NOT brought in scope (unless also aliased).
         let mut traits = FxHashSet::default();
 
         for scope in self.scopes() {
@@ -428,6 +461,15 @@ impl Resolver {
         traits
     }
 
+    pub fn traits_in_scope_from_block_scopes(&self) -> impl Iterator<Item = TraitId> + '_ {
+        self.scopes()
+            .filter_map(|scope| match scope {
+                Scope::BlockScope(m) => Some(m.def_map[m.module_id].scope.traits()),
+                _ => None,
+            })
+            .flatten()
+    }
+
     pub fn module(&self) -> ModuleId {
         let (def_map, local_id) = self.item_scope();
         def_map.module_id(local_id)
@@ -459,14 +501,85 @@ impl Resolver {
         })
     }
 
+    pub fn generic_params(&self) -> Option<&Interned<GenericParams>> {
+        self.scopes().find_map(|scope| match scope {
+            Scope::GenericParams { params, .. } => Some(params),
+            _ => None,
+        })
+    }
+
     pub fn body_owner(&self) -> Option<DefWithBodyId> {
         self.scopes().find_map(|scope| match scope {
             Scope::ExprScope(it) => Some(it.owner),
             _ => None,
         })
     }
+    /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
+    #[must_use]
+    pub fn update_to_inner_scope(
+        &mut self,
+        db: &dyn DefDatabase,
+        owner: DefWithBodyId,
+        expr_id: ExprId,
+    ) -> UpdateGuard {
+        #[inline(always)]
+        fn append_expr_scope(
+            db: &dyn DefDatabase,
+            resolver: &mut Resolver,
+            owner: DefWithBodyId,
+            expr_scopes: &Arc<ExprScopes>,
+            scope_id: ScopeId,
+        ) {
+            resolver.scopes.push(Scope::ExprScope(ExprScope {
+                owner,
+                expr_scopes: expr_scopes.clone(),
+                scope_id,
+            }));
+            if let Some(block) = expr_scopes.block(scope_id) {
+                if let Some(def_map) = db.block_def_map(block) {
+                    let root = def_map.root();
+                    resolver
+                        .scopes
+                        .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
+                    // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+                    // already traverses all parents, so this is O(n²). I think we could only store the
+                    // innermost module scope instead?
+                }
+            }
+        }
+
+        let start = self.scopes.len();
+        let innermost_scope = self.scopes().next();
+        match innermost_scope {
+            Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
+                let expr_scopes = expr_scopes.clone();
+                let scope_chain = expr_scopes
+                    .scope_chain(expr_scopes.scope_for(expr_id))
+                    .take_while(|&it| it != scope_id);
+                for scope_id in scope_chain {
+                    append_expr_scope(db, self, owner, &expr_scopes, scope_id);
+                }
+            }
+            _ => {
+                let expr_scopes = db.expr_scopes(owner);
+                let scope_chain = expr_scopes.scope_chain(expr_scopes.scope_for(expr_id));
+
+                for scope_id in scope_chain {
+                    append_expr_scope(db, self, owner, &expr_scopes, scope_id);
+                }
+            }
+        }
+        self.scopes[start..].reverse();
+        UpdateGuard(start)
+    }
+
+    pub fn reset_to_guard(&mut self, UpdateGuard(start): UpdateGuard) {
+        self.scopes.truncate(start);
+    }
 }
 
+pub struct UpdateGuard(usize);
+
 impl Resolver {
     fn scopes(&self) -> impl Iterator<Item = &Scope> {
         self.scopes.iter().rev()
@@ -504,7 +617,7 @@ pub enum ScopeDef {
     ImplSelfType(ImplId),
     AdtSelfType(AdtId),
     GenericParam(GenericParamId),
-    Local(PatId),
+    Local(BindingId),
     Label(LabelId),
 }
 
@@ -556,17 +669,18 @@ impl Scope {
                     acc.add(&name, ScopeDef::Label(label))
                 }
                 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
-                    acc.add_local(e.name(), e.pat());
+                    acc.add_local(e.name(), e.binding());
                 });
             }
         }
     }
 }
 
-// needs arbitrary_self_types to be a method... or maybe move to the def?
 pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver {
+    let r = owner.resolver(db);
     let scopes = db.expr_scopes(owner);
-    resolver_for_scope(db, owner, scopes.scope_for(expr_id))
+    let scope_id = scopes.scope_for(expr_id);
+    resolver_for_scope_(db, scopes, scope_id, r, owner)
 }
 
 pub fn resolver_for_scope(
@@ -574,8 +688,18 @@ pub fn resolver_for_scope(
     owner: DefWithBodyId,
     scope_id: Option<ScopeId>,
 ) -> Resolver {
-    let mut r = owner.resolver(db);
+    let r = owner.resolver(db);
     let scopes = db.expr_scopes(owner);
+    resolver_for_scope_(db, scopes, scope_id, r, owner)
+}
+
+fn resolver_for_scope_(
+    db: &dyn DefDatabase,
+    scopes: Arc<ExprScopes>,
+    scope_id: Option<ScopeId>,
+    mut r: Resolver,
+    owner: DefWithBodyId,
+) -> Resolver {
     let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
     r.scopes.reserve(scope_chain.len());
 
@@ -641,6 +765,7 @@ impl ModuleItemMap {
                 let ty = match module_def.take_types()? {
                     ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
                     ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
+                    ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
                     ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
                     ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
 
@@ -678,6 +803,7 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
 
         ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_))
         | ModuleDefId::TraitId(_)
+        | ModuleDefId::TraitAliasId(_)
         | ModuleDefId::TypeAliasId(_)
         | ModuleDefId::BuiltinType(_)
         | ModuleDefId::MacroId(_)
@@ -695,6 +821,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
         ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
 
         ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
+        ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
 
         ModuleDefId::FunctionId(_)
         | ModuleDefId::ConstId(_)
@@ -732,7 +859,7 @@ impl ScopeNames {
             self.add(name, ScopeDef::Unknown)
         }
     }
-    fn add_local(&mut self, name: &Name, pat: PatId) {
+    fn add_local(&mut self, name: &Name, binding: BindingId) {
         let set = self.map.entry(name.clone()).or_default();
         // XXX: hack, account for local (and only local) shadowing.
         //
@@ -743,7 +870,7 @@ impl ScopeNames {
             cov_mark::hit!(shadowing_shows_single_completion);
             return;
         }
-        set.push(ScopeDef::Local(pat))
+        set.push(ScopeDef::Local(binding))
     }
 }
 
@@ -779,6 +906,12 @@ impl HasResolver for TraitId {
     }
 }
 
+impl HasResolver for TraitAliasId {
+    fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+        self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
+    }
+}
+
 impl<T: Into<AdtId> + Copy> HasResolver for T {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         let def = self.into();
@@ -858,6 +991,7 @@ impl HasResolver for GenericDefId {
             GenericDefId::FunctionId(inner) => inner.resolver(db),
             GenericDefId::AdtId(adt) => adt.resolver(db),
             GenericDefId::TraitId(inner) => inner.resolver(db),
+            GenericDefId::TraitAliasId(inner) => inner.resolver(db),
             GenericDefId::TypeAliasId(inner) => inner.resolver(db),
             GenericDefId::ImplId(inner) => inner.resolver(db),
             GenericDefId::EnumVariantId(inner) => inner.parent.resolver(db),
diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs
index 9652b01b91b..8e30f429a9f 100644
--- a/crates/hir-def/src/type_ref.rs
+++ b/crates/hir-def/src/type_ref.rs
@@ -116,7 +116,7 @@ pub enum TypeRef {
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
     // FIXME: for full const generics, the latter element (length) here is going to have to be an
     // expression that is further lowered later in hir_ty.
-    Array(Box<TypeRef>, ConstScalarOrPath),
+    Array(Box<TypeRef>, ConstRefOrPath),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -188,7 +188,7 @@ impl TypeRef {
                 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
                 // `hir_ty` level, which would allow knowing the type of:
                 // let v: [u8; 2 + 2] = [0u8; 4];
-                let len = ConstScalarOrPath::from_expr_opt(inner.expr());
+                let len = ConstRefOrPath::from_expr_opt(inner.expr());
                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
             }
             ast::Type::SliceType(inner) => {
@@ -378,25 +378,25 @@ impl TypeBound {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstScalarOrPath {
-    Scalar(ConstScalar),
+pub enum ConstRefOrPath {
+    Scalar(ConstRef),
     Path(Name),
 }
 
-impl std::fmt::Display for ConstScalarOrPath {
+impl std::fmt::Display for ConstRefOrPath {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            ConstScalarOrPath::Scalar(s) => s.fmt(f),
-            ConstScalarOrPath::Path(n) => n.fmt(f),
+            ConstRefOrPath::Scalar(s) => s.fmt(f),
+            ConstRefOrPath::Path(n) => n.fmt(f),
         }
     }
 }
 
-impl ConstScalarOrPath {
+impl ConstRefOrPath {
     pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
         match expr {
             Some(x) => Self::from_expr(x),
-            None => Self::Scalar(ConstScalar::Unknown),
+            None => Self::Scalar(ConstRef::Unknown),
         }
     }
 
@@ -407,7 +407,7 @@ impl ConstScalarOrPath {
             ast::Expr::PathExpr(p) => {
                 match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
                     Some(x) => Self::Path(x.as_name()),
-                    None => Self::Scalar(ConstScalar::Unknown),
+                    None => Self::Scalar(ConstRef::Unknown),
                 }
             }
             ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
@@ -415,8 +415,8 @@ impl ConstScalarOrPath {
                     let unsigned = Self::from_expr_opt(prefix_expr.expr());
                     // Add sign
                     match unsigned {
-                        Self::Scalar(ConstScalar::UInt(num)) => {
-                            Self::Scalar(ConstScalar::Int(-(num as i128)))
+                        Self::Scalar(ConstRef::UInt(num)) => {
+                            Self::Scalar(ConstRef::Int(-(num as i128)))
                         }
                         other => other,
                     }
@@ -425,22 +425,22 @@ impl ConstScalarOrPath {
             },
             ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
                 ast::LiteralKind::IntNumber(num) => {
-                    num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
+                    num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
                 }
                 ast::LiteralKind::Char(c) => {
-                    c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
+                    c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
                 }
-                ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
-                _ => ConstScalar::Unknown,
+                ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
+                _ => ConstRef::Unknown,
             }),
-            _ => Self::Scalar(ConstScalar::Unknown),
+            _ => Self::Scalar(ConstRef::Unknown),
         }
     }
 }
 
 /// A concrete constant value
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ConstScalar {
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ConstRef {
     Int(i128),
     UInt(u128),
     Bool(bool),
@@ -454,18 +454,18 @@ pub enum ConstScalar {
     Unknown,
 }
 
-impl ConstScalar {
+impl ConstRef {
     pub fn builtin_type(&self) -> BuiltinType {
         match self {
-            ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
-            ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
-            ConstScalar::Char(_) => BuiltinType::Char,
-            ConstScalar::Bool(_) => BuiltinType::Bool,
+            ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
+            ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+            ConstRef::Char(_) => BuiltinType::Char,
+            ConstRef::Bool(_) => BuiltinType::Bool,
         }
     }
 }
 
-impl From<Literal> for ConstScalar {
+impl From<Literal> for ConstRef {
     fn from(literal: Literal) -> Self {
         match literal {
             Literal::Char(c) => Self::Char(c),
@@ -477,14 +477,14 @@ impl From<Literal> for ConstScalar {
     }
 }
 
-impl std::fmt::Display for ConstScalar {
+impl std::fmt::Display for ConstRef {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
         match self {
-            ConstScalar::Int(num) => num.fmt(f),
-            ConstScalar::UInt(num) => num.fmt(f),
-            ConstScalar::Bool(flag) => flag.fmt(f),
-            ConstScalar::Char(c) => write!(f, "'{c}'"),
-            ConstScalar::Unknown => f.write_char('_'),
+            ConstRef::Int(num) => num.fmt(f),
+            ConstRef::UInt(num) => num.fmt(f),
+            ConstRef::Bool(flag) => flag.fmt(f),
+            ConstRef::Char(c) => write!(f, "'{c}'"),
+            ConstRef::Unknown => f.write_char('_'),
         }
     }
 }
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 087268a9ece..c9fcaae56cf 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -11,7 +11,7 @@ use crate::{
     nameres::DefMap,
     path::{ModPath, PathKind},
     resolver::HasResolver,
-    ConstId, FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
+    ConstId, FunctionId, HasModule, LocalFieldId, LocalModuleId, ModuleId, VariantId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -120,7 +120,7 @@ impl Visibility {
         self,
         db: &dyn DefDatabase,
         def_map: &DefMap,
-        mut from_module: crate::LocalModuleId,
+        mut from_module: LocalModuleId,
     ) -> bool {
         let mut to_module = match self {
             Visibility::Module(m) => m,
@@ -142,7 +142,8 @@ impl Visibility {
                 arc = to_module.def_map(db);
                 &arc
             };
-        let is_block_root = matches!(to_module.block, Some(_) if to_module_def_map[to_module.local_id].parent.is_none());
+        let is_block_root =
+            to_module.block.is_some() && to_module_def_map[to_module.local_id].parent.is_none();
         if is_block_root {
             to_module = to_module_def_map.containing_module(to_module.local_id).unwrap();
         }
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 9f3fa73d4e6..295083a37f2 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -206,7 +206,7 @@ fn assert_expand(
             let cond = cond.clone();
             let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
             quote! {{
-                if !#cond {
+                if !(#cond) {
                     #DOLLAR_CRATE::panic!(##panic_args);
                 }
             }}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index a52716cc02c..39fc08ecdcf 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -771,6 +771,15 @@ impl<T> InFile<Option<T>> {
     }
 }
 
+impl<L, R> InFile<Either<L, R>> {
+    pub fn transpose(self) -> Either<InFile<L>, InFile<R>> {
+        match self.value {
+            Either::Left(l) => Either::Left(InFile::new(self.file_id, l)),
+            Either::Right(r) => Either::Right(InFile::new(self.file_id, r)),
+        }
+    }
+}
+
 impl<'a> InFile<&'a SyntaxNode> {
     pub fn ancestors_with_macros(
         self,
@@ -806,7 +815,7 @@ impl<'a> InFile<&'a SyntaxNode> {
     /// Falls back to the macro call range if the node cannot be mapped up fully.
     ///
     /// For attributes and derives, this will point back to the attribute only.
-    /// For the entire item `InFile::use original_file_range_full`.
+    /// For the entire item use [`InFile::original_file_range_full`].
     pub fn original_file_range(self, db: &dyn db::AstDatabase) -> FileRange {
         match self.file_id.repr() {
             HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
@@ -821,6 +830,21 @@ impl<'a> InFile<&'a SyntaxNode> {
         }
     }
 
+    /// Falls back to the macro call range if the node cannot be mapped up fully.
+    pub fn original_file_range_full(self, db: &dyn db::AstDatabase) -> FileRange {
+        match self.file_id.repr() {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                if let Some(res) = self.original_file_range_opt(db) {
+                    return res;
+                }
+                // Fall back to whole macro call.
+                let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                loc.kind.original_call_range_with_body(db)
+            }
+        }
+    }
+
     /// Attempts to map the syntax node back up its macro calls.
     pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
         match ascend_node_border_tokens(db, self) {
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index a8b8d5222e4..4572e33486f 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -18,6 +18,7 @@ arrayvec = "0.7.2"
 bitflags = "1.3.2"
 smallvec.workspace = true
 ena = "0.14.0"
+either = "1.7.0"
 tracing = "0.1.35"
 rustc-hash = "1.1.0"
 scoped-tls = "1.0.0"
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs
index 8faef7bf71e..03e9443599d 100644
--- a/crates/hir-ty/src/builder.rs
+++ b/crates/hir-ty/src/builder.rs
@@ -152,6 +152,15 @@ impl TyBuilder<()> {
         TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner)
     }
 
+    // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well
+    pub fn discr_ty() -> Ty {
+        TyKind::Scalar(chalk_ir::Scalar::Int(chalk_ir::IntTy::I128)).intern(Interner)
+    }
+
+    pub fn bool() -> Ty {
+        TyKind::Scalar(chalk_ir::Scalar::Bool).intern(Interner)
+    }
+
     pub fn usize() -> Ty {
         TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner)
     }
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 6989e9fb9be..28ae4c349f8 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -540,8 +540,7 @@ pub(crate) fn trait_datum_query(
     let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
     let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
     let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
-    let well_known = lang_attr(db.upcast(), trait_)
-        .and_then(|name| well_known_trait_from_lang_item(LangItem::from_str(&name)?));
+    let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item);
     let trait_datum = TraitDatum {
         id: trait_id,
         binders: make_binders(db, &generic_params, trait_datum_bound),
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index 45c975dfcdc..e6aefbf2716 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -13,7 +13,7 @@ use crate::{
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
     from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
     CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
-    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
+    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
 };
 
 pub trait TyExt {
@@ -22,6 +22,7 @@ pub trait TyExt {
     fn is_floating_point(&self) -> bool;
     fn is_never(&self) -> bool;
     fn is_unknown(&self) -> bool;
+    fn contains_unknown(&self) -> bool;
     fn is_ty_var(&self) -> bool;
 
     fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
@@ -76,6 +77,10 @@ impl TyExt for Ty {
         matches!(self.kind(Interner), TyKind::Error)
     }
 
+    fn contains_unknown(&self) -> bool {
+        self.data(Interner).flags.contains(TypeFlags::HAS_ERROR)
+    }
+
     fn is_ty_var(&self) -> bool {
         matches!(self.kind(Interner), TyKind::InferenceVar(_, _))
     }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 8df70330fa9..5830c48988f 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -1,30 +1,25 @@
 //! Constant evaluation details
 
-use std::{
-    collections::HashMap,
-    fmt::{Display, Write},
-};
-
-use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
+use base_db::CrateId;
+use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
 use hir_def::{
-    builtin_type::BuiltinInt,
-    expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
+    expr::Expr,
     path::ModPath,
-    resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
-    src::HasChildSource,
-    type_ref::ConstScalar,
-    ConstId, DefWithBodyId, EnumVariantId, Lookup,
+    resolver::{Resolver, ValueNs},
+    type_ref::ConstRef,
+    ConstId, EnumVariantId,
 };
-use la_arena::{Arena, Idx, RawIdx};
+use la_arena::{Idx, RawIdx};
 use stdx::never;
-use syntax::ast::HasName;
 
 use crate::{
-    db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
-    utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty,
-    TyBuilder, TyKind,
+    db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
+    to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
+    Interner, MemoryMap, Ty, TyBuilder,
 };
 
+use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
+
 /// Extension trait for [`Const`]
 pub trait ConstExt {
     /// Is a [`Const`] unknown?
@@ -53,346 +48,24 @@ impl ConstExt for Const {
     }
 }
 
-pub struct ConstEvalCtx<'a> {
-    pub db: &'a dyn HirDatabase,
-    pub owner: DefWithBodyId,
-    pub exprs: &'a Arena<Expr>,
-    pub pats: &'a Arena<Pat>,
-    pub local_data: HashMap<PatId, ComputedExpr>,
-    infer: &'a InferenceResult,
-}
-
-impl ConstEvalCtx<'_> {
-    fn expr_ty(&mut self, expr: ExprId) -> Ty {
-        self.infer[expr].clone()
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ConstEvalError {
-    NotSupported(&'static str),
-    SemanticError(&'static str),
-    Loop,
-    IncompleteExpr,
-    Panic(String),
+    MirLowerError(MirLowerError),
+    MirEvalError(MirEvalError),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ComputedExpr {
-    Literal(Literal),
-    Enum(String, EnumVariantId, Literal),
-    Tuple(Box<[ComputedExpr]>),
-}
-
-impl Display for ComputedExpr {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ComputedExpr::Literal(l) => match l {
-                Literal::Int(x, _) => {
-                    if *x >= 10 {
-                        write!(f, "{x} ({x:#X})")
-                    } else {
-                        x.fmt(f)
-                    }
-                }
-                Literal::Uint(x, _) => {
-                    if *x >= 10 {
-                        write!(f, "{x} ({x:#X})")
-                    } else {
-                        x.fmt(f)
-                    }
-                }
-                Literal::Float(x, _) => x.fmt(f),
-                Literal::Bool(x) => x.fmt(f),
-                Literal::Char(x) => std::fmt::Debug::fmt(x, f),
-                Literal::String(x) => std::fmt::Debug::fmt(x, f),
-                Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
-            },
-            ComputedExpr::Enum(name, _, _) => name.fmt(f),
-            ComputedExpr::Tuple(t) => {
-                f.write_char('(')?;
-                for x in &**t {
-                    x.fmt(f)?;
-                    f.write_str(", ")?;
-                }
-                f.write_char(')')
-            }
+impl From<MirLowerError> for ConstEvalError {
+    fn from(value: MirLowerError) -> Self {
+        match value {
+            MirLowerError::ConstEvalError(e) => *e,
+            _ => ConstEvalError::MirLowerError(value),
         }
     }
 }
 
-fn scalar_max(scalar: &Scalar) -> i128 {
-    match scalar {
-        Scalar::Bool => 1,
-        Scalar::Char => u32::MAX as i128,
-        Scalar::Int(x) => match x {
-            IntTy::Isize => isize::MAX as i128,
-            IntTy::I8 => i8::MAX as i128,
-            IntTy::I16 => i16::MAX as i128,
-            IntTy::I32 => i32::MAX as i128,
-            IntTy::I64 => i64::MAX as i128,
-            IntTy::I128 => i128::MAX,
-        },
-        Scalar::Uint(x) => match x {
-            chalk_ir::UintTy::Usize => usize::MAX as i128,
-            chalk_ir::UintTy::U8 => u8::MAX as i128,
-            chalk_ir::UintTy::U16 => u16::MAX as i128,
-            chalk_ir::UintTy::U32 => u32::MAX as i128,
-            chalk_ir::UintTy::U64 => u64::MAX as i128,
-            chalk_ir::UintTy::U128 => i128::MAX, // ignore too big u128 for now
-        },
-        Scalar::Float(_) => 0,
-    }
-}
-
-fn is_valid(scalar: &Scalar, value: i128) -> bool {
-    if value < 0 {
-        !matches!(scalar, Scalar::Uint(_)) && -scalar_max(scalar) - 1 <= value
-    } else {
-        value <= scalar_max(scalar)
-    }
-}
-
-fn get_name(ctx: &mut ConstEvalCtx<'_>, variant: EnumVariantId) -> String {
-    let loc = variant.parent.lookup(ctx.db.upcast());
-    let children = variant.parent.child_source(ctx.db.upcast());
-    let item_tree = loc.id.item_tree(ctx.db.upcast());
-
-    let variant_name = children.value[variant.local_id].name();
-    let enum_name = item_tree[loc.id.value].name.to_string();
-    enum_name + "::" + &variant_name.unwrap().to_string()
-}
-
-pub fn eval_const(
-    expr_id: ExprId,
-    ctx: &mut ConstEvalCtx<'_>,
-) -> Result<ComputedExpr, ConstEvalError> {
-    let u128_to_i128 = |it: u128| -> Result<i128, ConstEvalError> {
-        it.try_into().map_err(|_| ConstEvalError::NotSupported("u128 is too big"))
-    };
-
-    let expr = &ctx.exprs[expr_id];
-    match expr {
-        Expr::Missing => match ctx.owner {
-            // evaluate the implicit variant index of an enum variant without expression
-            // FIXME: This should return the type of the enum representation
-            DefWithBodyId::VariantId(variant) => {
-                let prev_idx: u32 = variant.local_id.into_raw().into();
-                let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
-                let value = match prev_idx {
-                    Some(local_id) => {
-                        let prev_variant = EnumVariantId { local_id, parent: variant.parent };
-                        1 + match ctx.db.const_eval_variant(prev_variant)? {
-                            ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                            ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                            _ => {
-                                return Err(ConstEvalError::NotSupported(
-                                    "Enum can't contain this kind of value",
-                                ))
-                            }
-                        }
-                    }
-                    _ => 0,
-                };
-                Ok(ComputedExpr::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
-            }
-            _ => Err(ConstEvalError::IncompleteExpr),
-        },
-        Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
-        &Expr::UnaryOp { expr, op } => {
-            let ty = &ctx.expr_ty(expr);
-            let ev = eval_const(expr, ctx)?;
-            match op {
-                hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
-                hir_def::expr::UnaryOp::Not => {
-                    let v = match ev {
-                        ComputedExpr::Literal(Literal::Bool(b)) => {
-                            return Ok(ComputedExpr::Literal(Literal::Bool(!b)))
-                        }
-                        ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                        ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                        _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-                    };
-                    let r = match ty.kind(Interner) {
-                        TyKind::Scalar(Scalar::Uint(x)) => match x {
-                            chalk_ir::UintTy::U8 => !(v as u8) as i128,
-                            chalk_ir::UintTy::U16 => !(v as u16) as i128,
-                            chalk_ir::UintTy::U32 => !(v as u32) as i128,
-                            chalk_ir::UintTy::U64 => !(v as u64) as i128,
-                            chalk_ir::UintTy::U128 => {
-                                return Err(ConstEvalError::NotSupported("negation of u128"))
-                            }
-                            chalk_ir::UintTy::Usize => !(v as usize) as i128,
-                        },
-                        TyKind::Scalar(Scalar::Int(x)) => match x {
-                            chalk_ir::IntTy::I8 => !(v as i8) as i128,
-                            chalk_ir::IntTy::I16 => !(v as i16) as i128,
-                            chalk_ir::IntTy::I32 => !(v as i32) as i128,
-                            chalk_ir::IntTy::I64 => !(v as i64) as i128,
-                            chalk_ir::IntTy::I128 => !v,
-                            chalk_ir::IntTy::Isize => !(v as isize) as i128,
-                        },
-                        _ => return Err(ConstEvalError::NotSupported("unreachable?")),
-                    };
-                    Ok(ComputedExpr::Literal(Literal::Int(r, None)))
-                }
-                hir_def::expr::UnaryOp::Neg => {
-                    let v = match ev {
-                        ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                        ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                        _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-                    };
-                    Ok(ComputedExpr::Literal(Literal::Int(
-                        v.checked_neg().ok_or_else(|| {
-                            ConstEvalError::Panic("overflow in negation".to_string())
-                        })?,
-                        None,
-                    )))
-                }
-            }
-        }
-        &Expr::BinaryOp { lhs, rhs, op } => {
-            let ty = &ctx.expr_ty(lhs);
-            let lhs = eval_const(lhs, ctx)?;
-            let rhs = eval_const(rhs, ctx)?;
-            let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
-            let v1 = match lhs {
-                ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-            };
-            let v2 = match rhs {
-                ComputedExpr::Literal(Literal::Int(v, _)) => v,
-                ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
-                _ => return Err(ConstEvalError::NotSupported("this kind of operator")),
-            };
-            match op {
-                BinaryOp::ArithOp(b) => {
-                    let panic_arith = ConstEvalError::Panic(
-                        "attempt to run invalid arithmetic operation".to_string(),
-                    );
-                    let r = match b {
-                        ArithOp::Add => v1.checked_add(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Mul => v1.checked_mul(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Sub => v1.checked_sub(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Div => v1.checked_div(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Rem => v1.checked_rem(v2).ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Shl => v1
-                            .checked_shl(v2.try_into().map_err(|_| panic_arith.clone())?)
-                            .ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::Shr => v1
-                            .checked_shr(v2.try_into().map_err(|_| panic_arith.clone())?)
-                            .ok_or_else(|| panic_arith.clone())?,
-                        ArithOp::BitXor => v1 ^ v2,
-                        ArithOp::BitOr => v1 | v2,
-                        ArithOp::BitAnd => v1 & v2,
-                    };
-                    if let TyKind::Scalar(s) = ty.kind(Interner) {
-                        if !is_valid(s, r) {
-                            return Err(panic_arith);
-                        }
-                    }
-                    Ok(ComputedExpr::Literal(Literal::Int(r, None)))
-                }
-                BinaryOp::LogicOp(_) => Err(ConstEvalError::SemanticError("logic op on numbers")),
-                _ => Err(ConstEvalError::NotSupported("bin op on this operators")),
-            }
-        }
-        Expr::Block { statements, tail, .. } => {
-            let mut prev_values = HashMap::<PatId, Option<ComputedExpr>>::default();
-            for statement in &**statements {
-                match *statement {
-                    hir_def::expr::Statement::Let { pat: pat_id, initializer, .. } => {
-                        let pat = &ctx.pats[pat_id];
-                        match pat {
-                            Pat::Bind { subpat, .. } if subpat.is_none() => (),
-                            _ => {
-                                return Err(ConstEvalError::NotSupported("complex patterns in let"))
-                            }
-                        };
-                        let value = match initializer {
-                            Some(x) => eval_const(x, ctx)?,
-                            None => continue,
-                        };
-                        if !prev_values.contains_key(&pat_id) {
-                            let prev = ctx.local_data.insert(pat_id, value);
-                            prev_values.insert(pat_id, prev);
-                        } else {
-                            ctx.local_data.insert(pat_id, value);
-                        }
-                    }
-                    hir_def::expr::Statement::Expr { .. } => {
-                        return Err(ConstEvalError::NotSupported("this kind of statement"))
-                    }
-                }
-            }
-            let r = match tail {
-                &Some(x) => eval_const(x, ctx),
-                None => Ok(ComputedExpr::Tuple(Box::new([]))),
-            };
-            // clean up local data, so caller will receive the exact map that passed to us
-            for (name, val) in prev_values {
-                match val {
-                    Some(x) => ctx.local_data.insert(name, x),
-                    None => ctx.local_data.remove(&name),
-                };
-            }
-            r
-        }
-        Expr::Path(p) => {
-            let resolver = resolver_for_expr(ctx.db.upcast(), ctx.owner, expr_id);
-            let pr = resolver
-                .resolve_path_in_value_ns(ctx.db.upcast(), p.mod_path())
-                .ok_or(ConstEvalError::SemanticError("unresolved path"))?;
-            let pr = match pr {
-                ResolveValueResult::ValueNs(v) => v,
-                ResolveValueResult::Partial(..) => {
-                    return match ctx
-                        .infer
-                        .assoc_resolutions_for_expr(expr_id)
-                        .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
-                        .0
-                    {
-                        hir_def::AssocItemId::FunctionId(_) => {
-                            Err(ConstEvalError::NotSupported("assoc function"))
-                        }
-                        // FIXME use actual impl for trait assoc const
-                        hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c),
-                        hir_def::AssocItemId::TypeAliasId(_) => {
-                            Err(ConstEvalError::NotSupported("assoc type alias"))
-                        }
-                    };
-                }
-            };
-            match pr {
-                ValueNs::LocalBinding(pat_id) => {
-                    let r = ctx
-                        .local_data
-                        .get(&pat_id)
-                        .ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?;
-                    Ok(r.clone())
-                }
-                ValueNs::ConstId(id) => ctx.db.const_eval(id),
-                ValueNs::GenericParam(_) => {
-                    Err(ConstEvalError::NotSupported("const generic without substitution"))
-                }
-                ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
-                    ComputedExpr::Literal(lit) => {
-                        Ok(ComputedExpr::Enum(get_name(ctx, id), id, lit))
-                    }
-                    _ => Err(ConstEvalError::NotSupported(
-                        "Enums can't evalute to anything but numbers",
-                    )),
-                },
-                _ => Err(ConstEvalError::NotSupported("path that are not const or local")),
-            }
-        }
-        // FIXME: Handle the cast target
-        &Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
-            ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
-            _ => Err(ConstEvalError::NotSupported("Can't cast these types")),
-        },
-        _ => Err(ConstEvalError::NotSupported("This kind of expression")),
+impl From<MirEvalError> for ConstEvalError {
+    fn from(value: MirEvalError) -> Self {
+        ConstEvalError::MirEvalError(value)
     }
 }
 
@@ -449,68 +122,102 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
         .intern(Interner)
 }
 
+/// Interns a constant scalar with the given type
+pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+    let bytes = match value {
+        ConstRef::Int(i) => {
+            // FIXME: We should handle failure of layout better.
+            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
+        }
+        ConstRef::UInt(i) => {
+            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
+        }
+        ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
+        ConstRef::Char(c) => {
+            ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
+        }
+        ConstRef::Unknown => ConstScalar::Unknown,
+    };
+    intern_const_scalar(bytes, ty)
+}
+
 /// Interns a possibly-unknown target usize
-pub fn usize_const(value: Option<u128>) -> Const {
-    intern_const_scalar(value.map_or(ConstScalar::Unknown, ConstScalar::UInt), TyBuilder::usize())
+pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
+    intern_const_ref(
+        db,
+        &value.map_or(ConstRef::Unknown, ConstRef::UInt),
+        TyBuilder::usize(),
+        krate,
+    )
+}
+
+pub fn try_const_usize(c: &Const) -> Option<u128> {
+    match &c.data(Interner).value {
+        chalk_ir::ConstValue::BoundVar(_) => None,
+        chalk_ir::ConstValue::InferenceVar(_) => None,
+        chalk_ir::ConstValue::Placeholder(_) => None,
+        chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+            ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
+            _ => None,
+        },
+    }
 }
 
 pub(crate) fn const_eval_recover(
     _: &dyn HirDatabase,
     _: &[String],
     _: &ConstId,
-) -> Result<ComputedExpr, ConstEvalError> {
-    Err(ConstEvalError::Loop)
+) -> Result<Const, ConstEvalError> {
+    Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
 }
 
-pub(crate) fn const_eval_variant_recover(
+pub(crate) fn const_eval_discriminant_recover(
     _: &dyn HirDatabase,
     _: &[String],
     _: &EnumVariantId,
-) -> Result<ComputedExpr, ConstEvalError> {
-    Err(ConstEvalError::Loop)
+) -> Result<i128, ConstEvalError> {
+    Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
 }
 
-pub(crate) fn const_eval_variant_query(
+pub(crate) fn const_eval_query(
     db: &dyn HirDatabase,
     const_id: ConstId,
-) -> Result<ComputedExpr, ConstEvalError> {
+) -> Result<Const, ConstEvalError> {
     let def = const_id.into();
-    let body = db.body(def);
-    let infer = &db.infer(def);
-    let result = eval_const(
-        body.body_expr,
-        &mut ConstEvalCtx {
-            db,
-            owner: const_id.into(),
-            exprs: &body.exprs,
-            pats: &body.pats,
-            local_data: HashMap::default(),
-            infer,
-        },
-    );
-    result
+    let body = db.mir_body(def)?;
+    let c = interpret_mir(db, &body, false)?;
+    Ok(c)
 }
 
-pub(crate) fn const_eval_query_variant(
+pub(crate) fn const_eval_discriminant_variant(
     db: &dyn HirDatabase,
     variant_id: EnumVariantId,
-) -> Result<ComputedExpr, ConstEvalError> {
+) -> Result<i128, ConstEvalError> {
     let def = variant_id.into();
     let body = db.body(def);
-    let infer = &db.infer(def);
-    eval_const(
-        body.body_expr,
-        &mut ConstEvalCtx {
-            db,
-            owner: def,
-            exprs: &body.exprs,
-            pats: &body.pats,
-            local_data: HashMap::default(),
-            infer,
-        },
-    )
+    if body.exprs[body.body_expr] == Expr::Missing {
+        let prev_idx: u32 = variant_id.local_id.into_raw().into();
+        let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
+        let value = match prev_idx {
+            Some(local_id) => {
+                let prev_variant = EnumVariantId { local_id, parent: variant_id.parent };
+                1 + db.const_eval_discriminant(prev_variant)?
+            }
+            _ => 0,
+        };
+        return Ok(value);
+    }
+    let mir_body = db.mir_body(def)?;
+    let c = interpret_mir(db, &mir_body, false)?;
+    let c = try_const_usize(&c).unwrap() as i128;
+    Ok(c)
 }
 
+// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
+// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
+// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
 pub(crate) fn eval_to_const(
     expr: Idx<Expr>,
     mode: ParamLoweringMode,
@@ -518,28 +225,20 @@ pub(crate) fn eval_to_const(
     args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
+    let db = ctx.db;
     if let Expr::Path(p) = &ctx.body.exprs[expr] {
-        let db = ctx.db;
         let resolver = &ctx.resolver;
         if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
             return c;
         }
     }
-    let body = ctx.body.clone();
-    let mut ctx = ConstEvalCtx {
-        db: ctx.db,
-        owner: ctx.owner,
-        exprs: &body.exprs,
-        pats: &body.pats,
-        local_data: HashMap::default(),
-        infer: &ctx.result,
-    };
-    let computed_expr = eval_const(expr, &mut ctx);
-    let const_scalar = match computed_expr {
-        Ok(ComputedExpr::Literal(literal)) => literal.into(),
-        _ => ConstScalar::Unknown,
-    };
-    intern_const_scalar(const_scalar, TyBuilder::usize())
+    let infer = ctx.clone().resolve_all();
+    if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
+        if let Ok(result) = interpret_mir(db, &mir_body, true) {
+            return result;
+        }
+    }
+    unknown_const(infer[expr].clone())
 }
 
 #[cfg(test)]
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 3c930c077b3..6a29e8ce52e 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1,24 +1,44 @@
 use base_db::fixture::WithFixture;
-use hir_def::{db::DefDatabase, expr::Literal};
+use hir_def::db::DefDatabase;
 
-use crate::{consteval::ComputedExpr, db::HirDatabase, test_db::TestDB};
+use crate::{
+    consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
+};
 
-use super::ConstEvalError;
+use super::{
+    super::mir::{MirEvalError, MirLowerError},
+    ConstEvalError,
+};
 
-fn check_fail(ra_fixture: &str, error: ConstEvalError) {
-    assert_eq!(eval_goal(ra_fixture), Err(error));
-}
-
-fn check_number(ra_fixture: &str, answer: i128) {
-    let r = eval_goal(ra_fixture).unwrap();
-    match r {
-        ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer),
-        ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128),
-        x => panic!("Expected number but found {x:?}"),
+fn simplify(e: ConstEvalError) -> ConstEvalError {
+    match e {
+        ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
+            simplify(ConstEvalError::MirEvalError(*e))
+        }
+        _ => e,
     }
 }
 
-fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> {
+#[track_caller]
+fn check_fail(ra_fixture: &str, error: ConstEvalError) {
+    assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
+}
+
+#[track_caller]
+fn check_number(ra_fixture: &str, answer: i128) {
+    let r = eval_goal(ra_fixture).unwrap();
+    match &r.data(Interner).value {
+        chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+            ConstScalar::Bytes(b, _) => {
+                assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
+            }
+            x => panic!("Expected number but found {:?}", x),
+        },
+        _ => panic!("result of const eval wasn't a concrete const"),
+    }
+}
+
+fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let module_id = db.module_for_file(file_id);
     let def_map = module_id.def_map(&db);
@@ -42,21 +62,18 @@ fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> {
 #[test]
 fn add() {
     check_number(r#"const GOAL: usize = 2 + 2;"#, 4);
+    check_number(r#"const GOAL: i32 = -2 + --5;"#, 3);
+    check_number(r#"const GOAL: i32 = 7 - 5;"#, 2);
+    check_number(r#"const GOAL: i32 = 7 + (1 - 5);"#, 3);
 }
 
 #[test]
 fn bit_op() {
     check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
     check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
-    // FIXME: rustc evaluate this to -128
-    check_fail(
-        r#"const GOAL: i8 = 1 << 7"#,
-        ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()),
-    );
-    check_fail(
-        r#"const GOAL: i8 = 1 << 8"#,
-        ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()),
-    );
+    check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
+    // FIXME: report panic here
+    check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
 }
 
 #[test]
@@ -73,6 +90,803 @@ fn locals() {
     );
 }
 
+#[test]
+fn references() {
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &mut x;
+        *y = 5;
+        x
+    };
+    "#,
+        5,
+    );
+    check_number(
+        r#"
+    struct Foo(i32);
+    impl Foo {
+        fn method(&mut self, x: i32) {
+            self.0 = 2 * self.0 + x;
+        }
+    }
+    const GOAL: i32 = {
+        let mut x = Foo(3);
+        x.method(5);
+        x.0
+    };
+    "#,
+        11,
+    );
+}
+
+#[test]
+fn reference_autoderef() {
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &mut x;
+        let y: &mut usize = &mut y;
+        *y = 5;
+        x
+    };
+    "#,
+        5,
+    );
+    check_number(
+        r#"
+    const GOAL: usize = {
+        let x = 3;
+        let y = &&&&&&&x;
+        let z: &usize = &y;
+        *z
+    };
+    "#,
+        3,
+    );
+    check_number(
+        r#"
+    struct Foo<T> { x: T }
+    impl<T> Foo<T> {
+        fn foo(&mut self) -> T { self.x }
+    }
+    fn f(i: &mut &mut Foo<Foo<i32>>) -> i32 {
+        ((**i).x).foo()
+    }
+    fn g(i: Foo<Foo<i32>>) -> i32 {
+        i.x.foo()
+    }
+    const GOAL: i32 = f(&mut &mut Foo { x: Foo { x: 3 } }) + g(Foo { x: Foo { x: 5 } });
+    "#,
+        8,
+    );
+}
+
+#[test]
+fn overloaded_deref() {
+    // FIXME: We should support this.
+    check_fail(
+        r#"
+    //- minicore: deref_mut
+    struct Foo;
+
+    impl core::ops::Deref for Foo {
+        type Target = i32;
+        fn deref(&self) -> &i32 {
+            &5
+        }
+    }
+
+    const GOAL: i32 = {
+        let x = Foo;
+        let y = &*x;
+        *y + *x
+    };
+    "#,
+        ConstEvalError::MirLowerError(MirLowerError::NotSupported(
+            "explicit overloaded deref".into(),
+        )),
+    );
+}
+
+#[test]
+fn overloaded_deref_autoref() {
+    check_number(
+        r#"
+    //- minicore: deref_mut
+    struct Foo;
+    struct Bar;
+
+    impl core::ops::Deref for Foo {
+        type Target = Bar;
+        fn deref(&self) -> &Bar {
+            &Bar
+        }
+    }
+
+    impl Bar {
+        fn method(&self) -> i32 {
+            5
+        }
+    }
+
+    const GOAL: i32 = Foo.method();
+    "#,
+        5,
+    );
+}
+
+#[test]
+fn function_call() {
+    check_number(
+        r#"
+    const fn f(x: usize) -> usize {
+        2 * x + 5
+    }
+    const GOAL: usize = f(3);
+    "#,
+        11,
+    );
+    check_number(
+        r#"
+    const fn add(x: usize, y: usize) -> usize {
+        x + y
+    }
+    const GOAL: usize = add(add(1, 2), add(3, add(4, 5)));
+    "#,
+        15,
+    );
+}
+
+#[test]
+fn intrinsics() {
+    check_number(
+        r#"
+    extern "rust-intrinsic" {
+        pub fn size_of<T>() -> usize;
+    }
+
+    const GOAL: usize = size_of::<i32>();
+    "#,
+        4,
+    );
+}
+
+#[test]
+fn trait_basic() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    const GOAL: u8 = {
+        let x = 3;
+        Foo::f(&x)
+    };
+    "#,
+        36,
+    );
+}
+
+#[test]
+fn trait_method() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    const GOAL: u8 = {
+        let x = 3;
+        x.f()
+    };
+    "#,
+        36,
+    );
+}
+
+#[test]
+fn generic_fn() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for () {
+        fn f(&self) -> u8 {
+            0
+        }
+    }
+
+    struct Succ<S>(S);
+
+    impl<T: Foo> Foo for Succ<T> {
+        fn f(&self) -> u8 {
+            self.0.f() + 1
+        }
+    }
+
+    const GOAL: u8 = Succ(Succ(())).f();
+    "#,
+        2,
+    );
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    fn foof<T: Foo>(x: T, y: T) -> u8 {
+        x.f() + y.f()
+    }
+
+    const GOAL: u8 = foof(2, 5);
+    "#,
+        73,
+    );
+    check_number(
+        r#"
+    fn bar<A, B>(a: A, b: B) -> B {
+        b
+    }
+        const GOAL: u8 = bar("hello", 12);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    fn bar<A, B>(a: A, b: B) -> B {
+        b
+    }
+    fn foo<T>(x: [T; 2]) -> T {
+        bar(x[0], x[1])
+    }
+
+    const GOAL: u8 = foo([2, 5]);
+    "#,
+        5,
+    );
+}
+
+#[test]
+fn impl_trait() {
+    check_number(
+        r#"
+    trait Foo {
+        fn f(&self) -> u8;
+    }
+
+    impl Foo for u8 {
+        fn f(&self) -> u8 {
+            *self + 33
+        }
+    }
+
+    fn foof(x: impl Foo, y: impl Foo) -> impl Foo {
+        x.f() + y.f()
+    }
+
+    const GOAL: u8 = foof(2, 5).f();
+    "#,
+        106,
+    );
+    check_number(
+        r#"
+        struct Foo<T>(T, T, (T, T));
+        trait S {
+            fn sum(&self) -> i64;
+        }
+        impl S for i64 {
+            fn sum(&self) -> i64 {
+                *self
+            }
+        }
+        impl<T: S> S for Foo<T> {
+            fn sum(&self) -> i64 {
+                self.0.sum() + self.1.sum() + self.2 .0.sum() + self.2 .1.sum()
+            }
+        }
+
+        fn foo() -> Foo<impl S> {
+            Foo(
+                Foo(1i64, 2, (3, 4)),
+                Foo(5, 6, (7, 8)),
+                (
+                    Foo(9, 10, (11, 12)),
+                    Foo(13, 14, (15, 16)),
+                ),
+            )
+        }
+        const GOAL: i64 = foo().sum();
+    "#,
+        136,
+    );
+}
+
+#[test]
+fn ifs() {
+    check_number(
+        r#"
+    const fn f(b: bool) -> u8 {
+        if b { 1 } else { 10 }
+    }
+
+    const GOAL: u8 = f(true) + f(true) + f(false);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    const fn max(a: i32, b: i32) -> i32 {
+        if a < b { b } else { a }
+    }
+
+    const GOAL: i32 = max(max(1, max(10, 3)), 0-122);
+        "#,
+        10,
+    );
+
+    check_number(
+        r#"
+    const fn max(a: &i32, b: &i32) -> &i32 {
+        if *a < *b { b } else { a }
+    }
+
+    const GOAL: i32 = *max(max(&1, max(&10, &3)), &5);
+        "#,
+        10,
+    );
+}
+
+#[test]
+fn loops() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let mut x = 0;
+        loop {
+            x = x + 1;
+            while true {
+                break;
+            }
+            x = x + 1;
+            if x == 2 {
+                continue;
+            }
+            break;
+        }
+        x
+    };
+        "#,
+        4,
+    );
+}
+
+#[test]
+fn for_loops() {
+    check_number(
+        r#"
+    //- minicore: iterator
+
+    struct Range {
+        start: u8,
+        end: u8,
+    }
+
+    impl Iterator for Range {
+        type Item = u8;
+        fn next(&mut self) -> Option<u8> {
+            if self.start >= self.end {
+                None
+            } else {
+                let r = self.start;
+                self.start = self.start + 1;
+                Some(r)
+            }
+        }
+    }
+
+    const GOAL: u8 = {
+        let mut sum = 0;
+        let ar = Range { start: 1, end: 11 };
+        for i in ar {
+            sum = sum + i;
+        }
+        sum
+    };
+        "#,
+        55,
+    );
+}
+
+#[test]
+fn recursion() {
+    check_number(
+        r#"
+    const fn fact(k: i32) -> i32 {
+        if k > 0 { fact(k - 1) * k } else { 1 }
+    }
+
+    const GOAL: i32 = fact(5);
+        "#,
+        120,
+    );
+}
+
+#[test]
+fn structs() {
+    check_number(
+        r#"
+        struct Point {
+            x: i32,
+            y: i32,
+        }
+
+        const GOAL: i32 = {
+            let p = Point { x: 5, y: 2 };
+            let y = 1;
+            let x = 3;
+            let q = Point { y, x };
+            p.x + p.y + p.x + q.y + q.y + q.x
+        };
+        "#,
+        17,
+    );
+}
+
+#[test]
+fn unions() {
+    check_number(
+        r#"
+        union U {
+            f1: i64,
+            f2: (i32, i32),
+        }
+
+        const GOAL: i32 = {
+            let p = U { f1: 0x0123ABCD0123DCBA };
+            let p = unsafe { p.f2 };
+            p.0 + p.1 + p.1
+        };
+        "#,
+        0x0123ABCD * 2 + 0x0123DCBA,
+    );
+}
+
+#[test]
+fn tuples() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let a = (10, 20, 3, 15);
+        a.1
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let mut a = (10, 20, 3, 15);
+        a.1 = 2;
+        a.0 + a.1 + a.2 + a.3
+    };
+        "#,
+        30,
+    );
+    check_number(
+        r#"
+    struct TupleLike(i32, u8, i64, u16);
+    const GOAL: u8 = {
+        let a = TupleLike(10, 20, 3, 15);
+        a.1
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        match (&(2 + 2), &4) {
+            (left_val, right_val) => {
+                if !(*left_val == *right_val) {
+                    2
+                } else {
+                    5
+                }
+            }
+        }
+    };
+        "#,
+        5,
+    );
+}
+
+#[test]
+fn path_pattern_matching() {
+    check_number(
+        r#"
+    enum Season {
+        Spring,
+        Summer,
+        Fall,
+        Winter,
+    }
+
+    use Season::*;
+
+    const fn f(x: Season) -> i32 {
+        match x {
+            Spring => 1,
+            Summer => 2,
+            Fall => 3,
+            Winter => 4,
+        }
+    }
+    const GOAL: i32 = f(Spring) + 10 * f(Summer) + 100 * f(Fall) + 1000 * f(Winter);
+        "#,
+        4321,
+    );
+}
+
+#[test]
+fn pattern_matching_ergonomics() {
+    check_number(
+        r#"
+    const fn f(x: &(u8, u8)) -> u8 {
+        match x {
+            (a, b) => *a + *b
+        }
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+}
+
+#[test]
+fn let_else() {
+    check_number(
+        r#"
+    const fn f(x: &(u8, u8)) -> u8 {
+        let (a, b) = x;
+        *a + *b
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    enum SingleVariant {
+        Var(u8, u8),
+    }
+    const fn f(x: &&&&&SingleVariant) -> u8 {
+        let SingleVariant::Var(a, b) = x;
+        *a + *b
+    }
+    const GOAL: u8 = f(&&&&&SingleVariant::Var(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>) -> i32 {
+        let Some(x) = x else { return 10 };
+        2 * x
+    }
+    const GOAL: i32 = f(Some(1000)) + f(None);
+        "#,
+        2010,
+    );
+}
+
+#[test]
+fn function_param_patterns() {
+    check_number(
+        r#"
+    const fn f((a, b): &(u8, u8)) -> u8 {
+        *a + *b
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        5,
+    );
+    check_number(
+        r#"
+    const fn f(c @ (a, b): &(u8, u8)) -> u8 {
+        *a + *b + c.0 + (*c).1
+    }
+    const GOAL: u8 = f(&(2, 3));
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    const fn f(ref a: u8) -> u8 {
+        *a
+    }
+    const GOAL: u8 = f(2);
+        "#,
+        2,
+    );
+    check_number(
+        r#"
+    struct Foo(u8);
+    impl Foo {
+        const fn f(&self, (a, b): &(u8, u8)) -> u8 {
+            self.0 + *a + *b
+        }
+    }
+    const GOAL: u8 = Foo(4).f(&(2, 3));
+        "#,
+        9,
+    );
+}
+
+#[test]
+fn options() {
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: u8 = {
+        let x = Some(2);
+        match x {
+            Some(y) => 2 * y,
+            _ => 10,
+        }
+    };
+        "#,
+        4,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    fn f(x: Option<Option<i32>>) -> i32 {
+        if let Some(y) = x && let Some(z) = y {
+            z
+        } else if let Some(y) = x {
+            1
+        } else {
+            0
+        }
+    }
+    const GOAL: i32 = f(Some(Some(10))) + f(Some(None)) + f(None);
+        "#,
+        11,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: u8 = {
+        let x = None;
+        match x {
+            Some(y) => 2 * y,
+            _ => 10,
+        }
+    };
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const GOAL: Option<&u8> = None;
+        "#,
+        0,
+    );
+}
+
+#[test]
+fn or_pattern() {
+    check_number(
+        r#"
+    const GOAL: u8 = {
+        let (a | a) = 2;
+        a
+    };
+        "#,
+        2,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>) -> i32 {
+        let (Some(a) | Some(a)) = x else { return 2; };
+        a
+    }
+    const GOAL: i32 = f(Some(10)) + f(None);
+        "#,
+        12,
+    );
+    check_number(
+        r#"
+    //- minicore: option
+    const fn f(x: Option<i32>, y: Option<i32>) -> i32 {
+        match (x, y) {
+            (Some(x), Some(y)) => x * y,
+            (Some(a), _) | (_, Some(a)) => a,
+            _ => 10,
+        }
+    }
+    const GOAL: i32 = f(Some(10), Some(20)) + f(Some(30), None) + f(None, Some(40)) + f(None, None);
+        "#,
+        280,
+    );
+}
+
+#[test]
+fn array_and_index() {
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: u8 = {
+        let a = [10, 20, 3, 15];
+        let x: &[u8] = &a;
+        x[1]
+    };
+        "#,
+        20,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = [1, 2, 3][2];"#,
+        3,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = { let a = [1, 2, 3]; let x: &[i32] = &a; x.len() };"#,
+        3,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
+        5,
+    );
+}
+
+#[test]
+fn byte_string() {
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: u8 = {
+        let a = b"hello";
+        let x: &[u8] = a;
+        x[0]
+    };
+        "#,
+        104,
+    );
+}
+
 #[test]
 fn consts() {
     check_number(
@@ -92,41 +906,35 @@ fn enums() {
         r#"
     enum E {
         F1 = 1,
-        F2 = 2 * E::F1 as u8,
-        F3 = 3 * E::F2 as u8,
+        F2 = 2 * E::F1 as isize, // Rustc expects an isize here
+        F3 = 3 * E::F2 as isize,
     }
-    const GOAL: i32 = E::F3 as u8;
+    const GOAL: u8 = E::F3 as u8;
     "#,
         6,
     );
     check_number(
         r#"
     enum E { F1 = 1, F2, }
-    const GOAL: i32 = E::F2 as u8;
+    const GOAL: u8 = E::F2 as u8;
     "#,
         2,
     );
     check_number(
         r#"
     enum E { F1, }
-    const GOAL: i32 = E::F1 as u8;
+    const GOAL: u8 = E::F1 as u8;
     "#,
         0,
     );
     let r = eval_goal(
         r#"
-        enum E { A = 1, }
+        enum E { A = 1, B }
         const GOAL: E = E::A;
         "#,
     )
     .unwrap();
-    match r {
-        ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
-            assert_eq!(name, "E::A");
-            assert_eq!(val, 1);
-        }
-        x => panic!("Expected enum but found {x:?}"),
-    }
+    assert_eq!(try_const_usize(&r), Some(1));
 }
 
 #[test]
@@ -138,7 +946,19 @@ fn const_loop() {
     const F2: i32 = 2 * F1;
     const GOAL: i32 = F3;
     "#,
-        ConstEvalError::Loop,
+        ConstEvalError::MirLowerError(MirLowerError::Loop),
+    );
+}
+
+#[test]
+fn const_transfer_memory() {
+    check_number(
+        r#"
+    const A1: &i32 = &2;
+    const A2: &i32 = &5;
+    const GOAL: i32 = *A1 + *A2;
+    "#,
+        7,
     );
 }
 
@@ -157,7 +977,20 @@ fn const_impl_assoc() {
 }
 
 #[test]
-fn const_generic_subst() {
+fn const_generic_subst_fn() {
+    check_number(
+        r#"
+    const fn f<const A: usize>(x: usize) -> usize {
+        A * x + 5
+    }
+    const GOAL: usize = f::<2>(3);
+    "#,
+        11,
+    );
+}
+
+#[test]
+fn const_generic_subst_assoc_const_impl() {
     // FIXME: this should evaluate to 5
     check_fail(
         r#"
@@ -167,7 +1000,7 @@ fn const_generic_subst() {
     }
     const GOAL: usize = Adder::<2, 3>::VAL;
     "#,
-        ConstEvalError::NotSupported("const generic without substitution"),
+        ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
     );
 }
 
@@ -185,6 +1018,58 @@ fn const_trait_assoc() {
     }
     const GOAL: usize = U0::VAL;
     "#,
-        ConstEvalError::IncompleteExpr,
+        ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
     );
 }
+
+#[test]
+fn exec_limits() {
+    check_fail(
+        r#"
+    const GOAL: usize = loop {};
+    "#,
+        ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
+    );
+    check_fail(
+        r#"
+    const fn f(x: i32) -> i32 {
+        f(x + 1)
+    }
+    const GOAL: i32 = f(0);
+    "#,
+        ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+    );
+    // Reasonable code should still work
+    check_number(
+        r#"
+    const fn nth_odd(n: i32) -> i32 {
+        2 * n - 1
+    }
+    const fn f(n: i32) -> i32 {
+        let sum = 0;
+        let i = 0;
+        while i < n {
+            i = i + 1;
+            sum = sum + nth_odd(i);
+        }
+        sum
+    }
+    const GOAL: i32 = f(10000);
+    "#,
+        10000 * 10000,
+    );
+}
+
+#[test]
+fn type_error() {
+    let e = eval_goal(
+        r#"
+    const GOAL: u8 = {
+        let x: u16 = 2;
+        let y: (u8, u8) = x;
+        y.0
+    };
+    "#,
+    );
+    assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
+}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index d45e2a943ad..304c78767f1 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -16,10 +16,12 @@ use smallvec::SmallVec;
 
 use crate::{
     chalk_db,
-    consteval::{ComputedExpr, ConstEvalError},
+    consteval::ConstEvalError,
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
-    Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
-    QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId,
+    mir::{BorrowckResult, MirBody, MirLowerError},
+    Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
+    PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
+    ValueTyDefId,
 };
 use hir_expand::name::Name;
 
@@ -32,6 +34,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::infer::infer_query)]
     fn infer_query(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
 
+    #[salsa::invoke(crate::mir::mir_body_query)]
+    #[salsa::cycle(crate::mir::mir_body_recover)]
+    fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
+
+    #[salsa::invoke(crate::mir::borrowck_query)]
+    fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
+
     #[salsa::invoke(crate::lower::ty_query)]
     #[salsa::cycle(crate::lower::ty_recover)]
     fn ty(&self, def: TyDefId) -> Binders<Ty>;
@@ -46,13 +55,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::lower::const_param_ty_query)]
     fn const_param_ty(&self, def: ConstParamId) -> Ty;
 
-    #[salsa::invoke(crate::consteval::const_eval_variant_query)]
+    #[salsa::invoke(crate::consteval::const_eval_query)]
     #[salsa::cycle(crate::consteval::const_eval_recover)]
-    fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
+    fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
 
-    #[salsa::invoke(crate::consteval::const_eval_query_variant)]
-    #[salsa::cycle(crate::consteval::const_eval_variant_recover)]
-    fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
+    #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
+    #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
+    fn const_eval_discriminant(&self, def: EnumVariantId) -> Result<i128, ConstEvalError>;
 
     #[salsa::invoke(crate::lower::impl_trait_query)]
     fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index f7031a8546a..d36b93e3bdd 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -178,6 +178,7 @@ impl<'a> DeclValidator<'a> {
                 AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
+                AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
                 AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
                 // These warnings should not explore macro definitions at all
@@ -234,8 +235,8 @@ impl<'a> DeclValidator<'a> {
         let pats_replacements = body
             .pats
             .iter()
-            .filter_map(|(id, pat)| match pat {
-                Pat::Bind { name, .. } => Some((id, name)),
+            .filter_map(|(pat_id, pat)| match pat {
+                Pat::Bind { id, .. } => Some((pat_id, &body.bindings[*id].name)),
                 _ => None,
             })
             .filter_map(|(id, bind_name)| {
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 3286dcb5afd..2e9066788cf 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -5,11 +5,11 @@
 use std::fmt;
 use std::sync::Arc;
 
+use either::Either;
 use hir_def::lang_item::LangItem;
 use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
 use hir_def::{ItemContainerId, Lookup};
 use hir_expand::name;
-use itertools::Either;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use typed_arena::Arena;
@@ -84,7 +84,7 @@ impl ExprValidator {
 
             match expr {
                 Expr::Match { expr, arms } => {
-                    self.validate_match(id, *expr, arms, db, self.infer.clone());
+                    self.validate_match(id, *expr, arms, db);
                 }
                 Expr::Call { .. } | Expr::MethodCall { .. } => {
                     self.validate_call(db, id, expr, &mut filter_map_next_checker);
@@ -147,16 +147,15 @@ impl ExprValidator {
 
     fn validate_match(
         &mut self,
-        id: ExprId,
         match_expr: ExprId,
+        scrutinee_expr: ExprId,
         arms: &[MatchArm],
         db: &dyn HirDatabase,
-        infer: Arc<InferenceResult>,
     ) {
         let body = db.body(self.owner);
 
-        let match_expr_ty = &infer[match_expr];
-        if match_expr_ty.is_unknown() {
+        let scrut_ty = &self.infer[scrutinee_expr];
+        if scrut_ty.is_unknown() {
             return;
         }
 
@@ -166,23 +165,23 @@ impl ExprValidator {
         let mut m_arms = Vec::with_capacity(arms.len());
         let mut has_lowering_errors = false;
         for arm in arms {
-            if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) {
+            if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) {
                 // We only include patterns whose type matches the type
-                // of the match expression. If we had an InvalidMatchArmPattern
+                // of the scrutinee expression. If we had an InvalidMatchArmPattern
                 // diagnostic or similar we could raise that in an else
                 // block here.
                 //
                 // When comparing the types, we also have to consider that rustc
-                // will automatically de-reference the match expression type if
+                // will automatically de-reference the scrutinee expression type if
                 // necessary.
                 //
                 // FIXME we should use the type checker for this.
-                if (pat_ty == match_expr_ty
-                    || match_expr_ty
+                if (pat_ty == scrut_ty
+                    || scrut_ty
                         .as_reference()
                         .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
                         .unwrap_or(false))
-                    && types_of_subpatterns_do_match(arm.pat, &body, &infer)
+                    && types_of_subpatterns_do_match(arm.pat, &body, &self.infer)
                 {
                     // If we had a NotUsefulMatchArm diagnostic, we could
                     // check the usefulness of each pattern as we added it
@@ -206,7 +205,7 @@ impl ExprValidator {
             return;
         }
 
-        let report = compute_match_usefulness(&cx, &m_arms, match_expr_ty);
+        let report = compute_match_usefulness(&cx, &m_arms, scrut_ty);
 
         // FIXME Report unreacheble arms
         // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
@@ -214,8 +213,8 @@ impl ExprValidator {
         let witnesses = report.non_exhaustiveness_witnesses;
         if !witnesses.is_empty() {
             self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms {
-                match_expr: id,
-                uncovered_patterns: missing_match_arms(&cx, match_expr_ty, witnesses, arms),
+                match_expr,
+                uncovered_patterns: missing_match_arms(&cx, scrut_ty, witnesses, arms),
             });
         }
     }
@@ -379,7 +378,7 @@ fn missing_match_arms<'p>(
     arms: &[MatchArm],
 ) -> String {
     struct DisplayWitness<'a, 'p>(&'a DeconstructedPat<'p>, &'a MatchCheckCtx<'a, 'p>);
-    impl<'a, 'p> fmt::Display for DisplayWitness<'a, 'p> {
+    impl fmt::Display for DisplayWitness<'_, '_> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             let DisplayWitness(witness, cx) = *self;
             let pat = witness.to_pat(cx);
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index 8b0f051b46b..859a37804ae 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -146,8 +146,9 @@ impl<'a> PatCtxt<'a> {
                 PatKind::Leaf { subpatterns }
             }
 
-            hir_def::expr::Pat::Bind { ref name, subpat, .. } => {
+            hir_def::expr::Pat::Bind { id, subpat, .. } => {
                 let bm = self.infer.pat_binding_modes[&pat];
+                let name = &self.body.bindings[id].name;
                 match (bm, ty.kind(Interner)) {
                     (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
                     (BindingMode::Ref(_), _) => {
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 431ab949b46..d25c0ccf00d 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -94,8 +94,10 @@ fn walk_unsafe(
                 unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
             }
         }
-        Expr::Unsafe { body: child } => {
-            return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
+        Expr::Unsafe { .. } => {
+            return expr.walk_child_exprs(|child| {
+                walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
+            });
         }
         _ => {}
     }
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index b22064d8c42..bd3eccfe43d 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -5,8 +5,9 @@
 use std::fmt::{self, Debug};
 
 use base_db::CrateId;
-use chalk_ir::BoundVar;
+use chalk_ir::{BoundVar, TyKind};
 use hir_def::{
+    adt::VariantData,
     body,
     db::DefDatabase,
     find_path,
@@ -14,9 +15,9 @@ use hir_def::{
     item_scope::ItemInNs,
     lang_item::{LangItem, LangItemTarget},
     path::{Path, PathKind},
-    type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
+    type_ref::{TraitBoundModifier, TypeBound, TypeRef},
     visibility::Visibility,
-    HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
+    HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
 };
 use hir_expand::{hygiene::Hygiene, name::Name};
 use intern::{Internable, Interned};
@@ -25,14 +26,17 @@ use smallvec::SmallVec;
 
 use crate::{
     db::HirDatabase,
-    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
+    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
+    layout::layout_of_ty,
+    lt_from_placeholder_idx,
     mapping::from_chalk,
+    mir::pad16,
     primitive, to_assoc_type_id,
     utils::{self, generics},
-    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
-    GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
-    OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, Substitution, TraitRef,
-    TraitRefExt, Ty, TyExt, TyKind, WhereClause,
+    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
+    DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
+    MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
+    Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause,
 };
 
 pub trait HirWrite: fmt::Write {
@@ -362,20 +366,176 @@ impl HirDisplay for GenericArg {
 impl HirDisplay for Const {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         let data = self.interned();
-        match data.value {
+        match &data.value {
             ConstValue::BoundVar(idx) => idx.hir_fmt(f),
             ConstValue::InferenceVar(..) => write!(f, "#c#"),
             ConstValue::Placeholder(idx) => {
-                let id = from_placeholder_idx(f.db, idx);
+                let id = from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
                 let param_data = &generics.params.type_or_consts[id.local_id];
                 write!(f, "{}", param_data.name().unwrap())
             }
-            ConstValue::Concrete(c) => write!(f, "{}", c.interned),
+            ConstValue::Concrete(c) => match &c.interned {
+                ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
+                ConstScalar::Unknown => f.write_char('_'),
+            },
         }
     }
 }
 
+pub struct HexifiedConst(pub Const);
+
+impl HirDisplay for HexifiedConst {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        let data = &self.0.data(Interner);
+        if let TyKind::Scalar(s) = data.ty.kind(Interner) {
+            if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
+                if let ConstValue::Concrete(c) = &data.value {
+                    if let ConstScalar::Bytes(b, m) = &c.interned {
+                        let value = u128::from_le_bytes(pad16(b, false));
+                        if value >= 10 {
+                            render_const_scalar(f, &b, m, &data.ty)?;
+                            return write!(f, " ({:#X})", value);
+                        }
+                    }
+                }
+            }
+        }
+        self.0.hir_fmt(f)
+    }
+}
+
+fn render_const_scalar(
+    f: &mut HirFormatter<'_>,
+    b: &[u8],
+    memory_map: &MemoryMap,
+    ty: &Ty,
+) -> Result<(), HirDisplayError> {
+    match ty.kind(Interner) {
+        chalk_ir::TyKind::Scalar(s) => match s {
+            Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
+            Scalar::Char => {
+                let x = u128::from_le_bytes(pad16(b, false)) as u32;
+                let Ok(c) = char::try_from(x) else {
+                    return f.write_str("<unicode-error>");
+                };
+                write!(f, "{c:?}")
+            }
+            Scalar::Int(_) => {
+                let x = i128::from_le_bytes(pad16(b, true));
+                write!(f, "{x}")
+            }
+            Scalar::Uint(_) => {
+                let x = u128::from_le_bytes(pad16(b, false));
+                write!(f, "{x}")
+            }
+            Scalar::Float(fl) => match fl {
+                chalk_ir::FloatTy::F32 => {
+                    let x = f32::from_le_bytes(b.try_into().unwrap());
+                    write!(f, "{x:?}")
+                }
+                chalk_ir::FloatTy::F64 => {
+                    let x = f64::from_le_bytes(b.try_into().unwrap());
+                    write!(f, "{x:?}")
+                }
+            },
+        },
+        chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
+            chalk_ir::TyKind::Str => {
+                let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+                let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]);
+                let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
+                write!(f, "{s:?}")
+            }
+            _ => f.write_str("<ref-not-supported>"),
+        },
+        chalk_ir::TyKind::Tuple(_, subst) => {
+            // FIXME: Remove this line. If the target data layout is independent
+            // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need
+            // to get krate. Otherwise, we need to get krate from the final callers of the hir display
+            // infrastructure and have it here as a field on `f`.
+            let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
+            let Ok(layout) = layout_of_ty(f.db, ty, krate) else {
+                return f.write_str("<layout-error>");
+            };
+            f.write_str("(")?;
+            let mut first = true;
+            for (id, ty) in subst.iter(Interner).enumerate() {
+                if first {
+                    first = false;
+                } else {
+                    f.write_str(", ")?;
+                }
+                let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
+                let offset = layout.fields.offset(id).bytes_usize();
+                let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                    f.write_str("<layout-error>")?;
+                    continue;
+                };
+                let size = layout.size.bytes_usize();
+                render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)?;
+            }
+            f.write_str(")")
+        }
+        chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+            hir_def::AdtId::StructId(s) => {
+                let data = f.db.struct_data(s);
+                let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else {
+                    return f.write_str("<layout-error>");
+                };
+                match data.variant_data.as_ref() {
+                    VariantData::Record(fields) | VariantData::Tuple(fields) => {
+                        let field_types = f.db.field_types(s.into());
+                        let krate = adt.0.module(f.db.upcast()).krate();
+                        let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
+                            let offset = layout
+                                .fields
+                                .offset(u32::from(id.into_raw()) as usize)
+                                .bytes_usize();
+                            let ty = field_types[id].clone().substitute(Interner, subst);
+                            let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                                return f.write_str("<layout-error>");
+                            };
+                            let size = layout.size.bytes_usize();
+                            render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
+                        };
+                        let mut it = fields.iter();
+                        if matches!(data.variant_data.as_ref(), VariantData::Record(_)) {
+                            write!(f, "{} {{", data.name)?;
+                            if let Some((id, data)) = it.next() {
+                                write!(f, " {}: ", data.name)?;
+                                render_field(f, id)?;
+                            }
+                            for (id, data) in it {
+                                write!(f, ", {}: ", data.name)?;
+                                render_field(f, id)?;
+                            }
+                            write!(f, " }}")?;
+                        } else {
+                            let mut it = it.map(|x| x.0);
+                            write!(f, "{}(", data.name)?;
+                            if let Some(id) = it.next() {
+                                render_field(f, id)?;
+                            }
+                            for id in it {
+                                write!(f, ", ")?;
+                                render_field(f, id)?;
+                            }
+                            write!(f, ")")?;
+                        }
+                        return Ok(());
+                    }
+                    VariantData::Unit => write!(f, "{}", data.name),
+                }
+            }
+            hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name),
+            hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
+        },
+        chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
+        _ => f.write_str("<not-supported>"),
+    }
+}
+
 impl HirDisplay for BoundVar {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write!(f, "?{}.{}", self.debruijn.depth(), self.index)
@@ -614,8 +774,9 @@ impl HirDisplay for Ty {
                                     {
                                         return true;
                                     }
-                                    if let Some(ConstValue::Concrete(c)) =
-                                        parameter.constant(Interner).map(|x| x.data(Interner).value)
+                                    if let Some(ConstValue::Concrete(c)) = parameter
+                                        .constant(Interner)
+                                        .map(|x| &x.data(Interner).value)
                                     {
                                         if c.interned == ConstScalar::Unknown {
                                             return true;
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 767afdf9eb4..7de5b4295fc 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -17,11 +17,12 @@ use std::ops::Index;
 use std::sync::Arc;
 
 use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use either::Either;
 use hir_def::{
     body::Body,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
-    expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId},
+    expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
     path::Path,
@@ -30,10 +31,9 @@ use hir_def::{
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
     ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
 };
-use hir_expand::name::name;
-use itertools::Either;
+use hir_expand::name::{name, Name};
 use la_arena::ArenaMap;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::always;
 
 use crate::{
@@ -66,8 +66,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
     let mut ctx = InferenceContext::new(db, def, &body, resolver);
 
     match def {
+        DefWithBodyId::FunctionId(f) => {
+            ctx.collect_fn(f);
+        }
         DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
-        DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
         DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
         DefWithBodyId::VariantId(v) => {
             ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
@@ -144,44 +146,6 @@ impl Default for BindingMode {
     }
 }
 
-/// Used to generalize patterns and assignee expressions.
-trait PatLike: Into<ExprOrPatId> + Copy {
-    type BindingMode: Copy;
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        default_bm: Self::BindingMode,
-    ) -> Ty;
-}
-
-impl PatLike for ExprId {
-    type BindingMode = ();
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        _: Self::BindingMode,
-    ) -> Ty {
-        this.infer_assignee_expr(id, expected_ty)
-    }
-}
-
-impl PatLike for PatId {
-    type BindingMode = BindingMode;
-
-    fn infer(
-        this: &mut InferenceContext<'_>,
-        id: Self,
-        expected_ty: &Ty,
-        default_bm: Self::BindingMode,
-    ) -> Ty {
-        this.infer_pat(id, expected_ty, default_bm)
-    }
-}
-
 #[derive(Debug)]
 pub(crate) struct InferOk<T> {
     value: T,
@@ -200,11 +164,45 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum InferenceDiagnostic {
-    NoSuchField { expr: ExprId },
-    PrivateField { expr: ExprId, field: FieldId },
-    PrivateAssocItem { id: ExprOrPatId, item: AssocItemId },
-    BreakOutsideOfLoop { expr: ExprId, is_break: bool },
-    MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
+    NoSuchField {
+        expr: ExprId,
+    },
+    PrivateField {
+        expr: ExprId,
+        field: FieldId,
+    },
+    PrivateAssocItem {
+        id: ExprOrPatId,
+        item: AssocItemId,
+    },
+    UnresolvedField {
+        expr: ExprId,
+        receiver: Ty,
+        name: Name,
+        method_with_same_name_exists: bool,
+    },
+    UnresolvedMethodCall {
+        expr: ExprId,
+        receiver: Ty,
+        name: Name,
+        /// Contains the type the field resolves to
+        field_with_same_name: Option<Ty>,
+    },
+    // FIXME: Make this proper
+    BreakOutsideOfLoop {
+        expr: ExprId,
+        is_break: bool,
+        bad_value_break: bool,
+    },
+    MismatchedArgCount {
+        call_expr: ExprId,
+        expected: usize,
+        found: usize,
+    },
+    ExpectedFunction {
+        call_expr: ExprId,
+        found: Ty,
+    },
 }
 
 /// A mismatch between an expected and an inferred type.
@@ -293,8 +291,10 @@ pub enum Adjust {
 /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
 /// The target type is `U` in both cases, with the region and mutability
 /// being those shared by both the receiver and the returned reference.
+///
+/// Mutability is `None` when we are not sure.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct OverloadedDeref(pub Mutability);
+pub struct OverloadedDeref(pub Option<Mutability>);
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum AutoBorrow {
@@ -354,7 +354,10 @@ pub struct InferenceResult {
     /// **Note**: When a pattern type is resolved it may still contain
     /// unresolved or missing subpatterns or subpatterns of mismatched types.
     pub type_of_pat: ArenaMap<PatId, Ty>,
+    pub type_of_binding: ArenaMap<BindingId, Ty>,
     pub type_of_rpit: ArenaMap<RpitId, Ty>,
+    /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
+    pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Interned common types to return references to.
     standard_types: InternedStandardTypes,
@@ -389,18 +392,15 @@ impl InferenceResult {
     pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
         self.type_mismatches.get(&pat.into())
     }
+    pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> {
+        self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch))
+    }
     pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
         self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
             ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
             _ => None,
         })
     }
-    pub fn pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> {
-        self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat {
-            ExprOrPatId::PatId(pat) => Some((pat, mismatch)),
-            _ => None,
-        })
-    }
 }
 
 impl Index<ExprId> for InferenceResult {
@@ -419,6 +419,14 @@ impl Index<PatId> for InferenceResult {
     }
 }
 
+impl Index<BindingId> for InferenceResult {
+    type Output = Ty;
+
+    fn index(&self, b: BindingId) -> &Ty {
+        self.type_of_binding.get(b).unwrap_or(&self.standard_types.unknown)
+    }
+}
+
 /// The inference context contains all information needed during type inference.
 #[derive(Clone, Debug)]
 pub(crate) struct InferenceContext<'a> {
@@ -428,14 +436,19 @@ pub(crate) struct InferenceContext<'a> {
     pub(crate) resolver: Resolver,
     table: unify::InferenceTable<'a>,
     trait_env: Arc<TraitEnvironment>,
+    /// The traits in scope, disregarding block modules. This is used for caching purposes.
+    traits_in_scope: FxHashSet<TraitId>,
     pub(crate) result: InferenceResult,
     /// The return type of the function being inferred, the closure or async block if we're
     /// currently within one.
     ///
     /// We might consider using a nested inference context for checking
-    /// closures, but currently this is the only field that will change there,
-    /// so it doesn't make sense.
+    /// closures so we can swap all shared things out at once.
     return_ty: Ty,
+    /// If `Some`, this stores coercion information for returned
+    /// expressions. If `None`, this is in a context where return is
+    /// inappropriate, such as a const expression.
+    return_coercion: Option<CoerceMany>,
     /// The resume type and the yield type, respectively, of the generator being inferred.
     resume_yield_tys: Option<(Ty, Ty)>,
     diverges: Diverges,
@@ -447,7 +460,7 @@ struct BreakableContext {
     /// Whether this context contains at least one break expression.
     may_break: bool,
     /// The coercion target of the context.
-    coerce: CoerceMany,
+    coerce: Option<CoerceMany>,
     /// The optional label of the context.
     label: Option<name::Name>,
     kind: BreakableKind,
@@ -503,16 +516,22 @@ impl<'a> InferenceContext<'a> {
             trait_env,
             return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
             resume_yield_tys: None,
+            return_coercion: None,
             db,
             owner,
             body,
+            traits_in_scope: resolver.traits_in_scope(db.upcast()),
             resolver,
             diverges: Diverges::Maybe,
             breakables: Vec::new(),
         }
     }
 
-    fn resolve_all(self) -> InferenceResult {
+    // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
+    // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
+    // used this function for another workaround, mention it here. If you really need this function and believe that
+    // there is no problem in it being `pub(crate)`, remove this comment.
+    pub(crate) fn resolve_all(self) -> InferenceResult {
         let InferenceContext { mut table, mut result, .. } = self;
 
         table.fallback_if_possible();
@@ -528,13 +547,46 @@ impl<'a> InferenceContext<'a> {
         for ty in result.type_of_pat.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
-        for ty in result.type_of_rpit.iter_mut().map(|x| x.1) {
+        for ty in result.type_of_binding.values_mut() {
+            *ty = table.resolve_completely(ty.clone());
+        }
+        for ty in result.type_of_rpit.values_mut() {
+            *ty = table.resolve_completely(ty.clone());
+        }
+        for ty in result.type_of_for_iterator.values_mut() {
             *ty = table.resolve_completely(ty.clone());
         }
         for mismatch in result.type_mismatches.values_mut() {
             mismatch.expected = table.resolve_completely(mismatch.expected.clone());
             mismatch.actual = table.resolve_completely(mismatch.actual.clone());
         }
+        result.diagnostics.retain_mut(|diagnostic| {
+            if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+            | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
+            | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
+            {
+                *ty = table.resolve_completely(ty.clone());
+                // FIXME: Remove this when we are on par with rustc in terms of inference
+                if ty.contains_unknown() {
+                    return false;
+                }
+
+                if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
+                    diagnostic
+                {
+                    let clear = if let Some(ty) = field_with_same_name {
+                        *ty = table.resolve_completely(ty.clone());
+                        ty.contains_unknown()
+                    } else {
+                        false
+                    };
+                    if clear {
+                        *field_with_same_name = None;
+                    }
+                }
+            }
+            true
+        });
         for (_, subst) in result.method_resolutions.values_mut() {
             *subst = table.resolve_completely(subst.clone());
         }
@@ -580,7 +632,7 @@ impl<'a> InferenceContext<'a> {
             let ty = self.insert_type_vars(ty);
             let ty = self.normalize_associated_types_in(ty);
 
-            self.infer_pat(*pat, &ty, BindingMode::default());
+            self.infer_top_pat(*pat, &ty);
         }
         let error_ty = &TypeRef::Error;
         let return_ty = if data.has_async_kw() {
@@ -632,10 +684,19 @@ impl<'a> InferenceContext<'a> {
         };
 
         self.return_ty = self.normalize_associated_types_in(return_ty);
+        self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
     }
 
     fn infer_body(&mut self) {
-        self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
+        match self.return_coercion {
+            Some(_) => self.infer_return(self.body.body_expr),
+            None => {
+                _ = self.infer_expr_coerce(
+                    self.body.body_expr,
+                    &Expectation::has_type(self.return_ty.clone()),
+                )
+            }
+        }
     }
 
     fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) {
@@ -662,12 +723,15 @@ impl<'a> InferenceContext<'a> {
         self.result.type_of_pat.insert(pat, ty);
     }
 
+    fn write_binding_ty(&mut self, id: BindingId, ty: Ty) {
+        self.result.type_of_binding.insert(id, ty);
+    }
+
     fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
         self.result.diagnostics.push(diagnostic);
     }
 
     fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
-        // FIXME use right resolver for block
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
         let ty = ctx.lower_ty(type_ref);
         let ty = self.insert_type_vars(ty);
@@ -681,11 +745,9 @@ impl<'a> InferenceContext<'a> {
     /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
     fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
         let data = c.data(Interner);
-        match data.value {
+        match &data.value {
             ConstValue::Concrete(cc) => match cc.interned {
-                hir_def::type_ref::ConstScalar::Unknown => {
-                    self.table.new_const_var(data.ty.clone())
-                }
+                crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
                 _ => c,
             },
             _ => c,
@@ -785,12 +847,11 @@ impl<'a> InferenceContext<'a> {
             Some(path) => path,
             None => return (self.err_ty(), None),
         };
-        let resolver = &self.resolver;
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
         // FIXME: this should resolve assoc items as well, see this example:
         // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
         let (resolution, unresolved) = if value_ns {
-            match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
+            match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
                 Some(ResolveValueResult::ValueNs(value)) => match value {
                     ValueNs::EnumVariantId(var) => {
                         let substs = ctx.substs_from_path(path, var.into(), true);
@@ -811,7 +872,7 @@ impl<'a> InferenceContext<'a> {
                 None => return (self.err_ty(), None),
             }
         } else {
-            match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+            match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
                 Some(it) => it,
                 None => return (self.err_ty(), None),
             }
@@ -866,7 +927,10 @@ impl<'a> InferenceContext<'a> {
                 // FIXME potentially resolve assoc type
                 (self.err_ty(), None)
             }
-            TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
+            TypeNs::AdtId(AdtId::EnumId(_))
+            | TypeNs::BuiltinType(_)
+            | TypeNs::TraitId(_)
+            | TypeNs::TraitAliasId(_) => {
                 // FIXME diagnostic
                 (self.err_ty(), None)
             }
@@ -1018,6 +1082,15 @@ impl<'a> InferenceContext<'a> {
         let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
         Some(struct_.into())
     }
+
+    fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
+        let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
+        if b_traits.peek().is_some() {
+            Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
+        } else {
+            Either::Right(&self.traits_in_scope)
+        }
+    }
 }
 
 /// When inferring an expression, we propagate downward whatever type hint we
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 3293534a068..48c91530266 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -50,11 +50,44 @@ fn success(
 #[derive(Clone, Debug)]
 pub(super) struct CoerceMany {
     expected_ty: Ty,
+    final_ty: Option<Ty>,
 }
 
 impl CoerceMany {
     pub(super) fn new(expected: Ty) -> Self {
-        CoerceMany { expected_ty: expected }
+        CoerceMany { expected_ty: expected, final_ty: None }
+    }
+
+    /// Returns the "expected type" with which this coercion was
+    /// constructed. This represents the "downward propagated" type
+    /// that was given to us at the start of typing whatever construct
+    /// we are typing (e.g., the match expression).
+    ///
+    /// Typically, this is used as the expected type when
+    /// type-checking each of the alternative expressions whose types
+    /// we are trying to merge.
+    pub(super) fn expected_ty(&self) -> Ty {
+        self.expected_ty.clone()
+    }
+
+    /// Returns the current "merged type", representing our best-guess
+    /// at the LUB of the expressions we've seen so far (if any). This
+    /// isn't *final* until you call `self.complete()`, which will return
+    /// the merged type.
+    pub(super) fn merged_ty(&self) -> Ty {
+        self.final_ty.clone().unwrap_or_else(|| self.expected_ty.clone())
+    }
+
+    pub(super) fn complete(self, ctx: &mut InferenceContext<'_>) -> Ty {
+        if let Some(final_ty) = self.final_ty {
+            final_ty
+        } else {
+            ctx.result.standard_types.never.clone()
+        }
+    }
+
+    pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
+        self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
     }
 
     /// Merge two types from different branches, with possible coercion.
@@ -76,25 +109,25 @@ impl CoerceMany {
         // Special case: two function types. Try to coerce both to
         // pointers to have a chance at getting a match. See
         // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
-        let sig = match (self.expected_ty.kind(Interner), expr_ty.kind(Interner)) {
+        let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
             (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
                 // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
                 // we should be coercing the closure to a fn pointer of the safety of the FnDef
                 cov_mark::hit!(coerce_fn_reification);
                 let sig =
-                    self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig");
+                    self.merged_ty().callable_sig(ctx.db).expect("FnDef without callable sig");
                 Some(sig)
             }
             _ => None,
         };
         if let Some(sig) = sig {
             let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
-            let result1 = ctx.table.coerce_inner(self.expected_ty.clone(), &target_ty);
+            let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
             let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
             if let (Ok(result1), Ok(result2)) = (result1, result2) {
                 ctx.table.register_infer_ok(result1);
                 ctx.table.register_infer_ok(result2);
-                return self.expected_ty = target_ty;
+                return self.final_ty = Some(target_ty);
             }
         }
 
@@ -102,25 +135,20 @@ impl CoerceMany {
         // type is a type variable and the new one is `!`, trying it the other
         // way around first would mean we make the type variable `!`, instead of
         // just marking it as possibly diverging.
-        if ctx.coerce(expr, &expr_ty, &self.expected_ty).is_ok() {
-            /* self.expected_ty is already correct */
-        } else if ctx.coerce(expr, &self.expected_ty, &expr_ty).is_ok() {
-            self.expected_ty = expr_ty;
+        if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
+            self.final_ty = Some(res);
+        } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
+            self.final_ty = Some(res);
         } else {
             if let Some(id) = expr {
                 ctx.result.type_mismatches.insert(
                     id.into(),
-                    TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty },
+                    TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() },
                 );
             }
             cov_mark::hit!(coerce_merge_fail_fallback);
-            /* self.expected_ty is already correct */
         }
     }
-
-    pub(super) fn complete(self) -> Ty {
-        self.expected_ty
-    }
 }
 
 pub fn could_coerce(
@@ -665,7 +693,7 @@ pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec<Adju
         .iter()
         .map(|(kind, _source)| match kind {
             // We do not know what kind of deref we require at this point yet
-            AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
+            AutoderefKind::Overloaded => Some(OverloadedDeref(None)),
             AutoderefKind::Builtin => None,
         })
         .zip(targets)
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 175fded8cca..535189ff028 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -15,7 +15,6 @@ use hir_def::{
     generics::TypeOrConstParamData,
     lang_item::LangItem,
     path::{GenericArg, GenericArgs},
-    resolver::resolver_for_expr,
     ConstParamId, FieldId, ItemContainerId, Lookup,
 };
 use hir_expand::name::{name, Name};
@@ -25,7 +24,9 @@ use syntax::ast::RangeOp;
 use crate::{
     autoderef::{self, Autoderef},
     consteval,
-    infer::{coerce::CoerceMany, find_continuable, BreakableKind},
+    infer::{
+        coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
+    },
     lower::{
         const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
     },
@@ -39,8 +40,8 @@ use crate::{
 };
 
 use super::{
-    coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges,
-    Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
+    coerce::auto_deref_adjust_steps, find_breakable, BreakableContext, Diverges, Expectation,
+    InferenceContext, InferenceDiagnostic, TypeMismatch,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -58,6 +59,10 @@ impl<'a> InferenceContext<'a> {
         ty
     }
 
+    pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId) -> Ty {
+        self.infer_expr_inner(tgt_expr, &Expectation::None)
+    }
+
     /// Infer type of expression with possibly implicit coerce to the expected type.
     /// Return the type after possible coercion.
     pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
@@ -78,6 +83,30 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
+    pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+        let ty = self.infer_expr_inner(expr, expected);
+        // While we don't allow *arbitrary* coercions here, we *do* allow
+        // coercions from ! to `expected`.
+        if ty.is_never() {
+            if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
+                return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
+                    target.clone()
+                } else {
+                    self.err_ty()
+                };
+            }
+
+            let adj_ty = self.table.new_type_var();
+            self.write_expr_adj(
+                expr,
+                vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
+            );
+            adj_ty
+        } else {
+            ty
+        }
+    }
+
     fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         self.db.unwind_if_cancelled();
 
@@ -85,7 +114,7 @@ impl<'a> InferenceContext<'a> {
             Expr::Missing => self.err_ty(),
             &Expr::If { condition, then_branch, else_branch } => {
                 let expected = &expected.adjust_for_branches(&mut self.table);
-                self.infer_expr(
+                self.infer_expr_coerce_never(
                     condition,
                     &Expectation::HasType(self.result.standard_types.bool_.clone()),
                 );
@@ -97,59 +126,39 @@ impl<'a> InferenceContext<'a> {
                 both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
                 let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
                 coerce.coerce(self, Some(then_branch), &then_ty);
-                let else_ty = match else_branch {
-                    Some(else_branch) => self.infer_expr_inner(else_branch, expected),
-                    None => TyBuilder::unit(),
-                };
+                match else_branch {
+                    Some(else_branch) => {
+                        let else_ty = self.infer_expr_inner(else_branch, expected);
+                        coerce.coerce(self, Some(else_branch), &else_ty);
+                    }
+                    None => {
+                        coerce.coerce_forced_unit(self);
+                    }
+                }
                 both_arms_diverge &= self.diverges;
-                // FIXME: create a synthetic `else {}` so we have something to refer to here instead of None?
-                coerce.coerce(self, else_branch, &else_ty);
 
                 self.diverges = condition_diverges | both_arms_diverge;
 
-                coerce.complete()
+                coerce.complete(self)
             }
             &Expr::Let { pat, expr } => {
                 let input_ty = self.infer_expr(expr, &Expectation::none());
-                self.infer_pat(pat, &input_ty, BindingMode::default());
+                self.infer_top_pat(pat, &input_ty);
                 self.result.standard_types.bool_.clone()
             }
             Expr::Block { statements, tail, label, id: _ } => {
-                let old_resolver = mem::replace(
-                    &mut self.resolver,
-                    resolver_for_expr(self.db.upcast(), self.owner, tgt_expr),
-                );
-                let ty = match label {
-                    Some(_) => {
-                        let break_ty = self.table.new_type_var();
-                        let (breaks, ty) = self.with_breakable_ctx(
-                            BreakableKind::Block,
-                            break_ty.clone(),
-                            *label,
-                            |this| {
-                                this.infer_block(
-                                    tgt_expr,
-                                    statements,
-                                    *tail,
-                                    &Expectation::has_type(break_ty),
-                                )
-                            },
-                        );
-                        breaks.unwrap_or(ty)
-                    }
-                    None => self.infer_block(tgt_expr, statements, *tail, expected),
-                };
-                self.resolver = old_resolver;
-                ty
+                self.infer_block(tgt_expr, statements, *tail, *label, expected)
             }
-            Expr::Unsafe { body } => self.infer_expr(*body, expected),
-            Expr::Const { body } => {
-                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                    this.infer_expr(*body, expected)
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.infer_block(tgt_expr, statements, *tail, None, expected)
+            }
+            Expr::Const { id: _, statements, tail } => {
+                self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                    this.infer_block(tgt_expr, statements, *tail, None, expected)
                 })
                 .1
             }
-            Expr::TryBlock { body } => {
+            Expr::TryBlock { id: _, statements, tail } => {
                 // The type that is returned from the try block
                 let try_ty = self.table.new_type_var();
                 if let Some(ty) = expected.only_has_type(&mut self.table) {
@@ -160,28 +169,41 @@ impl<'a> InferenceContext<'a> {
                 let ok_ty =
                     self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
 
-                self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
-                    this.infer_expr(*body, &Expectation::has_type(ok_ty));
-                });
-
+                self.infer_block(
+                    tgt_expr,
+                    statements,
+                    *tail,
+                    None,
+                    &Expectation::has_type(ok_ty.clone()),
+                );
                 try_ty
             }
-            Expr::Async { body } => {
+            Expr::Async { id: _, statements, tail } => {
                 let ret_ty = self.table.new_type_var();
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+                let prev_ret_coercion =
+                    mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
 
                 let (_, inner_ty) =
-                    self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                        this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
+                    self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                        this.infer_block(
+                            tgt_expr,
+                            statements,
+                            *tail,
+                            None,
+                            &Expectation::has_type(ret_ty),
+                        )
                     });
 
                 self.diverges = prev_diverges;
                 self.return_ty = prev_ret_ty;
+                self.return_coercion = prev_ret_coercion;
 
                 // Use the first type parameter as the output type of future.
                 // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
-                let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
+                let impl_trait_id =
+                    crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
                 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)
@@ -191,7 +213,7 @@ impl<'a> InferenceContext<'a> {
                 // let ty = expected.coercion_target_type(&mut self.table);
                 let ty = self.table.new_type_var();
                 let (breaks, ()) =
-                    self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
+                    self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
                         this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
                     });
 
@@ -204,7 +226,7 @@ impl<'a> InferenceContext<'a> {
                 }
             }
             &Expr::While { condition, body, label } => {
-                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+                self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
                     this.infer_expr(
                         condition,
                         &Expectation::HasType(this.result.standard_types.bool_.clone()),
@@ -220,11 +242,13 @@ impl<'a> InferenceContext<'a> {
                 let iterable_ty = self.infer_expr(iterable, &Expectation::none());
                 let into_iter_ty =
                     self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
-                let pat_ty =
-                    self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
+                let pat_ty = self
+                    .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item());
 
-                self.infer_pat(pat, &pat_ty, BindingMode::default());
-                self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
+                self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty);
+
+                self.infer_top_pat(pat, &pat_ty);
+                self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
                     this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
                 });
 
@@ -298,20 +322,24 @@ impl<'a> InferenceContext<'a> {
 
                 // Now go through the argument patterns
                 for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
-                    self.infer_pat(*arg_pat, &arg_ty, BindingMode::default());
+                    self.infer_top_pat(*arg_pat, &arg_ty);
                 }
 
+                // FIXME: lift these out into a struct
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+                let prev_ret_coercion =
+                    mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
                 let prev_resume_yield_tys =
                     mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
 
-                self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
-                    this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
+                self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+                    this.infer_return(*body);
                 });
 
                 self.diverges = prev_diverges;
                 self.return_ty = prev_ret_ty;
+                self.return_coercion = prev_ret_coercion;
                 self.resume_yield_tys = prev_resume_yield_tys;
 
                 ty
@@ -348,7 +376,13 @@ impl<'a> InferenceContext<'a> {
                         }
                         (params, ret_ty)
                     }
-                    None => (Vec::new(), self.err_ty()), // FIXME diagnostic
+                    None => {
+                        self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
+                            call_expr: tgt_expr,
+                            found: callee_ty.clone(),
+                        });
+                        (Vec::new(), self.err_ty())
+                    }
                 };
                 let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
                 self.register_obligations_for_call(&callee_ty);
@@ -381,92 +415,109 @@ impl<'a> InferenceContext<'a> {
             Expr::Match { expr, arms } => {
                 let input_ty = self.infer_expr(*expr, &Expectation::none());
 
-                let expected = expected.adjust_for_branches(&mut self.table);
-
-                let result_ty = if arms.is_empty() {
+                if arms.is_empty() {
+                    self.diverges = Diverges::Always;
                     self.result.standard_types.never.clone()
                 } else {
-                    expected.coercion_target_type(&mut self.table)
-                };
-                let mut coerce = CoerceMany::new(result_ty);
-
-                let matchee_diverges = self.diverges;
-                let mut all_arms_diverge = Diverges::Always;
-
-                for arm in arms.iter() {
-                    self.diverges = Diverges::Maybe;
-                    let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
-                    if let Some(guard_expr) = arm.guard {
-                        self.infer_expr(
-                            guard_expr,
-                            &Expectation::HasType(self.result.standard_types.bool_.clone()),
-                        );
+                    let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+                    let mut all_arms_diverge = Diverges::Always;
+                    for arm in arms.iter() {
+                        let input_ty = self.resolve_ty_shallow(&input_ty);
+                        self.infer_top_pat(arm.pat, &input_ty);
                     }
 
-                    let arm_ty = self.infer_expr_inner(arm.expr, &expected);
-                    all_arms_diverge &= self.diverges;
-                    coerce.coerce(self, Some(arm.expr), &arm_ty);
+                    let expected = expected.adjust_for_branches(&mut self.table);
+                    let result_ty = match &expected {
+                        // We don't coerce to `()` so that if the match expression is a
+                        // statement it's branches can have any consistent type.
+                        Expectation::HasType(ty) if *ty != self.result.standard_types.unit => {
+                            ty.clone()
+                        }
+                        _ => self.table.new_type_var(),
+                    };
+                    let mut coerce = CoerceMany::new(result_ty);
+
+                    for arm in arms.iter() {
+                        if let Some(guard_expr) = arm.guard {
+                            self.diverges = Diverges::Maybe;
+                            self.infer_expr_coerce_never(
+                                guard_expr,
+                                &Expectation::HasType(self.result.standard_types.bool_.clone()),
+                            );
+                        }
+                        self.diverges = Diverges::Maybe;
+
+                        let arm_ty = self.infer_expr_inner(arm.expr, &expected);
+                        all_arms_diverge &= self.diverges;
+                        coerce.coerce(self, Some(arm.expr), &arm_ty);
+                    }
+
+                    self.diverges = matchee_diverges | all_arms_diverge;
+
+                    coerce.complete(self)
                 }
-
-                self.diverges = matchee_diverges | all_arms_diverge;
-
-                coerce.complete()
             }
             Expr::Path(p) => {
-                // FIXME this could be more efficient...
-                let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
-                self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or_else(|| self.err_ty())
+                let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+                let ty = self.infer_path(p, tgt_expr.into()).unwrap_or_else(|| self.err_ty());
+                self.resolver.reset_to_guard(g);
+                ty
             }
             Expr::Continue { label } => {
                 if let None = find_continuable(&mut self.breakables, label.as_ref()) {
                     self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
                         expr: tgt_expr,
                         is_break: false,
+                        bad_value_break: false,
                     });
                 };
                 self.result.standard_types.never.clone()
             }
             Expr::Break { expr, label } => {
                 let val_ty = if let Some(expr) = *expr {
-                    self.infer_expr(expr, &Expectation::none())
+                    let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
+                        Some(ctxt) => match &ctxt.coerce {
+                            Some(coerce) => coerce.expected_ty(),
+                            None => {
+                                self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
+                                    expr: tgt_expr,
+                                    is_break: true,
+                                    bad_value_break: true,
+                                });
+                                self.err_ty()
+                            }
+                        },
+                        None => self.err_ty(),
+                    };
+                    self.infer_expr_inner(expr, &Expectation::HasType(opt_coerce_to))
                 } else {
                     TyBuilder::unit()
                 };
 
                 match find_breakable(&mut self.breakables, label.as_ref()) {
-                    Some(ctxt) => {
-                        // avoiding the borrowck
-                        let mut coerce = mem::replace(
-                            &mut ctxt.coerce,
-                            CoerceMany::new(expected.coercion_target_type(&mut self.table)),
-                        );
+                    Some(ctxt) => match ctxt.coerce.take() {
+                        Some(mut coerce) => {
+                            coerce.coerce(self, *expr, &val_ty);
 
-                        // FIXME: create a synthetic `()` during lowering so we have something to refer to here?
-                        coerce.coerce(self, *expr, &val_ty);
-
-                        let ctxt = find_breakable(&mut self.breakables, label.as_ref())
-                            .expect("breakable stack changed during coercion");
-                        ctxt.coerce = coerce;
-                        ctxt.may_break = true;
-                    }
+                            // Avoiding borrowck
+                            let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+                                .expect("breakable stack changed during coercion");
+                            ctxt.may_break = true;
+                            ctxt.coerce = Some(coerce);
+                        }
+                        None => ctxt.may_break = true,
+                    },
                     None => {
                         self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
                             expr: tgt_expr,
                             is_break: true,
+                            bad_value_break: false,
                         });
                     }
                 }
                 self.result.standard_types.never.clone()
             }
-            Expr::Return { expr } => {
-                if let Some(expr) = expr {
-                    self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
-                } else {
-                    let unit = TyBuilder::unit();
-                    let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
-                }
-                self.result.standard_types.never.clone()
-            }
+            &Expr::Return { expr } => self.infer_expr_return(expr),
             Expr::Yield { expr } => {
                 if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
                     if let Some(expr) = expr {
@@ -483,7 +534,7 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Yeet { expr } => {
                 if let &Some(expr) = expr {
-                    self.infer_expr_inner(expr, &Expectation::None);
+                    self.infer_expr_no_expect(expr);
                 }
                 self.result.standard_types.never.clone()
             }
@@ -524,71 +575,7 @@ impl<'a> InferenceContext<'a> {
                 }
                 ty
             }
-            Expr::Field { expr, name } => {
-                let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
-
-                let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
-                let mut private_field = None;
-                let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
-                    let (field_id, parameters) = match derefed_ty.kind(Interner) {
-                        TyKind::Tuple(_, substs) => {
-                            return name.as_tuple_index().and_then(|idx| {
-                                substs
-                                    .as_slice(Interner)
-                                    .get(idx)
-                                    .map(|a| a.assert_ty_ref(Interner))
-                                    .cloned()
-                            });
-                        }
-                        TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
-                            let local_id = self.db.struct_data(*s).variant_data.field(name)?;
-                            let field = FieldId { parent: (*s).into(), local_id };
-                            (field, parameters.clone())
-                        }
-                        TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
-                            let local_id = self.db.union_data(*u).variant_data.field(name)?;
-                            let field = FieldId { parent: (*u).into(), local_id };
-                            (field, parameters.clone())
-                        }
-                        _ => return None,
-                    };
-                    let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
-                        .is_visible_from(self.db.upcast(), self.resolver.module());
-                    if !is_visible {
-                        if private_field.is_none() {
-                            private_field = Some(field_id);
-                        }
-                        return None;
-                    }
-                    // can't have `write_field_resolution` here because `self.table` is borrowed :(
-                    self.result.field_resolutions.insert(tgt_expr, field_id);
-                    let ty = self.db.field_types(field_id.parent)[field_id.local_id]
-                        .clone()
-                        .substitute(Interner, &parameters);
-                    Some(ty)
-                });
-                let ty = match ty {
-                    Some(ty) => {
-                        let adjustments = auto_deref_adjust_steps(&autoderef);
-                        self.write_expr_adj(*expr, adjustments);
-                        let ty = self.insert_type_vars(ty);
-                        let ty = self.normalize_associated_types_in(ty);
-                        ty
-                    }
-                    _ => {
-                        // Write down the first private field resolution if we found no field
-                        // This aids IDE features for private fields like goto def
-                        if let Some(field) = private_field {
-                            self.result.field_resolutions.insert(tgt_expr, field);
-                            self.result
-                                .diagnostics
-                                .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
-                        }
-                        self.err_ty()
-                    }
-                };
-                ty
-            }
+            Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
             Expr::Await { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -611,7 +598,7 @@ impl<'a> InferenceContext<'a> {
             Expr::Cast { expr, type_ref } => {
                 let cast_ty = self.make_ty(type_ref);
                 // FIXME: propagate the "castable to" expectation
-                let _inner_ty = self.infer_expr_inner(*expr, &Expectation::None);
+                let _inner_ty = self.infer_expr_no_expect(*expr);
                 // FIXME check the cast...
                 cast_ty
             }
@@ -807,49 +794,7 @@ impl<'a> InferenceContext<'a> {
 
                 TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner)
             }
-            Expr::Array(array) => {
-                let elem_ty =
-                    match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) {
-                        Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
-                        _ => self.table.new_type_var(),
-                    };
-                let mut coerce = CoerceMany::new(elem_ty.clone());
-
-                let expected = Expectation::has_type(elem_ty.clone());
-                let len = match array {
-                    Array::ElementList { elements, .. } => {
-                        for &expr in elements.iter() {
-                            let cur_elem_ty = self.infer_expr_inner(expr, &expected);
-                            coerce.coerce(self, Some(expr), &cur_elem_ty);
-                        }
-                        consteval::usize_const(Some(elements.len() as u128))
-                    }
-                    &Array::Repeat { initializer, repeat } => {
-                        self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
-                        self.infer_expr(
-                            repeat,
-                            &Expectation::HasType(
-                                TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
-                            ),
-                        );
-
-                        if let Some(g_def) = self.owner.as_generic_def_id() {
-                            let generics = generics(self.db.upcast(), g_def);
-                            consteval::eval_to_const(
-                                repeat,
-                                ParamLoweringMode::Placeholder,
-                                self,
-                                || generics,
-                                DebruijnIndex::INNERMOST,
-                            )
-                        } else {
-                            consteval::usize_const(None)
-                        }
-                    }
-                };
-
-                TyKind::Array(coerce.complete(), len).intern(Interner)
-            }
+            Expr::Array(array) => self.infer_expr_array(array, expected),
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => self.result.standard_types.bool_.clone(),
                 Literal::String(..) => {
@@ -859,7 +804,11 @@ impl<'a> InferenceContext<'a> {
                 Literal::ByteString(bs) => {
                     let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
 
-                    let len = consteval::usize_const(Some(bs.len() as u128));
+                    let len = consteval::usize_const(
+                        self.db,
+                        Some(bs.len() as u128),
+                        self.resolver.krate(),
+                    );
 
                     let array_type = TyKind::Array(byte_type, len).intern(Interner);
                     TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
@@ -904,6 +853,97 @@ impl<'a> InferenceContext<'a> {
         ty
     }
 
+    fn infer_expr_array(
+        &mut self,
+        array: &Array,
+        expected: &Expectation,
+    ) -> chalk_ir::Ty<Interner> {
+        let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind(Interner)) {
+            Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st.clone(),
+            _ => self.table.new_type_var(),
+        };
+
+        let krate = self.resolver.krate();
+
+        let expected = Expectation::has_type(elem_ty.clone());
+        let (elem_ty, len) = match array {
+            Array::ElementList { elements, .. } if elements.is_empty() => {
+                (elem_ty, consteval::usize_const(self.db, Some(0), krate))
+            }
+            Array::ElementList { elements, .. } => {
+                let mut coerce = CoerceMany::new(elem_ty.clone());
+                for &expr in elements.iter() {
+                    let cur_elem_ty = self.infer_expr_inner(expr, &expected);
+                    coerce.coerce(self, Some(expr), &cur_elem_ty);
+                }
+                (
+                    coerce.complete(self),
+                    consteval::usize_const(self.db, Some(elements.len() as u128), krate),
+                )
+            }
+            &Array::Repeat { initializer, repeat } => {
+                self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
+                self.infer_expr(
+                    repeat,
+                    &Expectation::HasType(
+                        TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
+                    ),
+                );
+
+                (
+                    elem_ty,
+                    if let Some(g_def) = self.owner.as_generic_def_id() {
+                        let generics = generics(self.db.upcast(), g_def);
+                        consteval::eval_to_const(
+                            repeat,
+                            ParamLoweringMode::Placeholder,
+                            self,
+                            || generics,
+                            DebruijnIndex::INNERMOST,
+                        )
+                    } else {
+                        consteval::usize_const(self.db, None, krate)
+                    },
+                )
+            }
+        };
+
+        TyKind::Array(elem_ty, len).intern(Interner)
+    }
+
+    pub(super) fn infer_return(&mut self, expr: ExprId) {
+        let ret_ty = self
+            .return_coercion
+            .as_mut()
+            .expect("infer_return called outside function body")
+            .expected_ty();
+        let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
+        let mut coerce_many = self.return_coercion.take().unwrap();
+        coerce_many.coerce(self, Some(expr), &return_expr_ty);
+        self.return_coercion = Some(coerce_many);
+    }
+
+    fn infer_expr_return(&mut self, expr: Option<ExprId>) -> Ty {
+        match self.return_coercion {
+            Some(_) => {
+                if let Some(expr) = expr {
+                    self.infer_return(expr);
+                } else {
+                    let mut coerce = self.return_coercion.take().unwrap();
+                    coerce.coerce_forced_unit(self);
+                    self.return_coercion = Some(coerce);
+                }
+            }
+            None => {
+                // FIXME: diagnose return outside of function
+                if let Some(expr) = expr {
+                    self.infer_expr_no_expect(expr);
+                }
+            }
+        }
+        self.result.standard_types.never.clone()
+    }
+
     fn infer_expr_box(&mut self, inner_expr: ExprId, expected: &Expectation) -> Ty {
         if let Some(box_id) = self.resolve_boxed_box() {
             let table = &mut self.table;
@@ -982,8 +1022,11 @@ impl<'a> InferenceContext<'a> {
                     // type and length). This should not be just an error type,
                     // because we are to compute the unifiability of this type and
                     // `rhs_ty` in the end of this function to issue type mismatches.
-                    _ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None))
-                        .intern(Interner),
+                    _ => TyKind::Array(
+                        self.err_ty(),
+                        crate::consteval::usize_const(self.db, None, self.resolver.krate()),
+                    )
+                    .intern(Interner),
                 }
             }
             Expr::RecordLit { path, fields, .. } => {
@@ -1123,65 +1166,211 @@ impl<'a> InferenceContext<'a> {
         expr: ExprId,
         statements: &[Statement],
         tail: Option<ExprId>,
+        label: Option<LabelId>,
         expected: &Expectation,
     ) -> Ty {
-        for stmt in statements {
-            match stmt {
-                Statement::Let { pat, type_ref, initializer, else_branch } => {
-                    let decl_ty = type_ref
-                        .as_ref()
-                        .map(|tr| self.make_ty(tr))
-                        .unwrap_or_else(|| self.err_ty());
+        let coerce_ty = expected.coercion_target_type(&mut self.table);
+        let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
 
-                    // Always use the declared type when specified
-                    let mut ty = decl_ty.clone();
+        let (break_ty, ty) =
+            self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
+                for stmt in statements {
+                    match stmt {
+                        Statement::Let { pat, type_ref, initializer, else_branch } => {
+                            let decl_ty = type_ref
+                                .as_ref()
+                                .map(|tr| this.make_ty(tr))
+                                .unwrap_or_else(|| this.table.new_type_var());
 
-                    if let Some(expr) = initializer {
-                        let actual_ty =
-                            self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()));
-                        if decl_ty.is_unknown() {
-                            ty = actual_ty;
+                            let ty = if let Some(expr) = initializer {
+                                let ty = if contains_explicit_ref_binding(&this.body, *pat) {
+                                    this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
+                                } else {
+                                    this.infer_expr_coerce(
+                                        *expr,
+                                        &Expectation::has_type(decl_ty.clone()),
+                                    )
+                                };
+                                if type_ref.is_some() {
+                                    decl_ty
+                                } else {
+                                    ty
+                                }
+                            } else {
+                                decl_ty
+                            };
+
+                            this.infer_top_pat(*pat, &ty);
+
+                            if let Some(expr) = else_branch {
+                                let previous_diverges =
+                                    mem::replace(&mut this.diverges, Diverges::Maybe);
+                                this.infer_expr_coerce(
+                                    *expr,
+                                    &Expectation::HasType(this.result.standard_types.never.clone()),
+                                );
+                                this.diverges = previous_diverges;
+                            }
+                        }
+                        &Statement::Expr { expr, has_semi } => {
+                            if has_semi {
+                                this.infer_expr(expr, &Expectation::none());
+                            } else {
+                                this.infer_expr_coerce(
+                                    expr,
+                                    &Expectation::HasType(this.result.standard_types.unit.clone()),
+                                );
+                            }
                         }
                     }
+                }
 
-                    if let Some(expr) = else_branch {
-                        self.infer_expr_coerce(
-                            *expr,
-                            &Expectation::HasType(self.result.standard_types.never.clone()),
-                        );
+                // FIXME: This should make use of the breakable CoerceMany
+                if let Some(expr) = tail {
+                    this.infer_expr_coerce(expr, expected)
+                } else {
+                    // Citing rustc: if there is no explicit tail expression,
+                    // that is typically equivalent to a tail expression
+                    // of `()` -- except if the block diverges. In that
+                    // case, there is no value supplied from the tail
+                    // expression (assuming there are no other breaks,
+                    // this implies that the type of the block will be
+                    // `!`).
+                    if this.diverges.is_always() {
+                        // we don't even make an attempt at coercion
+                        this.table.new_maybe_never_var()
+                    } else if let Some(t) = expected.only_has_type(&mut this.table) {
+                        if this
+                            .coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
+                            .is_err()
+                        {
+                            this.result.type_mismatches.insert(
+                                expr.into(),
+                                TypeMismatch {
+                                    expected: t.clone(),
+                                    actual: this.result.standard_types.unit.clone(),
+                                },
+                            );
+                        }
+                        t
+                    } else {
+                        this.result.standard_types.unit.clone()
                     }
+                }
+            });
+        self.resolver.reset_to_guard(g);
 
-                    self.infer_pat(*pat, &ty, BindingMode::default());
+        break_ty.unwrap_or(ty)
+    }
+
+    fn lookup_field(
+        &mut self,
+        receiver_ty: &Ty,
+        name: &Name,
+    ) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
+        let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone());
+        let mut private_field = None;
+        let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
+            let (field_id, parameters) = match derefed_ty.kind(Interner) {
+                TyKind::Tuple(_, substs) => {
+                    return name.as_tuple_index().and_then(|idx| {
+                        substs
+                            .as_slice(Interner)
+                            .get(idx)
+                            .map(|a| a.assert_ty_ref(Interner))
+                            .cloned()
+                            .map(|ty| (None, ty))
+                    });
                 }
-                Statement::Expr { expr, .. } => {
-                    self.infer_expr(*expr, &Expectation::none());
+                TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
+                    let local_id = self.db.struct_data(*s).variant_data.field(name)?;
+                    let field = FieldId { parent: (*s).into(), local_id };
+                    (field, parameters.clone())
                 }
+                TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
+                    let local_id = self.db.union_data(*u).variant_data.field(name)?;
+                    let field = FieldId { parent: (*u).into(), local_id };
+                    (field, parameters.clone())
+                }
+                _ => return None,
+            };
+            let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id]
+                .is_visible_from(self.db.upcast(), self.resolver.module());
+            if !is_visible {
+                if private_field.is_none() {
+                    private_field = Some((field_id, parameters));
+                }
+                return None;
             }
-        }
+            let ty = self.db.field_types(field_id.parent)[field_id.local_id]
+                .clone()
+                .substitute(Interner, &parameters);
+            Some((Some(field_id), ty))
+        });
 
-        if let Some(expr) = tail {
-            self.infer_expr_coerce(expr, expected)
-        } else {
-            // Citing rustc: if there is no explicit tail expression,
-            // that is typically equivalent to a tail expression
-            // of `()` -- except if the block diverges. In that
-            // case, there is no value supplied from the tail
-            // expression (assuming there are no other breaks,
-            // this implies that the type of the block will be
-            // `!`).
-            if self.diverges.is_always() {
-                // we don't even make an attempt at coercion
-                self.table.new_maybe_never_var()
-            } else if let Some(t) = expected.only_has_type(&mut self.table) {
-                if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
-                    self.result.type_mismatches.insert(
-                        expr.into(),
-                        TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
-                    );
+        Some(match res {
+            Some((field_id, ty)) => {
+                let adjustments = auto_deref_adjust_steps(&autoderef);
+                let ty = self.insert_type_vars(ty);
+                let ty = self.normalize_associated_types_in(ty);
+
+                (ty, field_id, adjustments, true)
+            }
+            None => {
+                let (field_id, subst) = private_field?;
+                let adjustments = auto_deref_adjust_steps(&autoderef);
+                let ty = self.db.field_types(field_id.parent)[field_id.local_id]
+                    .clone()
+                    .substitute(Interner, &subst);
+                let ty = self.insert_type_vars(ty);
+                let ty = self.normalize_associated_types_in(ty);
+
+                (ty, Some(field_id), adjustments, false)
+            }
+        })
+    }
+
+    fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
+        let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
+        match self.lookup_field(&receiver_ty, name) {
+            Some((ty, field_id, adjustments, is_public)) => {
+                self.write_expr_adj(receiver, adjustments);
+                if let Some(field_id) = field_id {
+                    self.result.field_resolutions.insert(tgt_expr, field_id);
                 }
-                t
-            } else {
-                TyBuilder::unit()
+                if !is_public {
+                    if let Some(field) = field_id {
+                        // FIXME: Merge this diagnostic into UnresolvedField?
+                        self.result
+                            .diagnostics
+                            .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
+                    }
+                }
+                ty
+            }
+            None => {
+                // no field found,
+                let method_with_same_name_exists = {
+                    self.get_traits_in_scope();
+
+                    let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+                    method_resolution::lookup_method(
+                        self.db,
+                        &canonicalized_receiver.value,
+                        self.trait_env.clone(),
+                        self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
+                        VisibleFromModule::Filter(self.resolver.module()),
+                        name,
+                    )
+                    .is_some()
+                };
+                self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
+                    expr: tgt_expr,
+                    receiver: receiver_ty,
+                    name: name.clone(),
+                    method_with_same_name_exists,
+                });
+                self.err_ty()
             }
         }
     }
@@ -1198,13 +1387,11 @@ impl<'a> InferenceContext<'a> {
         let receiver_ty = self.infer_expr(receiver, &Expectation::none());
         let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
 
-        let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
-
         let resolved = method_resolution::lookup_method(
             self.db,
             &canonicalized_receiver.value,
             self.trait_env.clone(),
-            &traits_in_scope,
+            self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
             VisibleFromModule::Filter(self.resolver.module()),
             method_name,
         );
@@ -1223,11 +1410,30 @@ impl<'a> InferenceContext<'a> {
                 }
                 (ty, self.db.value_ty(func.into()), substs)
             }
-            None => (
-                receiver_ty,
-                Binders::empty(Interner, self.err_ty()),
-                Substitution::empty(Interner),
-            ),
+            None => {
+                let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
+                {
+                    Some((ty, field_id, adjustments, _public)) => {
+                        self.write_expr_adj(receiver, adjustments);
+                        if let Some(field_id) = field_id {
+                            self.result.field_resolutions.insert(tgt_expr, field_id);
+                        }
+                        Some(ty)
+                    }
+                    None => None,
+                };
+                self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall {
+                    expr: tgt_expr,
+                    receiver: receiver_ty.clone(),
+                    name: method_name.clone(),
+                    field_with_same_name: field_with_same_name_exists,
+                });
+                (
+                    receiver_ty,
+                    Binders::empty(Interner, self.err_ty()),
+                    Substitution::empty(Interner),
+                )
+            }
         };
         let method_ty = method_ty.substitute(Interner, &substs);
         self.register_obligations_for_call(&method_ty);
@@ -1636,16 +1842,16 @@ impl<'a> InferenceContext<'a> {
     fn with_breakable_ctx<T>(
         &mut self,
         kind: BreakableKind,
-        ty: Ty,
+        ty: Option<Ty>,
         label: Option<LabelId>,
         cb: impl FnOnce(&mut Self) -> T,
     ) -> (Option<Ty>, T) {
         self.breakables.push({
             let label = label.map(|label| self.body[label].name.clone());
-            BreakableContext { kind, may_break: false, coerce: CoerceMany::new(ty), label }
+            BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
         });
         let res = cb(self);
         let ctx = self.breakables.pop().expect("breakable stack broken");
-        (ctx.may_break.then(|| ctx.coerce.complete()), res)
+        (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res)
     }
 }
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index f154dac8e87..0f49e837881 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -4,22 +4,60 @@ use std::iter::repeat_with;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    expr::{BindingAnnotation, Expr, Literal, Pat, PatId},
+    body::Body,
+    expr::{
+        Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
+        RecordFieldPat,
+    },
     path::Path,
-    type_ref::ConstScalar,
 };
 use hir_expand::name::Name;
 
 use crate::{
-    consteval::intern_const_scalar,
+    consteval::{try_const_usize, usize_const},
     infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
     lower::lower_to_chalk_mutability,
     primitive::UintTy,
-    static_lifetime, ConcreteConst, ConstValue, Interner, Scalar, Substitution, Ty, TyBuilder,
-    TyExt, TyKind,
+    static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
 };
 
-use super::PatLike;
+/// Used to generalize patterns and assignee expressions.
+pub(super) trait PatLike: Into<ExprOrPatId> + Copy {
+    type BindingMode: Copy;
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        default_bm: Self::BindingMode,
+    ) -> Ty;
+}
+
+impl PatLike for ExprId {
+    type BindingMode = ();
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        (): Self::BindingMode,
+    ) -> Ty {
+        this.infer_assignee_expr(id, expected_ty)
+    }
+}
+
+impl PatLike for PatId {
+    type BindingMode = BindingMode;
+
+    fn infer(
+        this: &mut InferenceContext<'_>,
+        id: Self,
+        expected_ty: &Ty,
+        default_bm: Self::BindingMode,
+    ) -> Ty {
+        this.infer_pat(id, expected_ty, default_bm)
+    }
+}
 
 impl<'a> InferenceContext<'a> {
     /// Infers type for tuple struct pattern or its corresponding assignee expression.
@@ -112,6 +150,7 @@ impl<'a> InferenceContext<'a> {
         ellipsis: Option<usize>,
         subs: &[T],
     ) -> Ty {
+        let expected = self.resolve_ty_shallow(expected);
         let expectations = match expected.as_tuple() {
             Some(parameters) => &*parameters.as_slice(Interner),
             _ => &[],
@@ -145,12 +184,11 @@ impl<'a> InferenceContext<'a> {
             .intern(Interner)
     }
 
-    pub(super) fn infer_pat(
-        &mut self,
-        pat: PatId,
-        expected: &Ty,
-        mut default_bm: BindingMode,
-    ) -> Ty {
+    pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
+        self.infer_pat(pat, expected, BindingMode::default());
+    }
+
+    fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
         let mut expected = self.resolve_ty_shallow(expected);
 
         if is_non_ref_pat(self.body, pat) {
@@ -185,30 +223,17 @@ impl<'a> InferenceContext<'a> {
                 self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
             }
             Pat::Or(pats) => {
-                if let Some((first_pat, rest)) = pats.split_first() {
-                    let ty = self.infer_pat(*first_pat, &expected, default_bm);
-                    for pat in rest {
-                        self.infer_pat(*pat, &expected, default_bm);
-                    }
-                    ty
-                } else {
-                    self.err_ty()
+                for pat in pats.iter() {
+                    self.infer_pat(*pat, &expected, default_bm);
                 }
+                expected.clone()
             }
-            Pat::Ref { pat, mutability } => {
-                let mutability = lower_to_chalk_mutability(*mutability);
-                let expectation = match expected.as_reference() {
-                    Some((inner_ty, _lifetime, exp_mut)) => {
-                        if mutability != exp_mut {
-                            // FIXME: emit type error?
-                        }
-                        inner_ty.clone()
-                    }
-                    _ => self.result.standard_types.unknown.clone(),
-                };
-                let subty = self.infer_pat(*pat, &expectation, default_bm);
-                TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
-            }
+            &Pat::Ref { pat, mutability } => self.infer_ref_pat(
+                pat,
+                lower_to_chalk_mutability(mutability),
+                &expected,
+                default_bm,
+            ),
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
                 .infer_tuple_struct_pat_like(
                     p.as_deref(),
@@ -223,72 +248,14 @@ impl<'a> InferenceContext<'a> {
                 self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
             }
             Pat::Path(path) => {
-                // FIXME use correct resolver for the surrounding expression
-                let resolver = self.resolver.clone();
-                self.infer_path(&resolver, path, pat.into()).unwrap_or_else(|| self.err_ty())
+                // FIXME update resolver for the surrounding expression
+                self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty())
             }
-            Pat::Bind { mode, name: _, subpat } => {
-                let mode = if mode == &BindingAnnotation::Unannotated {
-                    default_bm
-                } else {
-                    BindingMode::convert(*mode)
-                };
-                self.result.pat_binding_modes.insert(pat, mode);
-
-                let inner_ty = match subpat {
-                    Some(subpat) => self.infer_pat(*subpat, &expected, default_bm),
-                    None => expected,
-                };
-                let inner_ty = self.insert_type_vars_shallow(inner_ty);
-
-                let bound_ty = match mode {
-                    BindingMode::Ref(mutability) => {
-                        TyKind::Ref(mutability, static_lifetime(), inner_ty.clone())
-                            .intern(Interner)
-                    }
-                    BindingMode::Move => inner_ty.clone(),
-                };
-                self.write_pat_ty(pat, bound_ty);
-                return inner_ty;
+            Pat::Bind { id, subpat } => {
+                return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
             }
             Pat::Slice { prefix, slice, suffix } => {
-                let elem_ty = match expected.kind(Interner) {
-                    TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
-                    _ => self.err_ty(),
-                };
-
-                for &pat_id in prefix.iter().chain(suffix.iter()) {
-                    self.infer_pat(pat_id, &elem_ty, default_bm);
-                }
-
-                if let &Some(slice_pat_id) = slice {
-                    let rest_pat_ty = match expected.kind(Interner) {
-                        TyKind::Array(_, length) => {
-                            let len = match length.data(Interner).value {
-                                ConstValue::Concrete(ConcreteConst {
-                                    interned: ConstScalar::UInt(len),
-                                }) => len.checked_sub((prefix.len() + suffix.len()) as u128),
-                                _ => None,
-                            };
-                            TyKind::Array(
-                                elem_ty.clone(),
-                                intern_const_scalar(
-                                    len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
-                                    TyBuilder::usize(),
-                                ),
-                            )
-                        }
-                        _ => TyKind::Slice(elem_ty.clone()),
-                    }
-                    .intern(Interner);
-                    self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
-                }
-
-                match expected.kind(Interner) {
-                    TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
-                    _ => TyKind::Slice(elem_ty),
-                }
-                .intern(Interner)
+                self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
             }
             Pat::Wild => expected.clone(),
             Pat::Range { start, end } => {
@@ -296,27 +263,10 @@ impl<'a> InferenceContext<'a> {
                 self.infer_expr(*end, &Expectation::has_type(start_ty))
             }
             &Pat::Lit(expr) => {
-                // FIXME: using `Option` here is a workaround until we can use if-let chains in stable.
-                let mut pat_ty = None;
-
-                // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
-                if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
-                    if let Some((inner, ..)) = expected.as_reference() {
-                        let inner = self.resolve_ty_shallow(inner);
-                        if matches!(inner.kind(Interner), TyKind::Slice(_)) {
-                            let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
-                            let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
-                            let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty)
-                                .intern(Interner);
-                            self.write_expr_ty(expr, ty.clone());
-                            pat_ty = Some(ty);
-                        }
-                    }
-                }
-
-                pat_ty.unwrap_or_else(|| {
-                    self.infer_expr(expr, &Expectation::has_type(expected.clone()))
-                })
+                // Don't emit type mismatches again, the expression lowering already did that.
+                let ty = self.infer_lit_pat(expr, &expected);
+                self.write_pat_ty(pat, ty.clone());
+                return ty;
             }
             Pat::Box { inner } => match self.resolve_boxed_box() {
                 Some(box_adt) => {
@@ -345,7 +295,8 @@ impl<'a> InferenceContext<'a> {
         };
         // use a new type variable if we got error type here
         let ty = self.insert_type_vars_shallow(ty);
-        if !self.unify(&ty, &expected) {
+        // FIXME: This never check is odd, but required with out we do inference right now
+        if !expected.is_never() && !self.unify(&ty, &expected) {
             self.result
                 .type_mismatches
                 .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
@@ -353,6 +304,111 @@ impl<'a> InferenceContext<'a> {
         self.write_pat_ty(pat, ty.clone());
         ty
     }
+
+    fn infer_ref_pat(
+        &mut self,
+        pat: PatId,
+        mutability: Mutability,
+        expected: &Ty,
+        default_bm: BindingMode,
+    ) -> Ty {
+        let expectation = match expected.as_reference() {
+            Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
+            _ => self.result.standard_types.unknown.clone(),
+        };
+        let subty = self.infer_pat(pat, &expectation, default_bm);
+        TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
+    }
+
+    fn infer_bind_pat(
+        &mut self,
+        pat: PatId,
+        binding: BindingId,
+        default_bm: BindingMode,
+        subpat: Option<PatId>,
+        expected: &Ty,
+    ) -> Ty {
+        let Binding { mode, .. } = self.body.bindings[binding];
+        let mode = if mode == BindingAnnotation::Unannotated {
+            default_bm
+        } else {
+            BindingMode::convert(mode)
+        };
+        self.result.pat_binding_modes.insert(pat, mode);
+
+        let inner_ty = match subpat {
+            Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
+            None => expected.clone(),
+        };
+        let inner_ty = self.insert_type_vars_shallow(inner_ty);
+
+        let bound_ty = match mode {
+            BindingMode::Ref(mutability) => {
+                TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner)
+            }
+            BindingMode::Move => inner_ty.clone(),
+        };
+        self.write_pat_ty(pat, bound_ty.clone());
+        self.write_binding_ty(binding, bound_ty);
+        return inner_ty;
+    }
+
+    fn infer_slice_pat(
+        &mut self,
+        expected: &Ty,
+        prefix: &[PatId],
+        slice: &Option<PatId>,
+        suffix: &[PatId],
+        default_bm: BindingMode,
+    ) -> Ty {
+        let elem_ty = match expected.kind(Interner) {
+            TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
+            _ => self.err_ty(),
+        };
+
+        for &pat_id in prefix.iter().chain(suffix.iter()) {
+            self.infer_pat(pat_id, &elem_ty, default_bm);
+        }
+
+        if let &Some(slice_pat_id) = slice {
+            let rest_pat_ty = match expected.kind(Interner) {
+                TyKind::Array(_, length) => {
+                    let len = try_const_usize(length);
+                    let len =
+                        len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128));
+                    TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate()))
+                }
+                _ => TyKind::Slice(elem_ty.clone()),
+            }
+            .intern(Interner);
+            self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
+        }
+
+        match expected.kind(Interner) {
+            TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
+            _ => TyKind::Slice(elem_ty),
+        }
+        .intern(Interner)
+    }
+
+    fn infer_lit_pat(&mut self, expr: ExprId, expected: &Ty) -> Ty {
+        // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
+        if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] {
+            if let Some((inner, ..)) = expected.as_reference() {
+                let inner = self.resolve_ty_shallow(inner);
+                if matches!(inner.kind(Interner), TyKind::Slice(_)) {
+                    let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
+                    let slice_ty = TyKind::Slice(elem_ty).intern(Interner);
+                    let ty =
+                        TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty).intern(Interner);
+                    self.write_expr_ty(expr, ty.clone());
+                    return ty;
+                }
+            }
+        }
+
+        self.infer_expr(expr, &Expectation::has_type(expected.clone()))
+    }
 }
 
 fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
@@ -369,11 +425,52 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
         Pat::Lit(expr) => {
             !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
         }
-        Pat::Bind {
-            mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated,
-            subpat: Some(subpat),
-            ..
-        } => is_non_ref_pat(body, *subpat),
+        Pat::Bind { id, subpat: Some(subpat), .. }
+            if matches!(
+                body.bindings[*id].mode,
+                BindingAnnotation::Mutable | BindingAnnotation::Unannotated
+            ) =>
+        {
+            is_non_ref_pat(body, *subpat)
+        }
         Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
     }
 }
+
+pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
+    let mut res = false;
+    walk_pats(body, pat_id, &mut |pat| {
+        res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
+    });
+    res
+}
+
+fn walk_pats(body: &Body, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+    let pat = &body[pat_id];
+    f(pat);
+    match pat {
+        Pat::Range { .. }
+        | Pat::Lit(..)
+        | Pat::Path(..)
+        | Pat::ConstBlock(..)
+        | Pat::Wild
+        | Pat::Missing => {}
+        &Pat::Bind { subpat, .. } => {
+            if let Some(subpat) = subpat {
+                walk_pats(body, subpat, f);
+            }
+        }
+        Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
+            args.iter().copied().for_each(|p| walk_pats(body, p, f));
+        }
+        Pat::Ref { pat, .. } => walk_pats(body, *pat, f),
+        Pat::Slice { prefix, slice, suffix } => {
+            let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
+            total_iter.copied().for_each(|p| walk_pats(body, p, f));
+        }
+        Pat::Record { args, .. } => {
+            args.iter().for_each(|RecordFieldPat { pat, .. }| walk_pats(body, *pat, f));
+        }
+        Pat::Box { inner } => walk_pats(body, *inner, f),
+    }
+}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 0a8527afbd0..2267fedaa8e 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -3,7 +3,7 @@
 use chalk_ir::cast::Cast;
 use hir_def::{
     path::{Path, PathSegment},
-    resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
+    resolver::{ResolveValueResult, TypeNs, ValueNs},
     AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
 };
 use hir_expand::name::Name;
@@ -21,55 +21,42 @@ use crate::{
 use super::{ExprOrPatId, InferenceContext, TraitRef};
 
 impl<'a> InferenceContext<'a> {
-    pub(super) fn infer_path(
-        &mut self,
-        resolver: &Resolver,
-        path: &Path,
-        id: ExprOrPatId,
-    ) -> Option<Ty> {
-        let ty = self.resolve_value_path(resolver, path, id)?;
+    pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
+        let ty = self.resolve_value_path(path, id)?;
         let ty = self.insert_type_vars(ty);
         let ty = self.normalize_associated_types_in(ty);
         Some(ty)
     }
 
-    fn resolve_value_path(
-        &mut self,
-        resolver: &Resolver,
-        path: &Path,
-        id: ExprOrPatId,
-    ) -> Option<Ty> {
+    fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
         let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
-            if path.segments().is_empty() {
-                // This can't actually happen syntax-wise
-                return None;
-            }
+            let Some(last) = path.segments().last() else { return None };
             let ty = self.make_ty(type_ref);
             let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
-            let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
+            let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
             let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
-            self.resolve_ty_assoc_item(
-                ty,
-                path.segments().last().expect("path had at least one segment").name,
-                id,
-            )?
+            self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
         } else {
+            // FIXME: report error, unresolved first path segment
             let value_or_partial =
-                resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
+                self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
 
             match value_or_partial {
                 ResolveValueResult::ValueNs(it) => (it, None),
-                ResolveValueResult::Partial(def, remaining_index) => {
-                    self.resolve_assoc_item(def, path, remaining_index, id)?
-                }
+                ResolveValueResult::Partial(def, remaining_index) => self
+                    .resolve_assoc_item(def, path, remaining_index, id)
+                    .map(|(it, substs)| (it, Some(substs)))?,
             }
         };
 
         let typable: ValueTyDefId = match value {
-            ValueNs::LocalBinding(pat) => {
-                let ty = self.result.type_of_pat.get(pat)?.clone();
-                return Some(ty);
-            }
+            ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
+                Some(ty) => return Some(ty.clone()),
+                None => {
+                    never!("uninferred pattern?");
+                    return None;
+                }
+            },
             ValueNs::FunctionId(it) => it.into(),
             ValueNs::ConstId(it) => it.into(),
             ValueNs::StaticId(it) => it.into(),
@@ -91,7 +78,7 @@ impl<'a> InferenceContext<'a> {
                     let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
                     return Some(ty);
                 } else {
-                    // FIXME: diagnostic, invalid Self reference
+                    // FIXME: report error, invalid Self reference
                     return None;
                 }
             }
@@ -126,7 +113,7 @@ impl<'a> InferenceContext<'a> {
         path: &Path,
         remaining_index: usize,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         assert!(remaining_index < path.segments().len());
         // there may be more intermediate segments between the resolved one and
         // the end. Only the last segment needs to be resolved to a value; from
@@ -179,7 +166,7 @@ impl<'a> InferenceContext<'a> {
         trait_ref: TraitRef,
         segment: PathSegment<'_>,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         let trait_ = trait_ref.hir_trait_id();
         let item =
             self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| {
@@ -215,7 +202,7 @@ impl<'a> InferenceContext<'a> {
         };
 
         self.write_assoc_resolution(id, item, trait_ref.substitution.clone());
-        Some((def, Some(trait_ref.substitution)))
+        Some((def, trait_ref.substitution))
     }
 
     fn resolve_ty_assoc_item(
@@ -223,7 +210,7 @@ impl<'a> InferenceContext<'a> {
         ty: Ty,
         name: &Name,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         if let TyKind::Error = ty.kind(Interner) {
             return None;
         }
@@ -233,70 +220,66 @@ impl<'a> InferenceContext<'a> {
         }
 
         let canonical_ty = self.canonicalize(ty.clone());
-        let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
 
         let mut not_visible = None;
         let res = method_resolution::iterate_method_candidates(
             &canonical_ty.value,
             self.db,
             self.table.trait_env.clone(),
-            &traits_in_scope,
+            self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
             VisibleFromModule::Filter(self.resolver.module()),
             Some(name),
             method_resolution::LookupMode::Path,
             |_ty, item, visible| {
-                let (def, container) = match item {
-                    AssocItemId::FunctionId(f) => {
-                        (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
-                    }
-                    AssocItemId::ConstId(c) => {
-                        (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container)
-                    }
-                    AssocItemId::TypeAliasId(_) => unreachable!(),
-                };
-                let substs = match container {
-                    ItemContainerId::ImplId(impl_id) => {
-                        let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
-                            .fill_with_inference_vars(&mut self.table)
-                            .build();
-                        let impl_self_ty =
-                            self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
-                        self.unify(&impl_self_ty, &ty);
-                        impl_substs
-                    }
-                    ItemContainerId::TraitId(trait_) => {
-                        // we're picking this method
-                        let trait_ref = TyBuilder::trait_ref(self.db, trait_)
-                            .push(ty.clone())
-                            .fill_with_inference_vars(&mut self.table)
-                            .build();
-                        self.push_obligation(trait_ref.clone().cast(Interner));
-                        trait_ref.substitution
-                    }
-                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
-                        never!("assoc item contained in module/extern block");
-                        return None;
-                    }
-                };
-
                 if visible {
-                    Some((def, item, Some(substs), true))
+                    Some((item, true))
                 } else {
                     if not_visible.is_none() {
-                        not_visible = Some((def, item, Some(substs), false));
+                        not_visible = Some((item, false));
                     }
                     None
                 }
             },
         );
         let res = res.or(not_visible);
-        if let Some((_, item, Some(ref substs), visible)) = res {
-            self.write_assoc_resolution(id, item, substs.clone());
-            if !visible {
-                self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item })
+        let (item, visible) = res?;
+
+        let (def, container) = match item {
+            AssocItemId::FunctionId(f) => {
+                (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
             }
+            AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container),
+            AssocItemId::TypeAliasId(_) => unreachable!(),
+        };
+        let substs = match container {
+            ItemContainerId::ImplId(impl_id) => {
+                let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
+                    .fill_with_inference_vars(&mut self.table)
+                    .build();
+                let impl_self_ty = self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
+                self.unify(&impl_self_ty, &ty);
+                impl_substs
+            }
+            ItemContainerId::TraitId(trait_) => {
+                // we're picking this method
+                let trait_ref = TyBuilder::trait_ref(self.db, trait_)
+                    .push(ty.clone())
+                    .fill_with_inference_vars(&mut self.table)
+                    .build();
+                self.push_obligation(trait_ref.clone().cast(Interner));
+                trait_ref.substitution
+            }
+            ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
+                never!("assoc item contained in module/extern block");
+                return None;
+            }
+        };
+
+        self.write_assoc_resolution(id, item, substs.clone());
+        if !visible {
+            self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item });
         }
-        res.map(|(def, _, substs, _)| (def, substs))
+        Some((def, substs))
     }
 
     fn resolve_enum_variant_on_ty(
@@ -304,7 +287,7 @@ impl<'a> InferenceContext<'a> {
         ty: &Ty,
         name: &Name,
         id: ExprOrPatId,
-    ) -> Option<(ValueNs, Option<Substitution>)> {
+    ) -> Option<(ValueNs, Substitution)> {
         let ty = self.resolve_ty_shallow(ty);
         let (enum_id, subst) = match ty.as_adt() {
             Some((AdtId::EnumId(e), subst)) => (e, subst),
@@ -314,6 +297,6 @@ impl<'a> InferenceContext<'a> {
         let local_id = enum_data.variant(name)?;
         let variant = EnumVariantId { parent: enum_id, local_id };
         self.write_variant_resolution(id, variant.into());
-        Some((ValueNs::EnumVariantId(variant), Some(subst.clone())))
+        Some((ValueNs::EnumVariantId(variant), subst.clone()))
     }
 }
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 46ed3533c8c..504f0743aa9 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -704,14 +704,13 @@ impl<'a> fmt::Debug for InferenceTable<'a> {
 mod resolve {
     use super::InferenceTable;
     use crate::{
-        ConcreteConst, Const, ConstData, ConstValue, DebruijnIndex, GenericArg, InferenceVar,
-        Interner, Lifetime, Ty, TyVariableKind, VariableKind,
+        ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg,
+        InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind,
     };
     use chalk_ir::{
         cast::Cast,
         fold::{TypeFoldable, TypeFolder},
     };
-    use hir_def::type_ref::ConstScalar;
 
     #[derive(chalk_derive::FallibleTypeFolder)]
     #[has_interner(Interner)]
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index 0c547192ac0..36af78153d4 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -6,12 +6,12 @@ use chalk_ir::{
     DebruijnIndex,
 };
 use hir_def::{
-    adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
-    EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
+    adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
+    ModuleId, VariantId,
 };
 
 use crate::{
-    db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
+    consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
 };
 
 /// Checks whether a type is visibly uninhabited from a particular module.
@@ -69,7 +69,7 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
             TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
             TyKind::Never => BREAK_VISIBLY_UNINHABITED,
             TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
-            TyKind::Array(item_ty, len) => match try_usize_const(len) {
+            TyKind::Array(item_ty, len) => match try_const_usize(len) {
                 Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
                 Some(1..) => item_ty.super_visit_with(self, outer_binder),
             },
@@ -160,14 +160,3 @@ impl UninhabitedFrom<'_> {
         }
     }
 }
-
-fn try_usize_const(c: &Const) -> Option<u128> {
-    let data = &c.data(Interner);
-    if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
-        return None;
-    }
-    match data.value {
-        ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
-        _ => None,
-    }
-}
diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs
index 7bf73560cbe..aea7e9762fd 100644
--- a/crates/hir-ty/src/interner.rs
+++ b/crates/hir-ty/src/interner.rs
@@ -1,10 +1,10 @@
 //! Implementation of the Chalk `Interner` trait, which allows customizing the
 //! representation of the various objects Chalk deals with (types, goals etc.).
 
-use crate::{chalk_db, tls, GenericArg};
+use crate::{chalk_db, tls, ConstScalar, GenericArg};
 use base_db::salsa::InternId;
 use chalk_ir::{Goal, GoalData};
-use hir_def::{type_ref::ConstScalar, TypeAliasId};
+use hir_def::TypeAliasId;
 use intern::{impl_internable, Interned};
 use smallvec::SmallVec;
 use std::{fmt, sync::Arc};
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index f21b4f84c4c..b95bb01fcef 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -11,7 +11,7 @@ use hir_def::{
 };
 use stdx::never;
 
-use crate::{db::HirDatabase, Interner, Substitution, Ty};
+use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty};
 
 use self::adt::struct_variant_idx;
 pub use self::{
@@ -122,17 +122,9 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
         }
         TyKind::Array(element, count) => {
-            let count = match count.data(Interner).value {
-                chalk_ir::ConstValue::Concrete(c) => match c.interned {
-                    hir_def::type_ref::ConstScalar::Int(x) => x as u64,
-                    hir_def::type_ref::ConstScalar::UInt(x) => x as u64,
-                    hir_def::type_ref::ConstScalar::Unknown => {
-                        user_error!("unknown const generic parameter")
-                    }
-                    _ => user_error!("mismatched type of const generic parameter"),
-                },
-                _ => return Err(LayoutError::HasPlaceholder),
-            };
+            let count = try_const_usize(&count).ok_or(LayoutError::UserError(
+                "mismatched type of const generic parameter".to_string(),
+            ))? as u64;
             let element = layout_of_ty(db, element, krate)?;
             let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
 
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index cb7968c1446..b22d0fe8ded 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -76,17 +76,8 @@ pub fn layout_of_adt_query(
             |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
             variants.iter_enumerated().filter_map(|(id, _)| {
                 let AdtId::EnumId(e) = def else { return None };
-                let d = match db
-                    .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 })
-                    .ok()?
-                {
-                    crate::consteval::ComputedExpr::Literal(l) => match l {
-                        hir_def::expr::Literal::Int(i, _) => i,
-                        hir_def::expr::Literal::Uint(i, _) => i as i128,
-                        _ => return None,
-                    },
-                    _ => return None,
-                };
+                let d =
+                    db.const_eval_discriminant(EnumVariantId { parent: e, local_id: id.0 }).ok()?;
                 Some((id, d))
             }),
             // FIXME: The current code for niche-filling relies on variant indices
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 067bdc960da..a8971fde3c2 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -65,25 +65,17 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
         })
         .unwrap();
     let hir_body = db.body(adt_id.into());
-    let pat = hir_body
-        .pats
-        .iter()
-        .find(|x| match x.1 {
-            hir_def::expr::Pat::Bind { name, .. } => name.to_smol_str() == "goal",
-            _ => false,
-        })
-        .unwrap()
-        .0;
+    let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
     let infer = db.infer(adt_id.into());
-    let goal_ty = infer.type_of_pat[pat].clone();
+    let goal_ty = infer.type_of_binding[b].clone();
     layout_of_ty(&db, &goal_ty, module_id.krate())
 }
 
 #[track_caller]
 fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
     let l = eval_goal(ra_fixture, minicore).unwrap();
-    assert_eq!(l.size.bytes(), size);
-    assert_eq!(l.align.abi.bytes(), align);
+    assert_eq!(l.size.bytes(), size, "size mismatch");
+    assert_eq!(l.align.abi.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
@@ -300,4 +292,9 @@ fn enums_with_discriminants() {
             C, // implicitly becomes 256, so we need two bytes
         }
     }
+    size_and_align! {
+        enum Goal {
+            A = 1, // This one is (perhaps surprisingly) zero sized.
+        }
+    }
 }
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 59a5ef8c14d..9c63d67ab19 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -13,6 +13,7 @@ mod builder;
 mod chalk_db;
 mod chalk_ext;
 pub mod consteval;
+pub mod mir;
 mod infer;
 mod inhabitedness;
 mod interner;
@@ -34,7 +35,7 @@ mod tests;
 #[cfg(test)]
 mod test_db;
 
-use std::sync::Arc;
+use std::{collections::HashMap, hash::Hash, sync::Arc};
 
 use chalk_ir::{
     fold::{Shift, TypeFoldable},
@@ -42,10 +43,11 @@ use chalk_ir::{
     visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
     NoSolution, TyData,
 };
+use either::Either;
 use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
 use hir_expand::name;
-use itertools::Either;
 use la_arena::{Arena, Idx};
+use mir::MirEvalError;
 use rustc_hash::FxHashSet;
 use traits::FnTrait;
 use utils::Generics;
@@ -145,6 +147,49 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
 pub type Guidance = chalk_solve::Guidance<Interner>;
 pub type WhereClause = chalk_ir::WhereClause<Interner>;
 
+/// A constant can have reference to other things. Memory map job is holding
+/// the neccessary bits of memory of the const eval session to keep the constant
+/// meaningful.
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct MemoryMap(pub HashMap<usize, Vec<u8>>);
+
+impl MemoryMap {
+    fn insert(&mut self, addr: usize, x: Vec<u8>) {
+        self.0.insert(addr, x);
+    }
+
+    /// This functions convert each address by a function `f` which gets the byte intervals and assign an address
+    /// to them. It is useful when you want to load a constant with a memory map in a new memory. You can pass an
+    /// allocator function as `f` and it will return a mapping of old addresses to new addresses.
+    fn transform_addresses(
+        &self,
+        mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
+    ) -> Result<HashMap<usize, usize>, MirEvalError> {
+        self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+    }
+}
+
+/// A concrete constant value
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ConstScalar {
+    Bytes(Vec<u8>, MemoryMap),
+    /// Case of an unknown value that rustc might know but we don't
+    // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
+    // constants
+    // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177
+    // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
+    Unknown,
+}
+
+impl Hash for ConstScalar {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        core::mem::discriminant(self).hash(state);
+        if let ConstScalar::Bytes(b, _) = self {
+            b.hash(state)
+        }
+    }
+}
+
 /// Return an index of a parameter in the generic type parameter list by it's id.
 pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
     generics(db.upcast(), id.parent).param_idx(id)
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 29964673722..23b15087e31 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -16,6 +16,7 @@ use chalk_ir::{
     cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety,
 };
 
+use either::Either;
 use hir_def::{
     adt::StructKind,
     body::{Expander, LowerCtx},
@@ -26,16 +27,13 @@ use hir_def::{
     lang_item::{lang_attr, LangItem},
     path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
     resolver::{HasResolver, Resolver, TypeNs},
-    type_ref::{
-        ConstScalarOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
-    },
+    type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
     AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
     HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
     TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{name::Name, ExpandResult};
 use intern::Interned;
-use itertools::Either;
 use la_arena::{Arena, ArenaMap};
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
@@ -44,7 +42,7 @@ use syntax::ast;
 
 use crate::{
     all_super_traits,
-    consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
+    consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
     db::HirDatabase,
     make_binders,
     mapping::{from_chalk_trait_id, ToChalk},
@@ -524,6 +522,10 @@ impl<'a> TyLoweringContext<'a> {
                 };
                 return (ty, None);
             }
+            TypeNs::TraitAliasId(_) => {
+                // FIXME(trait_alias): Implement trait alias.
+                return (TyKind::Error.intern(Interner), None);
+            }
             TypeNs::GenericParam(param_id) => {
                 let generics = generics(
                     self.db.upcast(),
@@ -879,6 +881,7 @@ impl<'a> TyLoweringContext<'a> {
     ) -> Option<TraitRef> {
         let resolved =
             match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
+                // FIXME(trait_alias): We need to handle trait alias here.
                 TypeNs::TraitId(tr) => tr,
                 _ => return None,
             };
@@ -968,7 +971,7 @@ impl<'a> TyLoweringContext<'a> {
                         // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
                         //   the builtin impls are supported by Chalk, we ignore them here.
                         if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
-                            if lang == "drop" || lang == "destruct" {
+                            if matches!(lang, LangItem::Drop | LangItem::Destruct) {
                                 return false;
                             }
                         }
@@ -1444,6 +1447,7 @@ pub(crate) fn trait_environment_query(
         GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container),
         GenericDefId::AdtId(_) => None,
         GenericDefId::TraitId(_) => None,
+        GenericDefId::TraitAliasId(_) => None,
         GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container),
         GenericDefId::ImplId(_) => None,
         GenericDefId::EnumVariantId(_) => None,
@@ -1583,10 +1587,10 @@ pub(crate) fn generic_defaults_recover(
         .iter_id()
         .map(|id| {
             let val = match id {
-                itertools::Either::Left(_) => {
+                Either::Left(_) => {
                     GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
                 }
-                itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+                Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
             };
             crate::make_binders(db, &generic_params, val)
         })
@@ -1919,7 +1923,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
     arg: &'a GenericArg,
     this: &mut T,
     for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
-    for_const: impl FnOnce(&mut T, &ConstScalarOrPath, Ty) -> Const + 'a,
+    for_const: impl FnOnce(&mut T, &ConstRefOrPath, Ty) -> Const + 'a,
 ) -> Option<crate::GenericArg> {
     let kind = match kind_id {
         Either::Left(_) => ParamKind::Type,
@@ -1947,7 +1951,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
                 let p = p.mod_path();
                 if p.kind == PathKind::Plain {
                     if let [n] = p.segments() {
-                        let c = ConstScalarOrPath::Path(n.clone());
+                        let c = ConstRefOrPath::Path(n.clone());
                         return Some(
                             GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
                         );
@@ -1964,14 +1968,14 @@ pub(crate) fn const_or_path_to_chalk(
     db: &dyn HirDatabase,
     resolver: &Resolver,
     expected_ty: Ty,
-    value: &ConstScalarOrPath,
+    value: &ConstRefOrPath,
     mode: ParamLoweringMode,
     args: impl FnOnce() -> Generics,
     debruijn: DebruijnIndex,
 ) -> Const {
     match value {
-        ConstScalarOrPath::Scalar(s) => intern_const_scalar(*s, expected_ty),
-        ConstScalarOrPath::Path(n) => {
+        ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
+        ConstRefOrPath::Path(n) => {
             let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
             path_to_const(db, resolver, &path, mode, args, debruijn)
                 .unwrap_or_else(|| unknown_const(expected_ty))
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 8c7714b9a69..92a17fc3a99 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -579,8 +579,8 @@ impl ReceiverAdjustments {
                     ty = new_ty.clone();
                     adjust.push(Adjustment {
                         kind: Adjust::Deref(match kind {
-                            // FIXME should we know the mutability here?
-                            AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
+                            // FIXME should we know the mutability here, when autoref is `None`?
+                            AutoderefKind::Overloaded => Some(OverloadedDeref(self.autoref)),
                             AutoderefKind::Builtin => None,
                         }),
                         target: new_ty,
@@ -660,10 +660,10 @@ pub fn lookup_impl_const(
     env: Arc<TraitEnvironment>,
     const_id: ConstId,
     subs: Substitution,
-) -> ConstId {
+) -> (ConstId, Substitution) {
     let trait_id = match const_id.lookup(db.upcast()).container {
         ItemContainerId::TraitId(id) => id,
-        _ => return const_id,
+        _ => return (const_id, subs),
     };
     let substitution = Substitution::from_iter(Interner, subs.iter(Interner));
     let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution };
@@ -671,12 +671,14 @@ pub fn lookup_impl_const(
     let const_data = db.const_data(const_id);
     let name = match const_data.name.as_ref() {
         Some(name) => name,
-        None => return const_id,
+        None => return (const_id, subs),
     };
 
     lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
-        .and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None })
-        .unwrap_or(const_id)
+        .and_then(
+            |assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
+        )
+        .unwrap_or((const_id, subs))
 }
 
 /// Looks up the impl method that actually runs for the trait method `func`.
@@ -687,10 +689,10 @@ pub fn lookup_impl_method(
     env: Arc<TraitEnvironment>,
     func: FunctionId,
     fn_subst: Substitution,
-) -> FunctionId {
+) -> (FunctionId, Substitution) {
     let trait_id = match func.lookup(db.upcast()).container {
         ItemContainerId::TraitId(id) => id,
-        _ => return func,
+        _ => return (func, fn_subst),
     };
     let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
     let fn_params = fn_subst.len(Interner) - trait_params;
@@ -701,8 +703,14 @@ pub fn lookup_impl_method(
 
     let name = &db.function_data(func).name;
     lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
-        .and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None })
-        .unwrap_or(func)
+        .and_then(|assoc| {
+            if let (AssocItemId::FunctionId(id), subst) = assoc {
+                Some((id, subst))
+            } else {
+                None
+            }
+        })
+        .unwrap_or((func, fn_subst))
 }
 
 fn lookup_impl_assoc_item_for_trait_ref(
@@ -710,7 +718,7 @@ fn lookup_impl_assoc_item_for_trait_ref(
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     name: &Name,
-) -> Option<AssocItemId> {
+) -> Option<(AssocItemId, Substitution)> {
     let self_ty = trait_ref.self_type_parameter(Interner);
     let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
     let impls = db.trait_impls_in_deps(env.krate);
@@ -718,8 +726,8 @@ fn lookup_impl_assoc_item_for_trait_ref(
 
     let table = InferenceTable::new(db, env);
 
-    let impl_data = find_matching_impl(impls, table, trait_ref)?;
-    impl_data.items.iter().find_map(|&it| match it {
+    let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?;
+    let item = impl_data.items.iter().find_map(|&it| match it {
         AssocItemId::FunctionId(f) => {
             (db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
         }
@@ -730,14 +738,15 @@ fn lookup_impl_assoc_item_for_trait_ref(
             .map(|n| n == name)
             .and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
         AssocItemId::TypeAliasId(_) => None,
-    })
+    })?;
+    Some((item, impl_subst))
 }
 
 fn find_matching_impl(
     mut impls: impl Iterator<Item = ImplId>,
     mut table: InferenceTable<'_>,
     actual_trait_ref: TraitRef,
-) -> Option<Arc<ImplData>> {
+) -> Option<(Arc<ImplData>, Substitution)> {
     let db = table.db;
     loop {
         let impl_ = impls.next()?;
@@ -758,7 +767,7 @@ fn find_matching_impl(
                 .into_iter()
                 .map(|b| b.cast(Interner));
             let goal = crate::Goal::all(Interner, wcs);
-            table.try_obligation(goal).map(|_| impl_data)
+            table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
         });
         if r.is_some() {
             break r;
@@ -821,9 +830,9 @@ pub fn iterate_method_candidates_dyn(
 
             let mut table = InferenceTable::new(db, env.clone());
             let ty = table.instantiate_canonical(ty.clone());
-            let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
+            let deref_chain = autoderef_method_receiver(&mut table, ty);
 
-            let result = deref_chain.into_iter().zip(adj).try_for_each(|(receiver_ty, adj)| {
+            let result = deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
                 iterate_method_candidates_with_autoref(
                     &receiver_ty,
                     adj,
@@ -867,16 +876,20 @@ fn iterate_method_candidates_with_autoref(
         return ControlFlow::Continue(());
     }
 
-    iterate_method_candidates_by_receiver(
-        receiver_ty,
-        first_adjustment.clone(),
-        db,
-        env.clone(),
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
-    )?;
+    let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| {
+        iterate_method_candidates_by_receiver(
+            receiver_ty,
+            first_adjustment,
+            db,
+            env.clone(),
+            traits_in_scope,
+            visible_from_module,
+            name,
+            &mut callback,
+        )
+    };
+
+    iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
 
     let refed = Canonical {
         value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
@@ -884,16 +897,7 @@ fn iterate_method_candidates_with_autoref(
         binders: receiver_ty.binders.clone(),
     };
 
-    iterate_method_candidates_by_receiver(
-        &refed,
-        first_adjustment.with_autoref(Mutability::Not),
-        db,
-        env.clone(),
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
-    )?;
+    iterate_method_candidates_by_receiver(&refed, first_adjustment.with_autoref(Mutability::Not))?;
 
     let ref_muted = Canonical {
         value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
@@ -904,12 +908,6 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &ref_muted,
         first_adjustment.with_autoref(Mutability::Mut),
-        db,
-        env,
-        traits_in_scope,
-        visible_from_module,
-        name,
-        &mut callback,
     )
 }
 
@@ -1210,8 +1208,8 @@ pub fn resolve_indexing_op(
 ) -> Option<ReceiverAdjustments> {
     let mut table = InferenceTable::new(db, env.clone());
     let ty = table.instantiate_canonical(ty);
-    let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
-    for (ty, adj) in deref_chain.into_iter().zip(adj) {
+    let deref_chain = autoderef_method_receiver(&mut table, ty);
+    for (ty, adj) in deref_chain {
         let goal = generic_implements_goal(db, env.clone(), index_trait, &ty);
         if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
             return Some(adj);
@@ -1421,25 +1419,24 @@ fn generic_implements_goal(
 fn autoderef_method_receiver(
     table: &mut InferenceTable<'_>,
     ty: Ty,
-) -> (Vec<Canonical<Ty>>, Vec<ReceiverAdjustments>) {
-    let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new());
+) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
+    let mut deref_chain: Vec<_> = Vec::new();
     let mut autoderef = autoderef::Autoderef::new(table, ty);
     while let Some((ty, derefs)) = autoderef.next() {
-        deref_chain.push(autoderef.table.canonicalize(ty).value);
-        adjustments.push(ReceiverAdjustments {
-            autoref: None,
-            autoderefs: derefs,
-            unsize_array: false,
-        });
+        deref_chain.push((
+            autoderef.table.canonicalize(ty).value,
+            ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false },
+        ));
     }
     // 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, _), binders)), Some(adj)) = (
-        deref_chain.last().map(|ty| (ty.value.kind(Interner), ty.binders.clone())),
-        adjustments.last().cloned(),
-    ) {
+    if let Some((TyKind::Array(parameters, _), binders, adj)) =
+        deref_chain.last().map(|(ty, adj)| (ty.value.kind(Interner), ty.binders.clone(), adj))
+    {
         let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner);
-        deref_chain.push(Canonical { value: unsized_ty, binders });
-        adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj });
+        deref_chain.push((
+            Canonical { value: unsized_ty, binders },
+            ReceiverAdjustments { unsize_array: true, ..adj.clone() },
+        ));
     }
-    (deref_chain, adjustments)
+    deref_chain
 }
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
new file mode 100644
index 00000000000..7c1cbbdf53d
--- /dev/null
+++ b/crates/hir-ty/src/mir.rs
@@ -0,0 +1,863 @@
+//! MIR definitions and implementation
+
+use std::{fmt::Display, iter};
+
+use crate::{
+    infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
+};
+use chalk_ir::Mutability;
+use hir_def::{
+    expr::{BindingId, Expr, ExprId, Ordering, PatId},
+    DefWithBodyId, FieldId, UnionId, VariantId,
+};
+use la_arena::{Arena, ArenaMap, Idx, RawIdx};
+
+mod eval;
+mod lower;
+mod borrowck;
+mod pretty;
+
+pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
+pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
+pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
+use smallvec::{smallvec, SmallVec};
+use stdx::impl_from;
+
+use super::consteval::{intern_const_scalar, try_const_usize};
+
+pub type BasicBlockId = Idx<BasicBlock>;
+pub type LocalId = Idx<Local>;
+
+fn return_slot() -> LocalId {
+    LocalId::from_raw(RawIdx::from(0))
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Local {
+    pub ty: Ty,
+}
+
+/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
+/// the memory model. One proposal for a definition of values can be found [on UCG][value-def].
+///
+/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md
+///
+/// The most common way to create values is via loading a place. Loading a place is an operation
+/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed*
+/// operation. The nature of the value produced depends on the type of the conversion. Furthermore,
+/// there may be other effects: if the type has a validity constraint loading the place might be UB
+/// if the validity constraint is not met.
+///
+/// **Needs clarification:** Ralf proposes that loading a place not have side-effects.
+/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
+/// something we can even decide without knowing more about Rust's memory model?
+///
+/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri
+/// currently implements it, but it seems like this may be something to check against in the
+/// validator.
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Operand {
+    /// Creates a value by loading the given place.
+    ///
+    /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
+    /// is no such requirement.
+    Copy(Place),
+
+    /// Creates a value by performing loading the place, just like the `Copy` operand.
+    ///
+    /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide
+    /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this
+    /// place without first re-initializing it.
+    ///
+    /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
+    Move(Place),
+    /// Constants are already semantically values, and remain unchanged.
+    Constant(Const),
+}
+
+impl Operand {
+    fn from_concrete_const(data: Vec<u8>, memory_map: MemoryMap, ty: Ty) -> Self {
+        Operand::Constant(intern_const_scalar(ConstScalar::Bytes(data, memory_map), ty))
+    }
+
+    fn from_bytes(data: Vec<u8>, ty: Ty) -> Self {
+        Operand::from_concrete_const(data, MemoryMap::default(), ty)
+    }
+
+    fn const_zst(ty: Ty) -> Operand {
+        Self::from_bytes(vec![], ty)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum ProjectionElem<V, T> {
+    Deref,
+    Field(FieldId),
+    TupleField(usize),
+    Index(V),
+    ConstantIndex { offset: u64, min_length: u64, from_end: bool },
+    Subslice { from: u64, to: u64, from_end: bool },
+    //Downcast(Option<Symbol>, VariantIdx),
+    OpaqueCast(T),
+}
+
+type PlaceElem = ProjectionElem<LocalId, Ty>;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Place {
+    pub local: LocalId,
+    pub projection: Vec<PlaceElem>,
+}
+
+impl From<LocalId> for Place {
+    fn from(local: LocalId) -> Self {
+        Self { local, projection: vec![] }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum AggregateKind {
+    /// The type is of the element
+    Array(Ty),
+    /// The type is of the tuple
+    Tuple(Ty),
+    Adt(VariantId, Substitution),
+    Union(UnionId, FieldId),
+    //Closure(LocalDefId, SubstsRef),
+    //Generator(LocalDefId, SubstsRef, Movability),
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct SwitchTargets {
+    /// Possible values. The locations to branch to in each case
+    /// are found in the corresponding indices from the `targets` vector.
+    values: SmallVec<[u128; 1]>,
+
+    /// Possible branch sites. The last element of this vector is used
+    /// for the otherwise branch, so targets.len() == values.len() + 1
+    /// should hold.
+    //
+    // This invariant is quite non-obvious and also could be improved.
+    // One way to make this invariant is to have something like this instead:
+    //
+    // branches: Vec<(ConstInt, BasicBlock)>,
+    // otherwise: Option<BasicBlock> // exhaustive if None
+    //
+    // However we’ve decided to keep this as-is until we figure a case
+    // where some other approach seems to be strictly better than other.
+    targets: SmallVec<[BasicBlockId; 2]>,
+}
+
+impl SwitchTargets {
+    /// Creates switch targets from an iterator of values and target blocks.
+    ///
+    /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
+    /// `goto otherwise;`.
+    pub fn new(
+        targets: impl Iterator<Item = (u128, BasicBlockId)>,
+        otherwise: BasicBlockId,
+    ) -> Self {
+        let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
+        targets.push(otherwise);
+        Self { values, targets }
+    }
+
+    /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
+    /// and to `else_` if not.
+    pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
+        Self { values: smallvec![value], targets: smallvec![then, else_] }
+    }
+
+    /// Returns the fallback target that is jumped to when none of the values match the operand.
+    pub fn otherwise(&self) -> BasicBlockId {
+        *self.targets.last().unwrap()
+    }
+
+    /// Returns an iterator over the switch targets.
+    ///
+    /// The iterator will yield tuples containing the value and corresponding target to jump to, not
+    /// including the `otherwise` fallback target.
+    ///
+    /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
+    pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
+        iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
+    }
+
+    /// Returns a slice with all possible jump targets (including the fallback target).
+    pub fn all_targets(&self) -> &[BasicBlockId] {
+        &self.targets
+    }
+
+    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
+    /// specific value. This cannot fail, as it'll return the `otherwise`
+    /// branch if there's not a specific match for the value.
+    pub fn target_for_value(&self, value: u128) -> BasicBlockId {
+        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Terminator {
+    /// Block has one successor; we continue execution there.
+    Goto { target: BasicBlockId },
+
+    /// Switches based on the computed value.
+    ///
+    /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
+    /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
+    /// contains the computed value, continues execution at the associated basic block. Otherwise,
+    /// continues execution at the "otherwise" basic block.
+    ///
+    /// Target values may not appear more than once.
+    SwitchInt {
+        /// The discriminant value being tested.
+        discr: Operand,
+
+        targets: SwitchTargets,
+    },
+
+    /// Indicates that the landing pad is finished and that the process should continue unwinding.
+    ///
+    /// Like a return, this marks the end of this invocation of the function.
+    ///
+    /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
+    /// deaggregation runs.
+    Resume,
+
+    /// Indicates that the landing pad is finished and that the process should abort.
+    ///
+    /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
+    /// cleanup blocks.
+    Abort,
+
+    /// Returns from the function.
+    ///
+    /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
+    /// likely at least assigns the value currently in the return place (`_0`) to the place
+    /// specified in the associated `Call` terminator in the calling function, as if assigned via
+    /// `dest = move _0`. It might additionally do other things, like have side-effects in the
+    /// aliasing model.
+    ///
+    /// If the body is a generator body, this has slightly different semantics; it instead causes a
+    /// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
+    /// to the return place.
+    Return,
+
+    /// Indicates a terminator that can never be reached.
+    ///
+    /// Executing this terminator is UB.
+    Unreachable,
+
+    /// The behavior of this statement differs significantly before and after drop elaboration.
+    /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
+    /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
+    /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
+    /// issue tracking if drop glue has any interesting semantics in addition to those of a function
+    /// call?)
+    ///
+    /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
+    /// `Drop` will be executed if...
+    ///
+    /// **Needs clarification**: End of that sentence. This in effect should document the exact
+    /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
+    ///
+    /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
+    /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
+    /// > consider indirect assignments.
+    Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
+
+    /// Drops the place and assigns a new value to it.
+    ///
+    /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
+    /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
+    /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
+    /// explained by the elaboration:
+    ///
+    /// ```ignore (MIR)
+    /// BB0 {
+    ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
+    /// }
+    /// ```
+    ///
+    /// becomes
+    ///
+    /// ```ignore (MIR)
+    /// BB0 {
+    ///   Drop(P, goto BB1, unwind BB2)
+    /// }
+    /// BB1 {
+    ///   // P is now uninitialized
+    ///   P <- V
+    /// }
+    /// BB2 {
+    ///   // P is now uninitialized -- its dtor panicked
+    ///   P <- V
+    /// }
+    /// ```
+    ///
+    /// Disallowed after drop elaboration.
+    DropAndReplace {
+        place: Place,
+        value: Operand,
+        target: BasicBlockId,
+        unwind: Option<BasicBlockId>,
+    },
+
+    /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
+    /// the referred to function. The operand types must match the argument types of the function.
+    /// The return place type must match the return type. The type of the `func` operand must be
+    /// callable, meaning either a function pointer, a function type, or a closure type.
+    ///
+    /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
+    /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
+    /// [#71117].
+    ///
+    /// [#71117]: https://github.com/rust-lang/rust/issues/71117
+    Call {
+        /// The function that’s being called.
+        func: Operand,
+        /// Arguments the function is called with.
+        /// These are owned by the callee, which is free to modify them.
+        /// This allows the memory occupied by "by-value" arguments to be
+        /// reused across function calls without duplicating the contents.
+        args: Vec<Operand>,
+        /// Where the returned value will be written
+        destination: Place,
+        /// Where to go after this call returns. If none, the call necessarily diverges.
+        target: Option<BasicBlockId>,
+        /// Cleanups to be done if the call unwinds.
+        cleanup: Option<BasicBlockId>,
+        /// `true` if this is from a call in HIR rather than from an overloaded
+        /// operator. True for overloaded function call.
+        from_hir_call: bool,
+        // This `Span` is the span of the function, without the dot and receiver
+        // (e.g. `foo(a, b)` in `x.foo(a, b)`
+        //fn_span: Span,
+    },
+
+    /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
+    /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
+    /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
+    /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
+    /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
+    /// assertion does not fail, execution continues at the specified basic block.
+    Assert {
+        cond: Operand,
+        expected: bool,
+        //msg: AssertMessage,
+        target: BasicBlockId,
+        cleanup: Option<BasicBlockId>,
+    },
+
+    /// Marks a suspend point.
+    ///
+    /// Like `Return` terminators in generator bodies, this computes `value` and then a
+    /// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
+    /// the return place of the function calling this one, and execution continues in the calling
+    /// function. When next invoked with the same first argument, execution of this function
+    /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
+    /// place. If the generator is dropped before then, the `drop` basic block is invoked.
+    ///
+    /// Not permitted in bodies that are not generator bodies, or after generator lowering.
+    ///
+    /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
+    Yield {
+        /// The value to return.
+        value: Operand,
+        /// Where to resume to.
+        resume: BasicBlockId,
+        /// The place to store the resume argument in.
+        resume_arg: Place,
+        /// Cleanup to be done if the generator is dropped at this suspend point.
+        drop: Option<BasicBlockId>,
+    },
+
+    /// Indicates the end of dropping a generator.
+    ///
+    /// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations
+    /// as `yield`.
+    ///
+    /// **Needs clarification**: Is that even correct? The generator drop code is always confusing
+    /// to me, because it's not even really in the current body.
+    ///
+    /// **Needs clarification**: Are there type system constraints on these terminators? Should
+    /// there be a "block type" like `cleanup` blocks for them?
+    GeneratorDrop,
+
+    /// A block where control flow only ever takes one real path, but borrowck needs to be more
+    /// conservative.
+    ///
+    /// At runtime this is semantically just a goto.
+    ///
+    /// Disallowed after drop elaboration.
+    FalseEdge {
+        /// The target normal control flow will take.
+        real_target: BasicBlockId,
+        /// A block control flow could conceptually jump to, but won't in
+        /// practice.
+        imaginary_target: BasicBlockId,
+    },
+
+    /// A terminator for blocks that only take one path in reality, but where we reserve the right
+    /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
+    /// with no function calls for example.
+    ///
+    /// At runtime this is semantically just a goto.
+    ///
+    /// Disallowed after drop elaboration.
+    FalseUnwind {
+        /// The target normal control flow will take.
+        real_target: BasicBlockId,
+        /// The imaginary cleanup block link. This particular path will never be taken
+        /// in practice, but in order to avoid fragility we want to always
+        /// consider it in borrowck. We don't want to accept programs which
+        /// pass borrowck only when `panic=abort` or some assertions are disabled
+        /// due to release vs. debug mode builds. This needs to be an `Option` because
+        /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
+        unwind: Option<BasicBlockId>,
+    },
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum BorrowKind {
+    /// Data must be immutable and is aliasable.
+    Shared,
+
+    /// The immediately borrowed place must be immutable, but projections from
+    /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
+    /// conflict with a mutable borrow of `a.b.c`.
+    ///
+    /// This is used when lowering matches: when matching on a place we want to
+    /// ensure that place have the same value from the start of the match until
+    /// an arm is selected. This prevents this code from compiling:
+    /// ```compile_fail,E0510
+    /// let mut x = &Some(0);
+    /// match *x {
+    ///     None => (),
+    ///     Some(_) if { x = &None; false } => (),
+    ///     Some(_) => (),
+    /// }
+    /// ```
+    /// This can't be a shared borrow because mutably borrowing (*x as Some).0
+    /// should not prevent `if let None = x { ... }`, for example, because the
+    /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
+    /// We can also report errors with this kind of borrow differently.
+    Shallow,
+
+    /// Data must be immutable but not aliasable. This kind of borrow
+    /// cannot currently be expressed by the user and is used only in
+    /// implicit closure bindings. It is needed when the closure is
+    /// borrowing or mutating a mutable referent, e.g.:
+    /// ```
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = || *x += 5;
+    /// ```
+    /// If we were to try to translate this closure into a more explicit
+    /// form, we'd encounter an error with the code as written:
+    /// ```compile_fail,E0594
+    /// struct Env<'a> { x: &'a &'a mut isize }
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = (&mut Env { x: &x }, fn_ptr);  // Closure is pair of env and fn
+    /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+    /// ```
+    /// This is then illegal because you cannot mutate an `&mut` found
+    /// in an aliasable location. To solve, you'd have to translate with
+    /// an `&mut` borrow:
+    /// ```compile_fail,E0596
+    /// struct Env<'a> { x: &'a mut &'a mut isize }
+    /// let mut z = 3;
+    /// let x: &mut isize = &mut z;
+    /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x
+    /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+    /// ```
+    /// Now the assignment to `**env.x` is legal, but creating a
+    /// mutable pointer to `x` is not because `x` is not mutable. We
+    /// could fix this by declaring `x` as `let mut x`. This is ok in
+    /// user code, if awkward, but extra weird for closures, since the
+    /// borrow is hidden.
+    ///
+    /// So we introduce a "unique imm" borrow -- the referent is
+    /// immutable, but not aliasable. This solves the problem. For
+    /// simplicity, we don't give users the way to express this
+    /// borrow, it's just used when translating closures.
+    Unique,
+
+    /// Data is mutable and not aliasable.
+    Mut {
+        /// `true` if this borrow arose from method-call auto-ref
+        /// (i.e., `adjustment::Adjust::Borrow`).
+        allow_two_phase_borrow: bool,
+    },
+}
+
+impl BorrowKind {
+    fn from_hir(m: hir_def::type_ref::Mutability) -> Self {
+        match m {
+            hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
+            hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+        }
+    }
+
+    fn from_chalk(m: Mutability) -> Self {
+        match m {
+            Mutability::Not => BorrowKind::Shared,
+            Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum UnOp {
+    /// The `!` operator for logical inversion
+    Not,
+    /// The `-` operator for negation
+    Neg,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum BinOp {
+    /// The `+` operator (addition)
+    Add,
+    /// The `-` operator (subtraction)
+    Sub,
+    /// The `*` operator (multiplication)
+    Mul,
+    /// The `/` operator (division)
+    ///
+    /// Division by zero is UB, because the compiler should have inserted checks
+    /// prior to this.
+    Div,
+    /// The `%` operator (modulus)
+    ///
+    /// Using zero as the modulus (second operand) is UB, because the compiler
+    /// should have inserted checks prior to this.
+    Rem,
+    /// The `^` operator (bitwise xor)
+    BitXor,
+    /// The `&` operator (bitwise and)
+    BitAnd,
+    /// The `|` operator (bitwise or)
+    BitOr,
+    /// The `<<` operator (shift left)
+    ///
+    /// The offset is truncated to the size of the first operand before shifting.
+    Shl,
+    /// The `>>` operator (shift right)
+    ///
+    /// The offset is truncated to the size of the first operand before shifting.
+    Shr,
+    /// The `==` operator (equality)
+    Eq,
+    /// The `<` operator (less than)
+    Lt,
+    /// The `<=` operator (less than or equal to)
+    Le,
+    /// The `!=` operator (not equal to)
+    Ne,
+    /// The `>=` operator (greater than or equal to)
+    Ge,
+    /// The `>` operator (greater than)
+    Gt,
+    /// The `ptr.offset` operator
+    Offset,
+}
+
+impl Display for BinOp {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(match self {
+            BinOp::Add => "+",
+            BinOp::Sub => "-",
+            BinOp::Mul => "*",
+            BinOp::Div => "/",
+            BinOp::Rem => "%",
+            BinOp::BitXor => "^",
+            BinOp::BitAnd => "&",
+            BinOp::BitOr => "|",
+            BinOp::Shl => "<<",
+            BinOp::Shr => ">>",
+            BinOp::Eq => "==",
+            BinOp::Lt => "<",
+            BinOp::Le => "<=",
+            BinOp::Ne => "!=",
+            BinOp::Ge => ">=",
+            BinOp::Gt => ">",
+            BinOp::Offset => "`offset`",
+        })
+    }
+}
+
+impl From<hir_def::expr::ArithOp> for BinOp {
+    fn from(value: hir_def::expr::ArithOp) -> Self {
+        match value {
+            hir_def::expr::ArithOp::Add => BinOp::Add,
+            hir_def::expr::ArithOp::Mul => BinOp::Mul,
+            hir_def::expr::ArithOp::Sub => BinOp::Sub,
+            hir_def::expr::ArithOp::Div => BinOp::Div,
+            hir_def::expr::ArithOp::Rem => BinOp::Rem,
+            hir_def::expr::ArithOp::Shl => BinOp::Shl,
+            hir_def::expr::ArithOp::Shr => BinOp::Shr,
+            hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
+            hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
+            hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
+        }
+    }
+}
+
+impl From<hir_def::expr::CmpOp> for BinOp {
+    fn from(value: hir_def::expr::CmpOp) -> Self {
+        match value {
+            hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
+            hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
+            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
+        }
+    }
+}
+
+impl From<Operand> for Rvalue {
+    fn from(x: Operand) -> Self {
+        Self::Use(x)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum CastKind {
+    /// An exposing pointer to address cast. A cast between a pointer and an integer type, or
+    /// between a function pointer and an integer type.
+    /// See the docs on `expose_addr` for more details.
+    PointerExposeAddress,
+    /// An address-to-pointer cast that picks up an exposed provenance.
+    /// See the docs on `from_exposed_addr` for more details.
+    PointerFromExposedAddress,
+    /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
+    /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
+    Pointer(PointerCast),
+    /// Cast into a dyn* object.
+    DynStar,
+    IntToInt,
+    FloatToInt,
+    FloatToFloat,
+    IntToFloat,
+    PtrToPtr,
+    FnPtrToPtr,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum Rvalue {
+    /// Yields the operand unchanged
+    Use(Operand),
+
+    /// Creates an array where each element is the value of the operand.
+    ///
+    /// This is the cause of a bug in the case where the repetition count is zero because the value
+    /// is not dropped, see [#74836].
+    ///
+    /// Corresponds to source code like `[x; 32]`.
+    ///
+    /// [#74836]: https://github.com/rust-lang/rust/issues/74836
+    //Repeat(Operand, ty::Const),
+
+    /// Creates a reference of the indicated kind to the place.
+    ///
+    /// There is not much to document here, because besides the obvious parts the semantics of this
+    /// are essentially entirely a part of the aliasing model. There are many UCG issues discussing
+    /// exactly what the behavior of this operation should be.
+    ///
+    /// `Shallow` borrows are disallowed after drop lowering.
+    Ref(BorrowKind, Place),
+
+    /// Creates a pointer/reference to the given thread local.
+    ///
+    /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a
+    /// `*const T`, and if neither of those apply a `&T`.
+    ///
+    /// **Note:** This is a runtime operation that actually executes code and is in this sense more
+    /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to
+    /// SIGILL for some reason that I (JakobDegen) never got a chance to look into.
+    ///
+    /// **Needs clarification**: Are there weird additional semantics here related to the runtime
+    /// nature of this operation?
+    //ThreadLocalRef(DefId),
+
+    /// Creates a pointer with the indicated mutability to the place.
+    ///
+    /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like
+    /// `&raw v` or `addr_of!(v)`.
+    ///
+    /// Like with references, the semantics of this operation are heavily dependent on the aliasing
+    /// model.
+    //AddressOf(Mutability, Place),
+
+    /// Yields the length of the place, as a `usize`.
+    ///
+    /// If the type of the place is an array, this is the array length. For slices (`[T]`, not
+    /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
+    /// ill-formed for places of other types.
+    Len(Place),
+
+    /// Performs essentially all of the casts that can be performed via `as`.
+    ///
+    /// This allows for casts from/to a variety of types.
+    ///
+    /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
+    /// `ArrayToPointer` and `MutToConstPointer` are special.
+    Cast(CastKind, Operand, Ty),
+
+    // FIXME link to `pointer::offset` when it hits stable.
+    /// * `Offset` has the same semantics as `pointer::offset`, except that the second
+    ///   parameter may be a `usize` as well.
+    /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
+    ///   raw pointers, or function pointers and return a `bool`. The types of the operands must be
+    ///   matching, up to the usual caveat of the lifetimes in function pointers.
+    /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
+    ///   same type and return a value of the same type as their LHS. Like in Rust, the RHS is
+    ///   truncated as needed.
+    /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
+    ///   types and return a value of that type.
+    /// * The remaining operations accept signed integers, unsigned integers, or floats with
+    ///   matching types and return a value of that type.
+    //BinaryOp(BinOp, Box<(Operand, Operand)>),
+
+    /// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
+    ///
+    /// When overflow checking is disabled and we are generating run-time code, the error condition
+    /// is false. Otherwise, and always during CTFE, the error condition is determined as described
+    /// below.
+    ///
+    /// For addition, subtraction, and multiplication on integers the error condition is set when
+    /// the infinite precision result would be unequal to the actual result.
+    ///
+    /// For shift operations on integers the error condition is set when the value of right-hand
+    /// side is greater than or equal to the number of bits in the type of the left-hand side, or
+    /// when the value of right-hand side is negative.
+    ///
+    /// Other combinations of types and operators are unsupported.
+    CheckedBinaryOp(BinOp, Operand, Operand),
+
+    /// Computes a value as described by the operation.
+    //NullaryOp(NullOp, Ty),
+
+    /// Exactly like `BinaryOp`, but less operands.
+    ///
+    /// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
+    /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
+    /// return a value with the same type as their operand.
+    UnaryOp(UnOp, Operand),
+
+    /// Computes the discriminant of the place, returning it as an integer of type
+    /// [`discriminant_ty`]. Returns zero for types without discriminant.
+    ///
+    /// The validity requirements for the underlying value are undecided for this rvalue, see
+    /// [#91095]. Note too that the value of the discriminant is not the same thing as the
+    /// variant index; use [`discriminant_for_variant`] to convert.
+    ///
+    /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
+    /// [#91095]: https://github.com/rust-lang/rust/issues/91095
+    /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
+    Discriminant(Place),
+
+    /// Creates an aggregate value, like a tuple or struct.
+    ///
+    /// This is needed because dataflow analysis needs to distinguish
+    /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo`
+    /// has a destructor.
+    ///
+    /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After
+    /// generator lowering, `Generator` aggregate kinds are disallowed too.
+    Aggregate(AggregateKind, Vec<Operand>),
+
+    /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
+    ///
+    /// This is different from a normal transmute because dataflow analysis will treat the box as
+    /// initialized but its content as uninitialized. Like other pointer casts, this in general
+    /// affects alias analysis.
+    ShallowInitBox(Operand, Ty),
+
+    /// A CopyForDeref is equivalent to a read from a place at the
+    /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
+    /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
+    /// that the only use of the returned value is a deref operation, immediately
+    /// followed by one or more projections. Drop elaboration treats this rvalue as if the
+    /// read never happened and just projects further. This allows simplifying various MIR
+    /// optimizations and codegen backends that previously had to handle deref operations anywhere
+    /// in a place.
+    CopyForDeref(Place),
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum StatementKind {
+    Assign(Place, Rvalue),
+    //FakeRead(Box<(FakeReadCause, Place)>),
+    //SetDiscriminant {
+    //    place: Box<Place>,
+    //    variant_index: VariantIdx,
+    //},
+    Deinit(Place),
+    StorageLive(LocalId),
+    StorageDead(LocalId),
+    //Retag(RetagKind, Box<Place>),
+    //AscribeUserType(Place, UserTypeProjection, Variance),
+    //Intrinsic(Box<NonDivergingIntrinsic>),
+    Nop,
+}
+impl StatementKind {
+    fn with_span(self, span: MirSpan) -> Statement {
+        Statement { kind: self, span }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Statement {
+    pub kind: StatementKind,
+    pub span: MirSpan,
+}
+
+#[derive(Debug, Default, PartialEq, Eq)]
+pub struct BasicBlock {
+    /// List of statements in this block.
+    pub statements: Vec<Statement>,
+
+    /// Terminator for this block.
+    ///
+    /// N.B., this should generally ONLY be `None` during construction.
+    /// Therefore, you should generally access it via the
+    /// `terminator()` or `terminator_mut()` methods. The only
+    /// exception is that certain passes, such as `simplify_cfg`, swap
+    /// out the terminator temporarily with `None` while they continue
+    /// to recurse over the set of basic blocks.
+    pub terminator: Option<Terminator>,
+
+    /// If true, this block lies on an unwind path. This is used
+    /// during codegen where distinct kinds of basic blocks may be
+    /// generated (particularly for MSVC cleanup). Unwind blocks must
+    /// only branch to other unwind blocks.
+    pub is_cleanup: bool,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct MirBody {
+    pub basic_blocks: Arena<BasicBlock>,
+    pub locals: Arena<Local>,
+    pub start_block: BasicBlockId,
+    pub owner: DefWithBodyId,
+    pub arg_count: usize,
+    pub binding_locals: ArenaMap<BindingId, LocalId>,
+    pub param_locals: Vec<LocalId>,
+}
+
+fn const_as_usize(c: &Const) -> usize {
+    try_const_usize(c).unwrap() as usize
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum MirSpan {
+    ExprId(ExprId),
+    PatId(PatId),
+    Unknown,
+}
+
+impl_from!(ExprId, PatId for MirSpan);
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
new file mode 100644
index 00000000000..c8729af86a9
--- /dev/null
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -0,0 +1,223 @@
+//! MIR borrow checker, which is used in diagnostics like `unused_mut`
+
+// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
+// if needed for implementing a proper borrow checker.
+
+use std::sync::Arc;
+
+use hir_def::DefWithBodyId;
+use la_arena::ArenaMap;
+use stdx::never;
+
+use crate::db::HirDatabase;
+
+use super::{
+    BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
+    Rvalue, StatementKind, Terminator,
+};
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Stores spans which implies that the local should be mutable.
+pub enum MutabilityReason {
+    Mut { spans: Vec<MirSpan> },
+    Not,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct BorrowckResult {
+    pub mir_body: Arc<MirBody>,
+    pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
+}
+
+pub fn borrowck_query(
+    db: &dyn HirDatabase,
+    def: DefWithBodyId,
+) -> Result<Arc<BorrowckResult>, MirLowerError> {
+    let _p = profile::span("borrowck_query");
+    let body = db.mir_body(def)?;
+    let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
+    Ok(Arc::new(r))
+}
+
+fn is_place_direct(lvalue: &Place) -> bool {
+    !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
+}
+
+enum ProjectionCase {
+    /// Projection is a local
+    Direct,
+    /// Projection is some field or slice of a local
+    DirectPart,
+    /// Projection is deref of something
+    Indirect,
+}
+
+fn place_case(lvalue: &Place) -> ProjectionCase {
+    let mut is_part_of = false;
+    for proj in lvalue.projection.iter().rev() {
+        match proj {
+            ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. }
+            | ProjectionElem::Field(_)
+            | ProjectionElem::TupleField(_)
+            | ProjectionElem::Index(_) => {
+                is_part_of = true;
+            }
+            ProjectionElem::OpaqueCast(_) => (),
+        }
+    }
+    if is_part_of {
+        ProjectionCase::DirectPart
+    } else {
+        ProjectionCase::Direct
+    }
+}
+
+/// Returns a map from basic blocks to the set of locals that might be ever initialized before
+/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
+/// `Uninit` and `drop` and similars after initialization.
+fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
+    let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
+        body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
+    fn dfs(
+        body: &MirBody,
+        b: BasicBlockId,
+        l: LocalId,
+        result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
+    ) {
+        let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
+        let block = &body.basic_blocks[b];
+        for statement in &block.statements {
+            match &statement.kind {
+                StatementKind::Assign(p, _) => {
+                    if p.projection.len() == 0 && p.local == l {
+                        is_ever_initialized = true;
+                    }
+                }
+                StatementKind::StorageDead(p) => {
+                    if *p == l {
+                        is_ever_initialized = false;
+                    }
+                }
+                StatementKind::Deinit(_) | StatementKind::Nop | StatementKind::StorageLive(_) => (),
+            }
+        }
+        let Some(terminator) = &block.terminator else {
+            never!("Terminator should be none only in construction");
+            return;
+        };
+        let targets = match terminator {
+            Terminator::Goto { target } => vec![*target],
+            Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
+            Terminator::Resume
+            | Terminator::Abort
+            | Terminator::Return
+            | Terminator::Unreachable => vec![],
+            Terminator::Call { target, cleanup, destination, .. } => {
+                if destination.projection.len() == 0 && destination.local == l {
+                    is_ever_initialized = true;
+                }
+                target.into_iter().chain(cleanup.into_iter()).copied().collect()
+            }
+            Terminator::Drop { .. }
+            | Terminator::DropAndReplace { .. }
+            | Terminator::Assert { .. }
+            | Terminator::Yield { .. }
+            | Terminator::GeneratorDrop
+            | Terminator::FalseEdge { .. }
+            | Terminator::FalseUnwind { .. } => {
+                never!("We don't emit these MIR terminators yet");
+                vec![]
+            }
+        };
+        for target in targets {
+            if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
+                result[target].insert(l, is_ever_initialized);
+                dfs(body, target, l, result);
+            }
+        }
+    }
+    for &l in &body.param_locals {
+        result[body.start_block].insert(l, true);
+        dfs(body, body.start_block, l, &mut result);
+    }
+    for l in body.locals.iter().map(|x| x.0) {
+        if !result[body.start_block].contains_idx(l) {
+            result[body.start_block].insert(l, false);
+            dfs(body, body.start_block, l, &mut result);
+        }
+    }
+    result
+}
+
+fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
+    let mut result: ArenaMap<LocalId, MutabilityReason> =
+        body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
+    let mut push_mut_span = |local, span| match &mut result[local] {
+        MutabilityReason::Mut { spans } => spans.push(span),
+        x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
+    };
+    let ever_init_maps = ever_initialized_map(body);
+    for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
+        let block = &body.basic_blocks[block_id];
+        for statement in &block.statements {
+            match &statement.kind {
+                StatementKind::Assign(place, value) => {
+                    match place_case(place) {
+                        ProjectionCase::Direct => {
+                            if ever_init_map.get(place.local).copied().unwrap_or_default() {
+                                push_mut_span(place.local, statement.span);
+                            } else {
+                                ever_init_map.insert(place.local, true);
+                            }
+                        }
+                        ProjectionCase::DirectPart => {
+                            // Partial initialization is not supported, so it is definitely `mut`
+                            push_mut_span(place.local, statement.span);
+                        }
+                        ProjectionCase::Indirect => (),
+                    }
+                    if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
+                        if is_place_direct(p) {
+                            push_mut_span(p.local, statement.span);
+                        }
+                    }
+                }
+                StatementKind::StorageDead(p) => {
+                    ever_init_map.insert(*p, false);
+                }
+                StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (),
+            }
+        }
+        let Some(terminator) = &block.terminator else {
+            never!("Terminator should be none only in construction");
+            continue;
+        };
+        match terminator {
+            Terminator::Goto { .. }
+            | Terminator::Resume
+            | Terminator::Abort
+            | Terminator::Return
+            | Terminator::Unreachable
+            | Terminator::FalseEdge { .. }
+            | Terminator::FalseUnwind { .. }
+            | Terminator::GeneratorDrop
+            | Terminator::SwitchInt { .. }
+            | Terminator::Drop { .. }
+            | Terminator::DropAndReplace { .. }
+            | Terminator::Assert { .. }
+            | Terminator::Yield { .. } => (),
+            Terminator::Call { destination, .. } => {
+                if destination.projection.len() == 0 {
+                    if ever_init_map.get(destination.local).copied().unwrap_or_default() {
+                        push_mut_span(destination.local, MirSpan::Unknown);
+                    } else {
+                        ever_init_map.insert(destination.local, true);
+                    }
+                }
+            }
+        }
+    }
+    result
+}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
new file mode 100644
index 00000000000..c5d843d9ebd
--- /dev/null
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -0,0 +1,1253 @@
+//! This module provides a MIR interpreter, which is used in const eval.
+
+use std::{borrow::Cow, collections::HashMap, iter};
+
+use base_db::CrateId;
+use chalk_ir::{
+    fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+    DebruijnIndex, TyKind,
+};
+use hir_def::{
+    builtin_type::BuiltinType,
+    lang_item::{lang_attr, LangItem},
+    layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
+    AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+};
+use intern::Interned;
+use la_arena::ArenaMap;
+
+use crate::{
+    consteval::{intern_const_scalar, ConstEvalError},
+    db::HirDatabase,
+    from_placeholder_idx,
+    infer::{normalize, PointerCast},
+    layout::layout_of_ty,
+    mapping::from_chalk,
+    method_resolution::lookup_impl_method,
+    CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
+};
+
+use super::{
+    const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError,
+    Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
+};
+
+pub struct Evaluator<'a> {
+    db: &'a dyn HirDatabase,
+    stack: Vec<u8>,
+    heap: Vec<u8>,
+    crate_id: CrateId,
+    // FIXME: This is a workaround, see the comment on `interpret_mir`
+    assert_placeholder_ty_is_unused: bool,
+    /// A general limit on execution, to prevent non terminating programs from breaking r-a main process
+    execution_limit: usize,
+    /// An additional limit on stack depth, to prevent stack overflow
+    stack_depth_limit: usize,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Address {
+    Stack(usize),
+    Heap(usize),
+}
+
+use Address::*;
+
+struct Interval {
+    addr: Address,
+    size: usize,
+}
+
+impl Interval {
+    fn new(addr: Address, size: usize) -> Self {
+        Self { addr, size }
+    }
+
+    fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+        memory.read_memory(self.addr, self.size)
+    }
+}
+
+enum IntervalOrOwned {
+    Owned(Vec<u8>),
+    Borrowed(Interval),
+}
+impl IntervalOrOwned {
+    pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result<Vec<u8>> {
+        Ok(match self {
+            IntervalOrOwned::Owned(o) => o,
+            IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(),
+        })
+    }
+}
+
+macro_rules! from_bytes {
+    ($ty:tt, $value:expr) => {
+        ($ty::from_le_bytes(match ($value).try_into() {
+            Ok(x) => x,
+            Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+        }))
+    };
+}
+
+impl Address {
+    fn from_bytes(x: &[u8]) -> Result<Self> {
+        Ok(Address::from_usize(from_bytes!(usize, x)))
+    }
+
+    fn from_usize(x: usize) -> Self {
+        if x > usize::MAX / 2 {
+            Stack(usize::MAX - x)
+        } else {
+            Heap(x)
+        }
+    }
+
+    fn to_bytes(&self) -> Vec<u8> {
+        usize::to_le_bytes(self.to_usize()).to_vec()
+    }
+
+    fn to_usize(&self) -> usize {
+        let as_num = match self {
+            Stack(x) => usize::MAX - *x,
+            Heap(x) => *x,
+        };
+        as_num
+    }
+
+    fn map(&self, f: impl FnOnce(usize) -> usize) -> Address {
+        match self {
+            Stack(x) => Stack(f(*x)),
+            Heap(x) => Heap(f(*x)),
+        }
+    }
+
+    fn offset(&self, offset: usize) -> Address {
+        self.map(|x| x + offset)
+    }
+}
+
+#[derive(Clone, PartialEq, Eq)]
+pub enum MirEvalError {
+    ConstEvalError(Box<ConstEvalError>),
+    LayoutError(LayoutError, Ty),
+    /// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place.
+    TypeError(&'static str),
+    /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
+    /// then use this type of error.
+    UndefinedBehavior(&'static str),
+    Panic,
+    MirLowerError(FunctionId, MirLowerError),
+    TypeIsUnsized(Ty, &'static str),
+    NotSupported(String),
+    InvalidConst(Const),
+    InFunction(FunctionId, Box<MirEvalError>),
+    ExecutionLimitExceeded,
+    StackOverflow,
+    TargetDataLayoutNotAvailable,
+}
+
+impl std::fmt::Debug for MirEvalError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(),
+            Self::LayoutError(arg0, arg1) => {
+                f.debug_tuple("LayoutError").field(arg0).field(arg1).finish()
+            }
+            Self::TypeError(arg0) => f.debug_tuple("TypeError").field(arg0).finish(),
+            Self::UndefinedBehavior(arg0) => {
+                f.debug_tuple("UndefinedBehavior").field(arg0).finish()
+            }
+            Self::Panic => write!(f, "Panic"),
+            Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"),
+            Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
+            Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
+            Self::StackOverflow => write!(f, "stack overflow"),
+            Self::MirLowerError(arg0, arg1) => {
+                f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
+            }
+            Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
+            Self::InvalidConst(arg0) => {
+                let data = &arg0.data(Interner);
+                f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
+            }
+            Self::InFunction(func, e) => {
+                let mut e = &**e;
+                let mut stack = vec![*func];
+                while let Self::InFunction(f, next_e) = e {
+                    e = &next_e;
+                    stack.push(*f);
+                }
+                f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
+            }
+        }
+    }
+}
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirEvalError::NotSupported(format!($x)))
+    };
+}
+
+impl From<ConstEvalError> for MirEvalError {
+    fn from(value: ConstEvalError) -> Self {
+        match value {
+            _ => MirEvalError::ConstEvalError(Box::new(value)),
+        }
+    }
+}
+
+type Result<T> = std::result::Result<T, MirEvalError>;
+
+struct Locals<'a> {
+    ptr: &'a ArenaMap<LocalId, Address>,
+    body: &'a MirBody,
+    subst: &'a Substitution,
+}
+
+pub fn interpret_mir(
+    db: &dyn HirDatabase,
+    body: &MirBody,
+    // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
+    // they share their body with their parent, so in MIR lowering we have locals of the parent body, which
+    // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
+    // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can
+    // (and probably should) do better here, for example by excluding bindings outside of the target expression.
+    assert_placeholder_ty_is_unused: bool,
+) -> Result<Const> {
+    let ty = body.locals[return_slot()].ty.clone();
+    let mut evaluator =
+        Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
+    let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
+    let memory_map = evaluator.create_memory_map(
+        &bytes,
+        &ty,
+        &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
+    )?;
+    return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+}
+
+impl Evaluator<'_> {
+    pub fn new<'a>(
+        db: &'a dyn HirDatabase,
+        crate_id: CrateId,
+        assert_placeholder_ty_is_unused: bool,
+    ) -> Evaluator<'a> {
+        Evaluator {
+            stack: vec![0],
+            heap: vec![0],
+            db,
+            crate_id,
+            assert_placeholder_ty_is_unused,
+            stack_depth_limit: 100,
+            execution_limit: 100_000,
+        }
+    }
+
+    fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
+        Ok(self.place_addr_and_ty(p, locals)?.0)
+    }
+
+    fn ptr_size(&self) -> usize {
+        match self.db.target_data_layout(self.crate_id) {
+            Some(x) => x.pointer_size.bytes_usize(),
+            None => 8,
+        }
+    }
+
+    fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> {
+        let mut addr = locals.ptr[p.local];
+        let mut ty: Ty =
+            self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
+        for proj in &p.projection {
+            match proj {
+                ProjectionElem::Deref => {
+                    ty = match &ty.data(Interner).kind {
+                        TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
+                        _ => {
+                            return Err(MirEvalError::TypeError(
+                                "Overloaded deref in MIR is disallowed",
+                            ))
+                        }
+                    };
+                    let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
+                    addr = Address::from_usize(x);
+                }
+                ProjectionElem::Index(op) => {
+                    let offset =
+                        from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
+                    match &ty.data(Interner).kind {
+                        TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
+                            TyKind::Slice(inner) => {
+                                ty = inner.clone();
+                                let ty_size = self.size_of_sized(
+                                    &ty,
+                                    locals,
+                                    "slice inner type should be sized",
+                                )?;
+                                let value = self.read_memory(addr, self.ptr_size() * 2)?;
+                                addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
+                            }
+                            x => not_supported!("MIR index for ref type {x:?}"),
+                        },
+                        TyKind::Array(inner, _) | TyKind::Slice(inner) => {
+                            ty = inner.clone();
+                            let ty_size = self.size_of_sized(
+                                &ty,
+                                locals,
+                                "array inner type should be sized",
+                            )?;
+                            addr = addr.offset(ty_size * offset);
+                        }
+                        x => not_supported!("MIR index for type {x:?}"),
+                    }
+                }
+                &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
+                    TyKind::Tuple(_, subst) => {
+                        let layout = self.layout(&ty)?;
+                        ty = subst
+                            .as_slice(Interner)
+                            .get(f)
+                            .ok_or(MirEvalError::TypeError("not enough tuple fields"))?
+                            .assert_ty_ref(Interner)
+                            .clone();
+                        let offset = layout.fields.offset(f).bytes_usize();
+                        addr = addr.offset(offset);
+                    }
+                    _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
+                },
+                ProjectionElem::Field(f) => match &ty.data(Interner).kind {
+                    TyKind::Adt(adt, subst) => {
+                        let layout = self.layout_adt(adt.0, subst.clone())?;
+                        let variant_layout = match &layout.variants {
+                            Variants::Single { .. } => &layout,
+                            Variants::Multiple { variants, .. } => {
+                                &variants[match f.parent {
+                                    hir_def::VariantId::EnumVariantId(x) => {
+                                        RustcEnumVariantIdx(x.local_id)
+                                    }
+                                    _ => {
+                                        return Err(MirEvalError::TypeError(
+                                            "Multivariant layout only happens for enums",
+                                        ))
+                                    }
+                                }]
+                            }
+                        };
+                        ty = self.db.field_types(f.parent)[f.local_id]
+                            .clone()
+                            .substitute(Interner, subst);
+                        let offset = variant_layout
+                            .fields
+                            .offset(u32::from(f.local_id.into_raw()) as usize)
+                            .bytes_usize();
+                        addr = addr.offset(offset);
+                    }
+                    _ => return Err(MirEvalError::TypeError("Only adt has fields")),
+                },
+                ProjectionElem::ConstantIndex { .. } => {
+                    not_supported!("constant index")
+                }
+                ProjectionElem::Subslice { .. } => not_supported!("subslice"),
+                ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
+            }
+        }
+        Ok((addr, ty))
+    }
+
+    fn layout(&self, ty: &Ty) -> Result<Layout> {
+        layout_of_ty(self.db, ty, self.crate_id)
+            .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
+    }
+
+    fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> {
+        self.db.layout_of_adt(adt, subst.clone()).map_err(|e| {
+            MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
+        })
+    }
+
+    fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
+        Ok(self.place_addr_and_ty(p, locals)?.1)
+    }
+
+    fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result<Ty> {
+        Ok(match o {
+            Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
+            Operand::Constant(c) => c.data(Interner).ty.clone(),
+        })
+    }
+
+    fn interpret_mir(
+        &mut self,
+        body: &MirBody,
+        args: impl Iterator<Item = Vec<u8>>,
+        subst: Substitution,
+    ) -> Result<Vec<u8>> {
+        if let Some(x) = self.stack_depth_limit.checked_sub(1) {
+            self.stack_depth_limit = x;
+        } else {
+            return Err(MirEvalError::StackOverflow);
+        }
+        let mut current_block_idx = body.start_block;
+        let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst };
+        let (locals_ptr, stack_size) = {
+            let mut stack_ptr = self.stack.len();
+            let addr = body
+                .locals
+                .iter()
+                .map(|(id, x)| {
+                    let size =
+                        self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
+                    let my_ptr = stack_ptr;
+                    stack_ptr += size;
+                    Ok((id, Stack(my_ptr)))
+                })
+                .collect::<Result<ArenaMap<LocalId, _>>>()?;
+            let stack_size = stack_ptr - self.stack.len();
+            (addr, stack_size)
+        };
+        locals.ptr = &locals_ptr;
+        self.stack.extend(iter::repeat(0).take(stack_size));
+        let mut remain_args = body.arg_count;
+        for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) {
+            self.write_memory(*addr, &value)?;
+            if remain_args == 0 {
+                return Err(MirEvalError::TypeError("more arguments provided"));
+            }
+            remain_args -= 1;
+        }
+        if remain_args > 0 {
+            return Err(MirEvalError::TypeError("not enough arguments provided"));
+        }
+        loop {
+            let current_block = &body.basic_blocks[current_block_idx];
+            if let Some(x) = self.execution_limit.checked_sub(1) {
+                self.execution_limit = x;
+            } else {
+                return Err(MirEvalError::ExecutionLimitExceeded);
+            }
+            for statement in &current_block.statements {
+                match &statement.kind {
+                    StatementKind::Assign(l, r) => {
+                        let addr = self.place_addr(l, &locals)?;
+                        let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?;
+                        self.write_memory(addr, &result)?;
+                    }
+                    StatementKind::Deinit(_) => not_supported!("de-init statement"),
+                    StatementKind::StorageLive(_)
+                    | StatementKind::StorageDead(_)
+                    | StatementKind::Nop => (),
+                }
+            }
+            let Some(terminator) = current_block.terminator.as_ref() else {
+                not_supported!("block without terminator");
+            };
+            match terminator {
+                Terminator::Goto { target } => {
+                    current_block_idx = *target;
+                }
+                Terminator::Call {
+                    func,
+                    args,
+                    destination,
+                    target,
+                    cleanup: _,
+                    from_hir_call: _,
+                } => {
+                    let fn_ty = self.operand_ty(func, &locals)?;
+                    match &fn_ty.data(Interner).kind {
+                        TyKind::FnDef(def, generic_args) => {
+                            let def: CallableDefId = from_chalk(self.db, *def);
+                            let generic_args = self.subst_filler(generic_args, &locals);
+                            match def {
+                                CallableDefId::FunctionId(def) => {
+                                    let arg_bytes = args
+                                        .iter()
+                                        .map(|x| {
+                                            Ok(self
+                                                .eval_operand(x, &locals)?
+                                                .get(&self)?
+                                                .to_owned())
+                                        })
+                                        .collect::<Result<Vec<_>>>()?
+                                        .into_iter();
+                                    let function_data = self.db.function_data(def);
+                                    let is_intrinsic = match &function_data.abi {
+                                        Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+                                        None => match def.lookup(self.db.upcast()).container {
+                                            hir_def::ItemContainerId::ExternBlockId(block) => {
+                                                let id = block.lookup(self.db.upcast()).id;
+                                                id.item_tree(self.db.upcast())[id.value]
+                                                    .abi
+                                                    .as_deref()
+                                                    == Some("rust-intrinsic")
+                                            }
+                                            _ => false,
+                                        },
+                                    };
+                                    let result = if is_intrinsic {
+                                        self.exec_intrinsic(
+                                            function_data
+                                                .name
+                                                .as_text()
+                                                .unwrap_or_default()
+                                                .as_str(),
+                                            arg_bytes,
+                                            generic_args,
+                                            &locals,
+                                        )?
+                                    } else if let Some(x) = self.detect_lang_function(def) {
+                                        self.exec_lang_item(x, arg_bytes)?
+                                    } else {
+                                        let trait_env = {
+                                            let Some(d) = body.owner.as_generic_def_id() else {
+                                                not_supported!("trait resolving in non generic def id");
+                                            };
+                                            self.db.trait_environment(d)
+                                        };
+                                        let (imp, generic_args) = lookup_impl_method(
+                                            self.db,
+                                            trait_env,
+                                            def,
+                                            generic_args.clone(),
+                                        );
+                                        let generic_args =
+                                            self.subst_filler(&generic_args, &locals);
+                                        let def = imp.into();
+                                        let mir_body = self
+                                            .db
+                                            .mir_body(def)
+                                            .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+                                        self.interpret_mir(&mir_body, arg_bytes, generic_args)
+                                            .map_err(|e| {
+                                                MirEvalError::InFunction(imp, Box::new(e))
+                                            })?
+                                    };
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                                CallableDefId::StructId(id) => {
+                                    let (size, variant_layout, tag) = self.layout_of_variant(
+                                        id.into(),
+                                        generic_args.clone(),
+                                        &locals,
+                                    )?;
+                                    let result = self.make_by_layout(
+                                        size,
+                                        &variant_layout,
+                                        tag,
+                                        args,
+                                        &locals,
+                                    )?;
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                                CallableDefId::EnumVariantId(id) => {
+                                    let (size, variant_layout, tag) = self.layout_of_variant(
+                                        id.into(),
+                                        generic_args.clone(),
+                                        &locals,
+                                    )?;
+                                    let result = self.make_by_layout(
+                                        size,
+                                        &variant_layout,
+                                        tag,
+                                        args,
+                                        &locals,
+                                    )?;
+                                    let dest_addr = self.place_addr(destination, &locals)?;
+                                    self.write_memory(dest_addr, &result)?;
+                                }
+                            }
+                            current_block_idx =
+                                target.expect("broken mir, function without target");
+                        }
+                        _ => not_supported!("unknown function type"),
+                    }
+                }
+                Terminator::SwitchInt { discr, targets } => {
+                    let val = u128::from_le_bytes(pad16(
+                        self.eval_operand(discr, &locals)?.get(&self)?,
+                        false,
+                    ));
+                    current_block_idx = targets.target_for_value(val);
+                }
+                Terminator::Return => {
+                    let ty = body.locals[return_slot()].ty.clone();
+                    self.stack_depth_limit += 1;
+                    return Ok(self
+                        .read_memory(
+                            locals.ptr[return_slot()],
+                            self.size_of_sized(&ty, &locals, "return type")?,
+                        )?
+                        .to_owned());
+                }
+                Terminator::Unreachable => {
+                    return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
+                }
+                _ => not_supported!("unknown terminator"),
+            }
+        }
+    }
+
+    fn eval_rvalue<'a>(
+        &'a mut self,
+        r: &'a Rvalue,
+        locals: &'a Locals<'a>,
+    ) -> Result<IntervalOrOwned> {
+        use IntervalOrOwned::*;
+        Ok(match r {
+            Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
+            Rvalue::Ref(_, p) => {
+                let addr = self.place_addr(p, locals)?;
+                Owned(addr.to_bytes())
+            }
+            Rvalue::Len(_) => not_supported!("rvalue len"),
+            Rvalue::UnaryOp(op, val) => {
+                let mut c = self.eval_operand(val, locals)?.get(&self)?;
+                let mut ty = self.operand_ty(val, locals)?;
+                while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
+                    ty = z.clone();
+                    let size = self.size_of_sized(&ty, locals, "operand of unary op")?;
+                    c = self.read_memory(Address::from_bytes(c)?, size)?;
+                }
+                let mut c = c.to_vec();
+                if ty.as_builtin() == Some(BuiltinType::Bool) {
+                    c[0] = 1 - c[0];
+                } else {
+                    match op {
+                        UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
+                        UnOp::Neg => {
+                            c.iter_mut().for_each(|x| *x = !*x);
+                            for k in c.iter_mut() {
+                                let o;
+                                (*k, o) = k.overflowing_add(1);
+                                if !o {
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                Owned(c)
+            }
+            Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
+                let lc = self.eval_operand(lhs, locals)?;
+                let rc = self.eval_operand(rhs, locals)?;
+                let mut lc = lc.get(&self)?;
+                let mut rc = rc.get(&self)?;
+                let mut ty = self.operand_ty(lhs, locals)?;
+                while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
+                    ty = z.clone();
+                    let size = self.size_of_sized(&ty, locals, "operand of binary op")?;
+                    lc = self.read_memory(Address::from_bytes(lc)?, size)?;
+                    rc = self.read_memory(Address::from_bytes(rc)?, size)?;
+                }
+                let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
+                let l128 = i128::from_le_bytes(pad16(lc, is_signed));
+                let r128 = i128::from_le_bytes(pad16(rc, is_signed));
+                match op {
+                    BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
+                        let r = match op {
+                            BinOp::Ge => l128 >= r128,
+                            BinOp::Gt => l128 > r128,
+                            BinOp::Le => l128 <= r128,
+                            BinOp::Lt => l128 < r128,
+                            BinOp::Eq => l128 == r128,
+                            BinOp::Ne => l128 != r128,
+                            _ => unreachable!(),
+                        };
+                        let r = r as u8;
+                        Owned(vec![r])
+                    }
+                    BinOp::BitAnd
+                    | BinOp::BitOr
+                    | BinOp::BitXor
+                    | BinOp::Add
+                    | BinOp::Mul
+                    | BinOp::Div
+                    | BinOp::Rem
+                    | BinOp::Sub => {
+                        let r = match op {
+                            BinOp::Add => l128.overflowing_add(r128).0,
+                            BinOp::Mul => l128.overflowing_mul(r128).0,
+                            BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?,
+                            BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?,
+                            BinOp::Sub => l128.overflowing_sub(r128).0,
+                            BinOp::BitAnd => l128 & r128,
+                            BinOp::BitOr => l128 | r128,
+                            BinOp::BitXor => l128 ^ r128,
+                            _ => unreachable!(),
+                        };
+                        let r = r.to_le_bytes();
+                        for &k in &r[lc.len()..] {
+                            if k != 0 && (k != 255 || !is_signed) {
+                                return Err(MirEvalError::Panic);
+                            }
+                        }
+                        Owned(r[0..lc.len()].into())
+                    }
+                    BinOp::Shl | BinOp::Shr => {
+                        let shift_amout = if r128 < 0 {
+                            return Err(MirEvalError::Panic);
+                        } else if r128 > 128 {
+                            return Err(MirEvalError::Panic);
+                        } else {
+                            r128 as u8
+                        };
+                        let r = match op {
+                            BinOp::Shl => l128 << shift_amout,
+                            BinOp::Shr => l128 >> shift_amout,
+                            _ => unreachable!(),
+                        };
+                        Owned(r.to_le_bytes()[0..lc.len()].into())
+                    }
+                    BinOp::Offset => not_supported!("offset binop"),
+                }
+            }
+            Rvalue::Discriminant(p) => {
+                let ty = self.place_ty(p, locals)?;
+                let bytes = self.eval_place(p, locals)?.get(&self)?;
+                let layout = self.layout(&ty)?;
+                match layout.variants {
+                    Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
+                    Variants::Multiple { tag, tag_encoding, .. } => {
+                        let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
+                            not_supported!("missing target data layout");
+                        };
+                        let size = tag.size(&*target_data_layout).bytes_usize();
+                        let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+                        match tag_encoding {
+                            TagEncoding::Direct => {
+                                let tag = &bytes[offset..offset + size];
+                                Owned(pad16(tag, false).to_vec())
+                            }
+                            TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+                                let tag = &bytes[offset..offset + size];
+                                let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
+                                    .wrapping_sub(niche_start as i128);
+                                let enum_id = match ty.kind(Interner) {
+                                    TyKind::Adt(e, _) => match e.0 {
+                                        AdtId::EnumId(e) => e,
+                                        _ => not_supported!("Non enum with multi variant layout"),
+                                    },
+                                    _ => not_supported!("Non adt with multi variant layout"),
+                                };
+                                let enum_data = self.db.enum_data(enum_id);
+                                let result = 'b: {
+                                    for (local_id, _) in enum_data.variants.iter() {
+                                        if candidate_discriminant
+                                            == self.db.const_eval_discriminant(EnumVariantId {
+                                                parent: enum_id,
+                                                local_id,
+                                            })?
+                                        {
+                                            break 'b candidate_discriminant;
+                                        }
+                                    }
+                                    self.db.const_eval_discriminant(EnumVariantId {
+                                        parent: enum_id,
+                                        local_id: untagged_variant.0,
+                                    })?
+                                };
+                                Owned(result.to_le_bytes().to_vec())
+                            }
+                        }
+                    }
+                }
+            }
+            Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"),
+            Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
+            Rvalue::Aggregate(kind, values) => match kind {
+                AggregateKind::Array(_) => {
+                    let mut r = vec![];
+                    for x in values {
+                        let value = self.eval_operand(x, locals)?.get(&self)?;
+                        r.extend(value);
+                    }
+                    Owned(r)
+                }
+                AggregateKind::Tuple(ty) => {
+                    let layout = self.layout(&ty)?;
+                    Owned(self.make_by_layout(
+                        layout.size.bytes_usize(),
+                        &layout,
+                        None,
+                        values,
+                        locals,
+                    )?)
+                }
+                AggregateKind::Union(x, f) => {
+                    let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+                    let offset = layout
+                        .fields
+                        .offset(u32::from(f.local_id.into_raw()) as usize)
+                        .bytes_usize();
+                    let op = self.eval_operand(&values[0], locals)?.get(&self)?;
+                    let mut result = vec![0; layout.size.bytes_usize()];
+                    result[offset..offset + op.len()].copy_from_slice(op);
+                    Owned(result)
+                }
+                AggregateKind::Adt(x, subst) => {
+                    let (size, variant_layout, tag) =
+                        self.layout_of_variant(*x, subst.clone(), locals)?;
+                    Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
+                }
+            },
+            Rvalue::Cast(kind, operand, target_ty) => match kind {
+                CastKind::PointerExposeAddress => not_supported!("exposing pointer address"),
+                CastKind::PointerFromExposedAddress => {
+                    not_supported!("creating pointer from exposed address")
+                }
+                CastKind::Pointer(cast) => match cast {
+                    PointerCast::Unsize => {
+                        let current_ty = self.operand_ty(operand, locals)?;
+                        match &target_ty.data(Interner).kind {
+                            TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+                                match &ty.data(Interner).kind {
+                                    TyKind::Slice(_) => match &current_ty.data(Interner).kind {
+                                        TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+                                            match &ty.data(Interner).kind {
+                                                TyKind::Array(_, size) => {
+                                                    let addr = self
+                                                        .eval_operand(operand, locals)?
+                                                        .get(&self)?;
+                                                    let len = const_as_usize(size);
+                                                    let mut r = Vec::with_capacity(16);
+                                                    r.extend(addr.iter().copied());
+                                                    r.extend(len.to_le_bytes().into_iter());
+                                                    Owned(r)
+                                                }
+                                                _ => {
+                                                    not_supported!("slice unsizing from non arrays")
+                                                }
+                                            }
+                                        }
+                                        _ => not_supported!("slice unsizing from non pointers"),
+                                    },
+                                    TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"),
+                                    _ => not_supported!("unknown unsized cast"),
+                                }
+                            }
+                            _ => not_supported!("unsized cast on unknown pointer type"),
+                        }
+                    }
+                    x => not_supported!("pointer cast {x:?}"),
+                },
+                CastKind::DynStar => not_supported!("dyn star cast"),
+                CastKind::IntToInt => {
+                    // FIXME: handle signed cast
+                    let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
+                    let dest_size =
+                        self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
+                    Owned(current[0..dest_size].to_vec())
+                }
+                CastKind::FloatToInt => not_supported!("float to int cast"),
+                CastKind::FloatToFloat => not_supported!("float to float cast"),
+                CastKind::IntToFloat => not_supported!("float to int cast"),
+                CastKind::PtrToPtr => not_supported!("ptr to ptr cast"),
+                CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
+            },
+        })
+    }
+
+    fn layout_of_variant(
+        &mut self,
+        x: VariantId,
+        subst: Substitution,
+        locals: &Locals<'_>,
+    ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+        let adt = x.adt_id();
+        if let DefWithBodyId::VariantId(f) = locals.body.owner {
+            if let VariantId::EnumVariantId(x) = x {
+                if AdtId::from(f.parent) == adt {
+                    // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
+                    // infinite sized type errors) we use a dummy layout
+                    let i = self.db.const_eval_discriminant(x)?;
+                    return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i))));
+                }
+            }
+        }
+        let layout = self.layout_adt(adt, subst)?;
+        Ok(match layout.variants {
+            Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
+            Variants::Multiple { variants, tag, tag_encoding, .. } => {
+                let cx = self
+                    .db
+                    .target_data_layout(self.crate_id)
+                    .ok_or(MirEvalError::TargetDataLayoutNotAvailable)?;
+                let enum_variant_id = match x {
+                    VariantId::EnumVariantId(x) => x,
+                    _ => not_supported!("multi variant layout for non-enums"),
+                };
+                let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id);
+                let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?;
+                let variant_layout = variants[rustc_enum_variant_idx].clone();
+                let have_tag = match tag_encoding {
+                    TagEncoding::Direct => true,
+                    TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
+                        discriminant = discriminant.wrapping_add(niche_start as i128);
+                        untagged_variant != rustc_enum_variant_idx
+                    }
+                };
+                (
+                    layout.size.bytes_usize(),
+                    variant_layout,
+                    if have_tag {
+                        Some((
+                            layout.fields.offset(0).bytes_usize(),
+                            tag.size(&*cx).bytes_usize(),
+                            discriminant,
+                        ))
+                    } else {
+                        None
+                    },
+                )
+            }
+        })
+    }
+
+    fn make_by_layout(
+        &mut self,
+        size: usize, // Not neccessarily equal to variant_layout.size
+        variant_layout: &Layout,
+        tag: Option<(usize, usize, i128)>,
+        values: &Vec<Operand>,
+        locals: &Locals<'_>,
+    ) -> Result<Vec<u8>> {
+        let mut result = vec![0; size];
+        if let Some((offset, size, value)) = tag {
+            result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
+        }
+        for (i, op) in values.iter().enumerate() {
+            let offset = variant_layout.fields.offset(i).bytes_usize();
+            let op = self.eval_operand(op, locals)?.get(&self)?;
+            result[offset..offset + op.len()].copy_from_slice(op);
+        }
+        Ok(result)
+    }
+
+    fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result<Interval> {
+        Ok(match x {
+            Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?,
+            Operand::Constant(konst) => {
+                let data = &konst.data(Interner);
+                match &data.value {
+                    chalk_ir::ConstValue::BoundVar(b) => {
+                        let c = locals
+                            .subst
+                            .as_slice(Interner)
+                            .get(b.index)
+                            .ok_or(MirEvalError::TypeError("missing generic arg"))?
+                            .assert_const_ref(Interner);
+                        self.eval_operand(&Operand::Constant(c.clone()), locals)?
+                    }
+                    chalk_ir::ConstValue::InferenceVar(_) => {
+                        not_supported!("inference var constant")
+                    }
+                    chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"),
+                    chalk_ir::ConstValue::Concrete(c) => match &c.interned {
+                        ConstScalar::Bytes(v, memory_map) => {
+                            let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
+                            let patch_map = memory_map.transform_addresses(|b| {
+                                let addr = self.heap_allocate(b.len());
+                                self.write_memory(addr, b)?;
+                                Ok(addr.to_usize())
+                            })?;
+                            let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len());
+                            if size != v.len() {
+                                // Handle self enum
+                                if size == 16 && v.len() < 16 {
+                                    v = Cow::Owned(pad16(&v, false).to_vec());
+                                } else if size < 16 && v.len() == 16 {
+                                    v = Cow::Owned(v[0..size].to_vec());
+                                } else {
+                                    return Err(MirEvalError::InvalidConst(konst.clone()));
+                                }
+                            }
+                            let addr = self.heap_allocate(size);
+                            self.write_memory(addr, &v)?;
+                            self.patch_addresses(&patch_map, addr, &data.ty, locals)?;
+                            Interval::new(addr, size)
+                        }
+                        ConstScalar::Unknown => not_supported!("evaluating unknown const"),
+                    },
+                }
+            }
+        })
+    }
+
+    fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+        let addr = self.place_addr(p, locals)?;
+        Ok(Interval::new(
+            addr,
+            self.size_of_sized(&self.place_ty(p, locals)?, locals, "type of this place")?,
+        ))
+    }
+
+    fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> {
+        let (mem, pos) = match addr {
+            Stack(x) => (&self.stack, x),
+            Heap(x) => (&self.heap, x),
+        };
+        mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
+    }
+
+    fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+        let (mem, pos) = match addr {
+            Stack(x) => (&mut self.stack, x),
+            Heap(x) => (&mut self.heap, x),
+        };
+        mem.get_mut(pos..pos + r.len())
+            .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
+            .copy_from_slice(r);
+        Ok(())
+    }
+
+    fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<usize>> {
+        if let DefWithBodyId::VariantId(f) = locals.body.owner {
+            if let Some((adt, _)) = ty.as_adt() {
+                if AdtId::from(f.parent) == adt {
+                    // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
+                    // infinite sized type errors) we use a dummy size
+                    return Ok(Some(16));
+                }
+            }
+        }
+        let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?;
+        let layout = self.layout(ty);
+        if self.assert_placeholder_ty_is_unused {
+            if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
+                return Ok(Some(0));
+            }
+        }
+        let layout = layout?;
+        Ok(layout.is_sized().then(|| layout.size.bytes_usize()))
+    }
+
+    /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
+    /// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
+    fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> {
+        match self.size_of(ty, locals)? {
+            Some(x) => Ok(x),
+            None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
+        }
+    }
+
+    /// Uses `ty_filler` to fill an entire subst
+    fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
+        Substitution::from_iter(
+            Interner,
+            subst.iter(Interner).map(|x| match x.data(Interner) {
+                chalk_ir::GenericArgData::Ty(ty) => {
+                    let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
+                        return x.clone();
+                    };
+                    chalk_ir::GenericArgData::Ty(ty).intern(Interner)
+                }
+                _ => x.clone(),
+            }),
+        )
+    }
+
+    /// This function substitutes placeholders of the body with the provided subst, effectively plays
+    /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
+    /// position impl traits) with their underlying type.
+    fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
+        struct Filler<'a> {
+            db: &'a dyn HirDatabase,
+            subst: &'a Substitution,
+            skip_params: usize,
+        }
+        impl FallibleTypeFolder<Interner> for Filler<'_> {
+            type Error = MirEvalError;
+
+            fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+                self
+            }
+
+            fn interner(&self) -> Interner {
+                Interner
+            }
+
+            fn try_fold_ty(
+                &mut self,
+                ty: Ty,
+                outer_binder: DebruijnIndex,
+            ) -> std::result::Result<Ty, Self::Error> {
+                match ty.kind(Interner) {
+                    TyKind::OpaqueType(id, subst) => {
+                        let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+                        match impl_trait_id {
+                            crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+                                let infer = self.db.infer(func.into());
+                                let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
+                                filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+                            }
+                            crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+                                not_supported!("async block impl trait");
+                            }
+                        }
+                    }
+                    _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+                }
+            }
+
+            fn try_fold_free_placeholder_ty(
+                &mut self,
+                idx: chalk_ir::PlaceholderIndex,
+                _outer_binder: DebruijnIndex,
+            ) -> std::result::Result<Ty, Self::Error> {
+                let x = from_placeholder_idx(self.db, idx);
+                Ok(self
+                    .subst
+                    .as_slice(Interner)
+                    .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params)
+                    .and_then(|x| x.ty(Interner))
+                    .ok_or(MirEvalError::TypeError("Generic arg not provided"))?
+                    .clone())
+            }
+        }
+        let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
+        Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?))
+    }
+
+    fn heap_allocate(&mut self, s: usize) -> Address {
+        let pos = self.heap.len();
+        self.heap.extend(iter::repeat(0).take(s));
+        Address::Heap(pos)
+    }
+
+    pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
+        self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
+    }
+
+    fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
+        let candidate = lang_attr(self.db.upcast(), def)?;
+        // filter normal lang functions out
+        if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
+            return None;
+        }
+        Some(candidate)
+    }
+
+    fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
+        // FIXME: support indirect references
+        let mut mm = MemoryMap::default();
+        match ty.kind(Interner) {
+            TyKind::Ref(_, _, t) => {
+                let size = self.size_of(t, locals)?;
+                match size {
+                    Some(size) => {
+                        let addr_usize = from_bytes!(usize, bytes);
+                        mm.insert(
+                            addr_usize,
+                            self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
+                        )
+                    }
+                    None => {
+                        let element_size = match t.kind(Interner) {
+                            TyKind::Str => 1,
+                            TyKind::Slice(t) => {
+                                self.size_of_sized(t, locals, "slice inner type")?
+                            }
+                            _ => return Ok(mm), // FIXME: support other kind of unsized types
+                        };
+                        let (addr, meta) = bytes.split_at(bytes.len() / 2);
+                        let size = element_size * from_bytes!(usize, meta);
+                        let addr = Address::from_bytes(addr)?;
+                        mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec());
+                    }
+                }
+            }
+            _ => (),
+        }
+        Ok(mm)
+    }
+
+    fn patch_addresses(
+        &mut self,
+        patch_map: &HashMap<usize, usize>,
+        addr: Address,
+        ty: &Ty,
+        locals: &Locals<'_>,
+    ) -> Result<()> {
+        // FIXME: support indirect references
+        let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
+        match ty.kind(Interner) {
+            TyKind::Ref(_, _, t) => {
+                let size = self.size_of(t, locals)?;
+                match size {
+                    Some(_) => {
+                        let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
+                        if let Some(x) = patch_map.get(&current) {
+                            self.write_memory(addr, &x.to_le_bytes())?;
+                        }
+                    }
+                    None => {
+                        let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
+                        if let Some(x) = patch_map.get(&current) {
+                            self.write_memory(addr, &x.to_le_bytes())?;
+                        }
+                    }
+                }
+            }
+            _ => (),
+        }
+        Ok(())
+    }
+
+    fn exec_intrinsic(
+        &self,
+        as_str: &str,
+        _arg_bytes: impl Iterator<Item = Vec<u8>>,
+        generic_args: Substitution,
+        locals: &Locals<'_>,
+    ) -> Result<Vec<u8>> {
+        match as_str {
+            "size_of" => {
+                let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+                    return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+                };
+                let size = self.size_of(ty, locals)?;
+                match size {
+                    Some(x) => Ok(x.to_le_bytes().to_vec()),
+                    None => return Err(MirEvalError::TypeError("size_of arg is unsized")),
+                }
+            }
+            _ => not_supported!("unknown intrinsic {as_str}"),
+        }
+    }
+
+    pub(crate) fn exec_lang_item(
+        &self,
+        x: LangItem,
+        mut args: std::vec::IntoIter<Vec<u8>>,
+    ) -> Result<Vec<u8>> {
+        use LangItem::*;
+        match x {
+            PanicFmt | BeginPanic => Err(MirEvalError::Panic),
+            SliceLen => {
+                let arg = args
+                    .next()
+                    .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
+                let ptr_size = arg.len() / 2;
+                Ok(arg[ptr_size..].into())
+            }
+            x => not_supported!("Executing lang item {x:?}"),
+        }
+    }
+}
+
+pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] {
+    let is_negative = is_signed && x.last().unwrap_or(&0) > &128;
+    let fill_with = if is_negative { 255 } else { 0 };
+    x.iter()
+        .copied()
+        .chain(iter::repeat(fill_with))
+        .take(16)
+        .collect::<Vec<u8>>()
+        .try_into()
+        .expect("iterator take is not working")
+}
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
new file mode 100644
index 00000000000..435a914088b
--- /dev/null
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -0,0 +1,1577 @@
+//! This module generates a polymorphic MIR from a hir body
+
+use std::{iter, mem, sync::Arc};
+
+use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
+use hir_def::{
+    body::Body,
+    expr::{
+        Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
+        RecordLitField,
+    },
+    lang_item::{LangItem, LangItemTarget},
+    layout::LayoutError,
+    path::Path,
+    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    DefWithBodyId, EnumVariantId, HasModule,
+};
+use hir_expand::name::Name;
+use la_arena::ArenaMap;
+
+use crate::{
+    consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
+    inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
+    utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+};
+
+use super::*;
+
+mod as_place;
+
+#[derive(Debug, Clone, Copy)]
+struct LoopBlocks {
+    begin: BasicBlockId,
+    /// `None` for loops that are not terminating
+    end: Option<BasicBlockId>,
+}
+
+struct MirLowerCtx<'a> {
+    result: MirBody,
+    owner: DefWithBodyId,
+    current_loop_blocks: Option<LoopBlocks>,
+    discr_temp: Option<Place>,
+    db: &'a dyn HirDatabase,
+    body: &'a Body,
+    infer: &'a InferenceResult,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum MirLowerError {
+    ConstEvalError(Box<ConstEvalError>),
+    LayoutError(LayoutError),
+    IncompleteExpr,
+    UnresolvedName(String),
+    RecordLiteralWithoutPath,
+    UnresolvedMethod,
+    UnresolvedField,
+    MissingFunctionDefinition,
+    TypeMismatch(TypeMismatch),
+    /// This should be never happen. Type mismatch should catch everything.
+    TypeError(&'static str),
+    NotSupported(String),
+    ContinueWithoutLoop,
+    BreakWithoutLoop,
+    Loop,
+    /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
+    ImplementationError(&'static str),
+    LangItemNotFound(LangItem),
+    MutatingRvalue,
+}
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirLowerError::NotSupported(format!($x)))
+    };
+}
+
+macro_rules! implementation_error {
+    ($x: expr) => {{
+        ::stdx::never!("MIR lower implementation bug: {}", $x);
+        return Err(MirLowerError::ImplementationError($x));
+    }};
+}
+
+impl From<ConstEvalError> for MirLowerError {
+    fn from(value: ConstEvalError) -> Self {
+        match value {
+            ConstEvalError::MirLowerError(e) => e,
+            _ => MirLowerError::ConstEvalError(Box::new(value)),
+        }
+    }
+}
+
+impl From<LayoutError> for MirLowerError {
+    fn from(value: LayoutError) -> Self {
+        MirLowerError::LayoutError(value)
+    }
+}
+
+impl MirLowerError {
+    fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self {
+        Self::UnresolvedName(p.display(db).to_string())
+    }
+}
+
+type Result<T> = std::result::Result<T, MirLowerError>;
+
+impl MirLowerCtx<'_> {
+    fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+        if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+            implementation_error!("unsized temporaries");
+        }
+        Ok(self.result.locals.alloc(Local { ty }))
+    }
+
+    fn lower_expr_to_some_operand(
+        &mut self,
+        expr_id: ExprId,
+        current: BasicBlockId,
+    ) -> Result<Option<(Operand, BasicBlockId)>> {
+        if !self.has_adjustments(expr_id) {
+            match &self.body.exprs[expr_id] {
+                Expr::Literal(l) => {
+                    let ty = self.expr_ty(expr_id);
+                    return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
+                }
+                _ => (),
+            }
+        }
+        let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
+            return Ok(None);
+        };
+        Ok(Some((Operand::Copy(p), current)))
+    }
+
+    fn lower_expr_to_place_with_adjust(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        current: BasicBlockId,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<BasicBlockId>> {
+        match adjustments.split_last() {
+            Some((last, rest)) => match &last.kind {
+                Adjust::NeverToAny => {
+                    let temp = self.temp(TyKind::Never.intern(Interner))?;
+                    self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
+                }
+                Adjust::Deref(_) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else {
+                            return Ok(None);
+                        };
+                    self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+                    Ok(Some(current))
+                }
+                Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
+                            return Ok(None);
+                        };
+                    let bk = BorrowKind::from_chalk(*m);
+                    self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
+                    Ok(Some(current))
+                }
+                Adjust::Pointer(cast) => {
+                    let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
+                            return Ok(None);
+                        };
+                    self.push_assignment(
+                        current,
+                        place,
+                        Rvalue::Cast(
+                            CastKind::Pointer(cast.clone()),
+                            Operand::Copy(p).into(),
+                            last.target.clone(),
+                        ),
+                        expr_id.into(),
+                    );
+                    Ok(Some(current))
+                }
+            },
+            None => self.lower_expr_to_place_without_adjust(expr_id, place, current),
+        }
+    }
+
+    fn lower_expr_to_place(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        prev_block: BasicBlockId,
+    ) -> Result<Option<BasicBlockId>> {
+        if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) {
+            return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments);
+        }
+        self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
+    }
+
+    fn lower_expr_to_place_without_adjust(
+        &mut self,
+        expr_id: ExprId,
+        place: Place,
+        mut current: BasicBlockId,
+    ) -> Result<Option<BasicBlockId>> {
+        match &self.body.exprs[expr_id] {
+            Expr::Missing => Err(MirLowerError::IncompleteExpr),
+            Expr::Path(p) => {
+                let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+                let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+                let pr = resolver
+                    .resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
+                    .ok_or_else(unresolved_name)?;
+                let pr = match pr {
+                    ResolveValueResult::ValueNs(v) => v,
+                    ResolveValueResult::Partial(..) => {
+                        if let Some(assoc) = self
+                            .infer
+                            .assoc_resolutions_for_expr(expr_id)
+                        {
+                            match assoc.0 {
+                                hir_def::AssocItemId::ConstId(c) => {
+                                    self.lower_const(c, current, place, expr_id.into())?;
+                                    return Ok(Some(current))
+                                },
+                                _ => not_supported!("associated functions and types"),
+                            }
+                        } else if let Some(variant) = self
+                            .infer
+                            .variant_resolution_for_expr(expr_id)
+                        {
+                            match variant {
+                                VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+                                VariantId::StructId(s) => ValueNs::StructId(s),
+                                VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+                            }
+                        } else {
+                            return Err(unresolved_name());
+                        }
+                    }
+                };
+                match pr {
+                    ValueNs::LocalBinding(pat_id) => {
+                        self.push_assignment(
+                            current,
+                            place,
+                            Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    ValueNs::ConstId(const_id) => {
+                        self.lower_const(const_id, current, place, expr_id.into())?;
+                        Ok(Some(current))
+                    }
+                    ValueNs::EnumVariantId(variant_id) => {
+                        let ty = self.infer.type_of_expr[expr_id].clone();
+                        let current = self.lower_enum_variant(
+                            variant_id,
+                            current,
+                            place,
+                            ty,
+                            vec![],
+                            expr_id.into(),
+                        )?;
+                        Ok(Some(current))
+                    }
+                    ValueNs::GenericParam(p) => {
+                        let Some(def) = self.owner.as_generic_def_id() else {
+                            not_supported!("owner without generic def id");
+                        };
+                        let gen = generics(self.db.upcast(), def);
+                        let ty = self.expr_ty(expr_id);
+                        self.push_assignment(
+                            current,
+                            place,
+                            Operand::Constant(
+                                ConstData {
+                                    ty,
+                                    value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
+                                        DebruijnIndex::INNERMOST,
+                                        gen.param_idx(p.into()).ok_or(MirLowerError::TypeError(
+                                            "fail to lower const generic param",
+                                        ))?,
+                                    )),
+                                }
+                                .intern(Interner),
+                            )
+                            .into(),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    ValueNs::StructId(_) => {
+                        // It's probably a unit struct or a zero sized function, so no action is needed.
+                        Ok(Some(current))
+                    }
+                    x => {
+                        not_supported!("unknown name {x:?} in value name space");
+                    }
+                }
+            }
+            Expr::If { condition, then_branch, else_branch } => {
+                let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else {
+                    return Ok(None);
+                };
+                let start_of_then = self.new_basic_block();
+                let end_of_then =
+                    self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?;
+                let start_of_else = self.new_basic_block();
+                let end_of_else = if let Some(else_branch) = else_branch {
+                    self.lower_expr_to_place(*else_branch, place, start_of_else)?
+                } else {
+                    Some(start_of_else)
+                };
+                self.set_terminator(
+                    current,
+                    Terminator::SwitchInt {
+                        discr,
+                        targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
+                    },
+                );
+                Ok(self.merge_blocks(end_of_then, end_of_else))
+            }
+            Expr::Let { pat, expr } => {
+                let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                let (then_target, else_target) = self.pattern_match(
+                    current,
+                    None,
+                    cond_place,
+                    self.expr_ty_after_adjustments(*expr),
+                    *pat,
+                    BindingAnnotation::Unannotated,
+                )?;
+                self.write_bytes_to_place(
+                    then_target,
+                    place.clone(),
+                    vec![1],
+                    TyBuilder::bool(),
+                    MirSpan::Unknown,
+                )?;
+                if let Some(else_target) = else_target {
+                    self.write_bytes_to_place(
+                        else_target,
+                        place,
+                        vec![0],
+                        TyBuilder::bool(),
+                        MirSpan::Unknown,
+                    )?;
+                }
+                Ok(self.merge_blocks(Some(then_target), else_target))
+            }
+            Expr::Unsafe { id: _, statements, tail } => {
+                self.lower_block_to_place(None, statements, current, *tail, place)
+            }
+            Expr::Block { id: _, statements, tail, label } => {
+                self.lower_block_to_place(*label, statements, current, *tail, place)
+            }
+            Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
+                if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
+                    this.set_goto(block, begin);
+                }
+                Ok(())
+            }),
+            Expr::While { condition, body, label } => {
+                self.lower_loop(current, *label, |this, begin| {
+                    let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
+                        return Ok(());
+                    };
+                    let end = this.current_loop_end()?;
+                    let after_cond = this.new_basic_block();
+                    this.set_terminator(
+                        to_switch,
+                        Terminator::SwitchInt {
+                            discr,
+                            targets: SwitchTargets::static_if(1, after_cond, end),
+                        },
+                    );
+                    if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
+                        this.set_goto(block, begin);
+                    }
+                    Ok(())
+                })
+            }
+            &Expr::For { iterable, pat, body, label } => {
+                let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
+                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
+                let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
+                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
+                let option_some = self.resolve_lang_item(LangItem::OptionSome)?
+                    .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
+                let option = option_some.parent;
+                let into_iter_fn_op = Operand::const_zst(
+                    TyKind::FnDef(
+                        self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
+                        Substitution::from1(Interner, self.expr_ty(iterable))
+                    ).intern(Interner));
+                let iter_next_fn_op = Operand::const_zst(
+                    TyKind::FnDef(
+                        self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
+                        Substitution::from1(Interner, self.expr_ty(iterable))
+                    ).intern(Interner));
+                let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
+                    return Err(MirLowerError::TypeError("unknown for loop iterator type"));
+                };
+                let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
+                let item_ty = &self.infer.type_of_pat[pat];
+                let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
+                let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
+                let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
+                let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
+                let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
+                else {
+                    return Ok(None);
+                };
+                self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
+                self.lower_loop(current, label, |this, begin| {
+                    let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
+                    else {
+                        return Ok(());
+                    };
+                    let end = this.current_loop_end()?;
+                    let (current, _) = this.pattern_matching_variant(
+                        option_item_ty.clone(),
+                        BindingAnnotation::Unannotated,
+                        option_item_place.into(),
+                        option_some.into(),
+                        current,
+                        pat.into(),
+                        Some(end),
+                        &[pat], &None)?;
+                    if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
+                        this.set_goto(block, begin);
+                    }
+                    Ok(())
+                })
+            },
+            Expr::Call { callee, args, .. } => {
+                let callee_ty = self.expr_ty_after_adjustments(*callee);
+                match &callee_ty.data(Interner).kind {
+                    chalk_ir::TyKind::FnDef(..) => {
+                        let func = Operand::from_bytes(vec![], callee_ty.clone());
+                        self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
+                    }
+                    TyKind::Scalar(_)
+                    | TyKind::Tuple(_, _)
+                    | TyKind::Array(_, _)
+                    | TyKind::Adt(_, _)
+                    | TyKind::Str
+                    | TyKind::Foreign(_)
+                    | TyKind::Slice(_) => {
+                        return Err(MirLowerError::TypeError("function call on data type"))
+                    }
+                    TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
+                    TyKind::AssociatedType(_, _)
+                    | TyKind::Raw(_, _)
+                    | TyKind::Ref(_, _, _)
+                    | TyKind::OpaqueType(_, _)
+                    | TyKind::Never
+                    | TyKind::Closure(_, _)
+                    | TyKind::Generator(_, _)
+                    | TyKind::GeneratorWitness(_, _)
+                    | TyKind::Placeholder(_)
+                    | TyKind::Dyn(_)
+                    | TyKind::Alias(_)
+                    | TyKind::Function(_)
+                    | TyKind::BoundVar(_)
+                    | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+                }
+            }
+            Expr::MethodCall { receiver, args, .. } => {
+                let (func_id, generic_args) =
+                    self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
+                let ty = chalk_ir::TyKind::FnDef(
+                    CallableDefId::FunctionId(func_id).to_chalk(self.db),
+                    generic_args,
+                )
+                .intern(Interner);
+                let func = Operand::from_bytes(vec![], ty);
+                self.lower_call_and_args(
+                    func,
+                    iter::once(*receiver).chain(args.iter().copied()),
+                    place,
+                    current,
+                    self.is_uninhabited(expr_id),
+                )
+            }
+            Expr::Match { expr, arms } => {
+                let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)?
+                else {
+                    return Ok(None);
+                };
+                let cond_ty = self.expr_ty_after_adjustments(*expr);
+                let mut end = None;
+                for MatchArm { pat, guard, expr } in arms.iter() {
+                    if guard.is_some() {
+                        not_supported!("pattern matching with guard");
+                    }
+                    let (then, otherwise) = self.pattern_match(
+                        current,
+                        None,
+                        cond_place.clone(),
+                        cond_ty.clone(),
+                        *pat,
+                        BindingAnnotation::Unannotated,
+                    )?;
+                    if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
+                        let r = end.get_or_insert_with(|| self.new_basic_block());
+                        self.set_goto(block, *r);
+                    }
+                    match otherwise {
+                        Some(o) => current = o,
+                        None => {
+                            // The current pattern was irrefutable, so there is no need to generate code
+                            // for the rest of patterns
+                            break;
+                        }
+                    }
+                }
+                if self.is_unterminated(current) {
+                    self.set_terminator(current, Terminator::Unreachable);
+                }
+                Ok(end)
+            }
+            Expr::Continue { label } => match label {
+                Some(_) => not_supported!("continue with label"),
+                None => {
+                    let loop_data =
+                        self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
+                    self.set_goto(current, loop_data.begin);
+                    Ok(None)
+                }
+            },
+            Expr::Break { expr, label } => {
+                if expr.is_some() {
+                    not_supported!("break with value");
+                }
+                match label {
+                    Some(_) => not_supported!("break with label"),
+                    None => {
+                        let end =
+                            self.current_loop_end()?;
+                        self.set_goto(current, end);
+                        Ok(None)
+                    }
+                }
+            }
+            Expr::Return { expr } => {
+                if let Some(expr) = expr {
+                    if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? {
+                        current = c;
+                    } else {
+                        return Ok(None);
+                    }
+                }
+                self.set_terminator(current, Terminator::Return);
+                Ok(None)
+            }
+            Expr::Yield { .. } => not_supported!("yield"),
+            Expr::RecordLit { fields, path, .. } => {
+                let variant_id = self
+                    .infer
+                    .variant_resolution_for_expr(expr_id)
+                    .ok_or_else(|| match path {
+                        Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
+                        None => MirLowerError::RecordLiteralWithoutPath,
+                    })?;
+                let subst = match self.expr_ty(expr_id).kind(Interner) {
+                    TyKind::Adt(_, s) => s.clone(),
+                    _ => not_supported!("Non ADT record literal"),
+                };
+                let variant_data = variant_id.variant_data(self.db.upcast());
+                match variant_id {
+                    VariantId::EnumVariantId(_) | VariantId::StructId(_) => {
+                        let mut operands = vec![None; variant_data.fields().len()];
+                        for RecordLitField { name, expr } in fields.iter() {
+                            let field_id =
+                                variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
+                            let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                                return Ok(None);
+                            };
+                            current = c;
+                            operands[u32::from(field_id.into_raw()) as usize] = Some(op);
+                        }
+                        self.push_assignment(
+                            current,
+                            place,
+                            Rvalue::Aggregate(
+                                AggregateKind::Adt(variant_id, subst),
+                                operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
+                                    MirLowerError::TypeError("missing field in record literal"),
+                                )?,
+                            ),
+                            expr_id.into(),
+                        );
+                        Ok(Some(current))
+                    }
+                    VariantId::UnionId(union_id) => {
+                        let [RecordLitField { name, expr }] = fields.as_ref() else {
+                            not_supported!("Union record literal with more than one field");
+                        };
+                        let local_id =
+                            variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
+                        let mut place = place;
+                        place
+                            .projection
+                            .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
+                        self.lower_expr_to_place(*expr, place, current)
+                    }
+                }
+            }
+            Expr::Await { .. } => not_supported!("await"),
+            Expr::Try { .. } => not_supported!("? operator"),
+            Expr::Yeet { .. } => not_supported!("yeet"),
+            Expr::TryBlock { .. } => not_supported!("try block"),
+            Expr::Async { .. } => not_supported!("async block"),
+            Expr::Const { .. } => not_supported!("anonymous const block"),
+            Expr::Cast { expr, type_ref: _ } => {
+                let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                    return Ok(None);
+                };
+                let source_ty = self.infer[*expr].clone();
+                let target_ty = self.infer[expr_id].clone();
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            }
+            Expr::Ref { expr, rawness: _, mutability } => {
+                let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                let bk = BorrowKind::from_hir(*mutability);
+                self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Box { .. } => not_supported!("box expression"),
+            Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+                let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
+                    return Ok(None);
+                };
+                self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+                let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+                    return Ok(None);
+                };
+                let operation = match op {
+                    hir_def::expr::UnaryOp::Not => UnOp::Not,
+                    hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+                    _ => unreachable!(),
+                };
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::UnaryOp(operation, operand),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            },
+            Expr::BinaryOp { lhs, rhs, op } => {
+                let op = op.ok_or(MirLowerError::IncompleteExpr)?;
+                if let hir_def::expr::BinaryOp::Assignment { op } = op {
+                    if op.is_some() {
+                        not_supported!("assignment with arith op (like +=)");
+                    }
+                    let Some((lhs_place, current)) =
+                        self.lower_expr_as_place(current, *lhs, false)?
+                    else {
+                        return Ok(None);
+                    };
+                    let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+                        return Ok(None);
+                    };
+                    self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
+                    return Ok(Some(current));
+                }
+                let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
+                    return Ok(None);
+                };
+                let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+                    return Ok(None);
+                };
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::CheckedBinaryOp(
+                        match op {
+                            hir_def::expr::BinaryOp::LogicOp(op) => match op {
+                                hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+                                hir_def::expr::LogicOp::Or => BinOp::BitOr,
+                            },
+                            hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
+                            hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
+                            hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+                        },
+                        lhs_op,
+                        rhs_op,
+                    ),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            }
+            Expr::Range { .. } => not_supported!("range"),
+            Expr::Closure { .. } => not_supported!("closure"),
+            Expr::Tuple { exprs, is_assignee_expr: _ } => {
+                let Some(values) = exprs
+                        .iter()
+                        .map(|x| {
+                            let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
+                                return Ok(None);
+                            };
+                            current = c;
+                            Ok(Some(o))
+                        })
+                        .collect::<Result<Option<_>>>()?
+                else {
+                    return Ok(None);
+                };
+                let r = Rvalue::Aggregate(
+                    AggregateKind::Tuple(self.expr_ty(expr_id)),
+                    values,
+                );
+                self.push_assignment(current, place, r, expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Array(l) => match l {
+                Array::ElementList { elements, .. } => {
+                    let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
+                        TyKind::Array(ty, _) => ty.clone(),
+                        _ => {
+                            return Err(MirLowerError::TypeError(
+                                "Array expression with non array type",
+                            ))
+                        }
+                    };
+                    let Some(values) = elements
+                            .iter()
+                            .map(|x| {
+                                let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
+                                    return Ok(None);
+                                };
+                                current = c;
+                                Ok(Some(o))
+                            })
+                            .collect::<Result<Option<_>>>()?
+                    else {
+                        return Ok(None);
+                    };
+                    let r = Rvalue::Aggregate(
+                        AggregateKind::Array(elem_ty),
+                        values,
+                    );
+                    self.push_assignment(current, place, r, expr_id.into());
+                    Ok(Some(current))
+                }
+                Array::Repeat { .. } => not_supported!("array repeat"),
+            },
+            Expr::Literal(l) => {
+                let ty = self.expr_ty(expr_id);
+                let op = self.lower_literal_to_operand(ty, l)?;
+                self.push_assignment(current, place, op.into(), expr_id.into());
+                Ok(Some(current))
+            }
+            Expr::Underscore => not_supported!("underscore"),
+        }
+    }
+
+    fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
+        if let Expr::Field { expr, name } = &self.body[expr_id] {
+            if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
+                let index = name
+                    .as_tuple_index()
+                    .ok_or(MirLowerError::TypeError("named field on tuple"))?;
+                place.projection.push(ProjectionElem::TupleField(index))
+            } else {
+                let field =
+                    self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
+                place.projection.push(ProjectionElem::Field(field));
+            }
+        } else {
+            not_supported!("")
+        }
+        Ok(())
+    }
+
+    fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
+        let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+            .size
+            .bytes_usize();
+        let bytes = match l {
+            hir_def::expr::Literal::String(b) => {
+                let b = b.as_bytes();
+                let mut data = vec![];
+                data.extend(0usize.to_le_bytes());
+                data.extend(b.len().to_le_bytes());
+                let mut mm = MemoryMap::default();
+                mm.insert(0, b.to_vec());
+                return Ok(Operand::from_concrete_const(data, mm, ty));
+            }
+            hir_def::expr::Literal::ByteString(b) => {
+                let mut data = vec![];
+                data.extend(0usize.to_le_bytes());
+                data.extend(b.len().to_le_bytes());
+                let mut mm = MemoryMap::default();
+                mm.insert(0, b.to_vec());
+                return Ok(Operand::from_concrete_const(data, mm, ty));
+            }
+            hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+            hir_def::expr::Literal::Bool(b) => vec![*b as u8],
+            hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::expr::Literal::Float(f, _) => match size {
+                8 => f.into_f64().to_le_bytes().into(),
+                4 => f.into_f32().to_le_bytes().into(),
+                _ => {
+                    return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
+                }
+            },
+        };
+        Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty))
+    }
+
+    fn new_basic_block(&mut self) -> BasicBlockId {
+        self.result.basic_blocks.alloc(BasicBlock::default())
+    }
+
+    fn lower_const(
+        &mut self,
+        const_id: hir_def::ConstId,
+        prev_block: BasicBlockId,
+        place: Place,
+        span: MirSpan,
+    ) -> Result<()> {
+        let c = self.db.const_eval(const_id)?;
+        self.write_const_to_place(c, prev_block, place, span)
+    }
+
+    fn write_const_to_place(
+        &mut self,
+        c: Const,
+        prev_block: BasicBlockId,
+        place: Place,
+        span: MirSpan,
+    ) -> Result<()> {
+        self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
+        Ok(())
+    }
+
+    fn write_bytes_to_place(
+        &mut self,
+        prev_block: BasicBlockId,
+        place: Place,
+        cv: Vec<u8>,
+        ty: Ty,
+        span: MirSpan,
+    ) -> Result<()> {
+        self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span);
+        Ok(())
+    }
+
+    fn lower_enum_variant(
+        &mut self,
+        variant_id: EnumVariantId,
+        prev_block: BasicBlockId,
+        place: Place,
+        ty: Ty,
+        fields: Vec<Operand>,
+        span: MirSpan,
+    ) -> Result<BasicBlockId> {
+        let subst = match ty.kind(Interner) {
+            TyKind::Adt(_, subst) => subst.clone(),
+            _ => not_supported!("Non ADT enum"),
+        };
+        self.push_assignment(
+            prev_block,
+            place,
+            Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields),
+            span,
+        );
+        Ok(prev_block)
+    }
+
+    fn lower_call_and_args(
+        &mut self,
+        func: Operand,
+        args: impl Iterator<Item = ExprId>,
+        place: Place,
+        mut current: BasicBlockId,
+        is_uninhabited: bool,
+    ) -> Result<Option<BasicBlockId>> {
+        let Some(args) = args
+            .map(|arg| {
+                if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {
+                    current = c;
+                    Ok(Some(temp))
+                } else {
+                    Ok(None)
+                }
+            })
+            .collect::<Result<Option<Vec<_>>>>()?
+        else {
+            return Ok(None);
+        };
+        self.lower_call(func, args, place, current, is_uninhabited)
+    }
+
+    fn lower_call(
+        &mut self,
+        func: Operand,
+        args: Vec<Operand>,
+        place: Place,
+        current: BasicBlockId,
+        is_uninhabited: bool,
+    ) -> Result<Option<BasicBlockId>> {
+        let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
+        self.set_terminator(
+            current,
+            Terminator::Call {
+                func,
+                args,
+                destination: place,
+                target: b,
+                cleanup: None,
+                from_hir_call: true,
+            },
+        );
+        Ok(b)
+    }
+
+    fn is_unterminated(&mut self, source: BasicBlockId) -> bool {
+        self.result.basic_blocks[source].terminator.is_none()
+    }
+
+    fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
+        self.result.basic_blocks[source].terminator = Some(terminator);
+    }
+
+    fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
+        self.set_terminator(source, Terminator::Goto { target });
+    }
+
+    fn expr_ty(&self, e: ExprId) -> Ty {
+        self.infer[e].clone()
+    }
+
+    fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
+        let mut ty = None;
+        if let Some(x) = self.infer.expr_adjustments.get(&e) {
+            if let Some(x) = x.last() {
+                ty = Some(x.target.clone());
+            }
+        }
+        ty.unwrap_or_else(|| self.expr_ty(e))
+    }
+
+    fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
+        self.result.basic_blocks[block].statements.push(statement);
+    }
+
+    fn push_assignment(
+        &mut self,
+        block: BasicBlockId,
+        place: Place,
+        rvalue: Rvalue,
+        span: MirSpan,
+    ) {
+        self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
+    }
+
+    /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
+    /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
+    /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
+    /// mismatched path block is `None`.
+    ///
+    /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
+    /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
+    /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
+    /// so it should be an empty block.
+    fn pattern_match(
+        &mut self,
+        mut current: BasicBlockId,
+        mut current_else: Option<BasicBlockId>,
+        mut cond_place: Place,
+        mut cond_ty: Ty,
+        pattern: PatId,
+        mut binding_mode: BindingAnnotation,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        Ok(match &self.body.pats[pattern] {
+            Pat::Missing => return Err(MirLowerError::IncompleteExpr),
+            Pat::Wild => (current, current_else),
+            Pat::Tuple { args, ellipsis } => {
+                pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
+                let subst = match cond_ty.kind(Interner) {
+                    TyKind::Tuple(_, s) => s,
+                    _ => {
+                        return Err(MirLowerError::TypeError(
+                            "non tuple type matched with tuple pattern",
+                        ))
+                    }
+                };
+                self.pattern_match_tuple_like(
+                    current,
+                    current_else,
+                    args.iter().enumerate().map(|(i, x)| {
+                        (
+                            PlaceElem::TupleField(i),
+                            *x,
+                            subst.at(Interner, i).assert_ty_ref(Interner).clone(),
+                        )
+                    }),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            Pat::Or(pats) => {
+                let then_target = self.new_basic_block();
+                let mut finished = false;
+                for pat in &**pats {
+                    let (next, next_else) = self.pattern_match(
+                        current,
+                        None,
+                        cond_place.clone(),
+                        cond_ty.clone(),
+                        *pat,
+                        binding_mode,
+                    )?;
+                    self.set_goto(next, then_target);
+                    match next_else {
+                        Some(t) => {
+                            current = t;
+                        }
+                        None => {
+                            finished = true;
+                            break;
+                        }
+                    }
+                }
+                if !finished {
+                    let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
+                    self.set_goto(current, ce);
+                }
+                (then_target, current_else)
+            }
+            Pat::Record { .. } => not_supported!("record pattern"),
+            Pat::Range { .. } => not_supported!("range pattern"),
+            Pat::Slice { .. } => not_supported!("slice pattern"),
+            Pat::Path(_) => {
+                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+                    not_supported!("unresolved variant");
+                };
+                self.pattern_matching_variant(
+                    cond_ty,
+                    binding_mode,
+                    cond_place,
+                    variant,
+                    current,
+                    pattern.into(),
+                    current_else,
+                    &[],
+                    &None,
+                )?
+            }
+            Pat::Lit(l) => {
+                let then_target = self.new_basic_block();
+                let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+                match &self.body.exprs[*l] {
+                    Expr::Literal(l) => match l {
+                        hir_def::expr::Literal::Int(x, _) => {
+                            self.set_terminator(
+                                current,
+                                Terminator::SwitchInt {
+                                    discr: Operand::Copy(cond_place),
+                                    targets: SwitchTargets::static_if(
+                                        *x as u128,
+                                        then_target,
+                                        else_target,
+                                    ),
+                                },
+                            );
+                        }
+                        hir_def::expr::Literal::Uint(x, _) => {
+                            self.set_terminator(
+                                current,
+                                Terminator::SwitchInt {
+                                    discr: Operand::Copy(cond_place),
+                                    targets: SwitchTargets::static_if(*x, then_target, else_target),
+                                },
+                            );
+                        }
+                        _ => not_supported!("non int path literal"),
+                    },
+                    _ => not_supported!("expression path literal"),
+                }
+                (then_target, Some(else_target))
+            }
+            Pat::Bind { id, subpat } => {
+                let target_place = self.result.binding_locals[*id];
+                let mode = self.body.bindings[*id].mode;
+                if let Some(subpat) = subpat {
+                    (current, current_else) = self.pattern_match(
+                        current,
+                        current_else,
+                        cond_place.clone(),
+                        cond_ty,
+                        *subpat,
+                        binding_mode,
+                    )?
+                }
+                if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
+                    binding_mode = mode;
+                }
+                self.push_storage_live(*id, current)?;
+                self.push_assignment(
+                    current,
+                    target_place.into(),
+                    match binding_mode {
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
+                            Operand::Copy(cond_place).into()
+                        }
+                        BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
+                        BindingAnnotation::RefMut => Rvalue::Ref(
+                            BorrowKind::Mut { allow_two_phase_borrow: false },
+                            cond_place,
+                        ),
+                    },
+                    pattern.into(),
+                );
+                (current, current_else)
+            }
+            Pat::TupleStruct { path: _, args, ellipsis } => {
+                let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+                    not_supported!("unresolved variant");
+                };
+                self.pattern_matching_variant(
+                    cond_ty,
+                    binding_mode,
+                    cond_place,
+                    variant,
+                    current,
+                    pattern.into(),
+                    current_else,
+                    args,
+                    ellipsis,
+                )?
+            }
+            Pat::Ref { .. } => not_supported!("& pattern"),
+            Pat::Box { .. } => not_supported!("box pattern"),
+            Pat::ConstBlock(_) => not_supported!("const block pattern"),
+        })
+    }
+
+    fn pattern_matching_variant(
+        &mut self,
+        mut cond_ty: Ty,
+        mut binding_mode: BindingAnnotation,
+        mut cond_place: Place,
+        variant: VariantId,
+        current: BasicBlockId,
+        span: MirSpan,
+        current_else: Option<BasicBlockId>,
+        args: &[PatId],
+        ellipsis: &Option<usize>,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
+        let subst = match cond_ty.kind(Interner) {
+            TyKind::Adt(_, s) => s,
+            _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
+        };
+        let fields_type = self.db.field_types(variant);
+        Ok(match variant {
+            VariantId::EnumVariantId(v) => {
+                let e = self.db.const_eval_discriminant(v)? as u128;
+                let next = self.new_basic_block();
+                let tmp = self.discr_temp_place();
+                self.push_assignment(
+                    current,
+                    tmp.clone(),
+                    Rvalue::Discriminant(cond_place.clone()),
+                    span,
+                );
+                let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+                self.set_terminator(
+                    current,
+                    Terminator::SwitchInt {
+                        discr: Operand::Copy(tmp),
+                        targets: SwitchTargets::static_if(e, next, else_target),
+                    },
+                );
+                let enum_data = self.db.enum_data(v.parent);
+                let fields =
+                    enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
+                        (
+                            PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
+                            fields_type[x].clone().substitute(Interner, subst),
+                        )
+                    });
+                self.pattern_match_tuple_like(
+                    next,
+                    Some(else_target),
+                    args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            VariantId::StructId(s) => {
+                let struct_data = self.db.struct_data(s);
+                let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
+                    (
+                        PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
+                        fields_type[x].clone().substitute(Interner, subst),
+                    )
+                });
+                self.pattern_match_tuple_like(
+                    current,
+                    current_else,
+                    args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
+                    *ellipsis,
+                    &cond_place,
+                    binding_mode,
+                )?
+            }
+            VariantId::UnionId(_) => {
+                return Err(MirLowerError::TypeError("pattern matching on union"))
+            }
+        })
+    }
+
+    fn pattern_match_tuple_like(
+        &mut self,
+        mut current: BasicBlockId,
+        mut current_else: Option<BasicBlockId>,
+        args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
+        ellipsis: Option<usize>,
+        cond_place: &Place,
+        binding_mode: BindingAnnotation,
+    ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+        if ellipsis.is_some() {
+            not_supported!("tuple like pattern with ellipsis");
+        }
+        for (proj, arg, ty) in args {
+            let mut cond_place = cond_place.clone();
+            cond_place.projection.push(proj);
+            (current, current_else) =
+                self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
+        }
+        Ok((current, current_else))
+    }
+
+    fn discr_temp_place(&mut self) -> Place {
+        match &self.discr_temp {
+            Some(x) => x.clone(),
+            None => {
+                let tmp: Place =
+                    self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+                self.discr_temp = Some(tmp.clone());
+                tmp
+            }
+        }
+    }
+
+    fn lower_loop(
+        &mut self,
+        prev_block: BasicBlockId,
+        label: Option<LabelId>,
+        f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
+    ) -> Result<Option<BasicBlockId>> {
+        if label.is_some() {
+            not_supported!("loop with label");
+        }
+        let begin = self.new_basic_block();
+        let prev =
+            mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
+        self.set_goto(prev_block, begin);
+        f(self, begin)?;
+        let my = mem::replace(&mut self.current_loop_blocks, prev)
+            .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+        Ok(my.end)
+    }
+
+    fn has_adjustments(&self, expr_id: ExprId) -> bool {
+        !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true)
+    }
+
+    fn merge_blocks(
+        &mut self,
+        b1: Option<BasicBlockId>,
+        b2: Option<BasicBlockId>,
+    ) -> Option<BasicBlockId> {
+        match (b1, b2) {
+            (None, None) => None,
+            (None, Some(b)) | (Some(b), None) => Some(b),
+            (Some(b1), Some(b2)) => {
+                let bm = self.new_basic_block();
+                self.set_goto(b1, bm);
+                self.set_goto(b2, bm);
+                Some(bm)
+            }
+        }
+    }
+
+    fn current_loop_end(&mut self) -> Result<BasicBlockId> {
+        let r = match self
+            .current_loop_blocks
+            .as_mut()
+            .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+            .end
+        {
+            Some(x) => x,
+            None => {
+                let s = self.new_basic_block();
+                self.current_loop_blocks
+                    .as_mut()
+                    .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+                    .end = Some(s);
+                s
+            }
+        };
+        Ok(r)
+    }
+
+    fn is_uninhabited(&self, expr_id: ExprId) -> bool {
+        is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
+    }
+
+    /// This function push `StorageLive` statements for each binding in the pattern.
+    fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
+        // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
+        // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
+        // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
+        // allow this:
+        //
+        // ```
+        // let x;
+        // loop {
+        //     let y = 2;
+        //     x = &y;
+        //     if some_condition {
+        //         break; // we need to add a StorageDead(y) above this to kill the x borrow
+        //     }
+        // }
+        // use(x)
+        // ```
+        // But I think this approach work for mutability analysis, as user can't write code which mutates a binding
+        // after StorageDead, except loops, which are handled by this hack.
+        let span = self.body.bindings[b]
+            .definitions
+            .first()
+            .copied()
+            .map(MirSpan::PatId)
+            .unwrap_or(MirSpan::Unknown);
+        let l = self.result.binding_locals[b];
+        self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
+        self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+        Ok(())
+    }
+
+    fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
+        let crate_id = self.owner.module(self.db.upcast()).krate();
+        self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
+    }
+
+    fn lower_block_to_place(
+        &mut self,
+        label: Option<LabelId>,
+        statements: &[hir_def::expr::Statement],
+        mut current: BasicBlockId,
+        tail: Option<ExprId>,
+        place: Place,
+    ) -> Result<Option<Idx<BasicBlock>>> {
+        if label.is_some() {
+            not_supported!("block with label");
+        }
+        for statement in statements.iter() {
+            match statement {
+                hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+                    if let Some(expr_id) = initializer {
+                        let else_block;
+                        let Some((init_place, c)) =
+                        self.lower_expr_as_place(current, *expr_id, true)?
+                    else {
+                        return Ok(None);
+                    };
+                        current = c;
+                        (current, else_block) = self.pattern_match(
+                            current,
+                            None,
+                            init_place,
+                            self.expr_ty_after_adjustments(*expr_id),
+                            *pat,
+                            BindingAnnotation::Unannotated,
+                        )?;
+                        match (else_block, else_branch) {
+                            (None, _) => (),
+                            (Some(else_block), None) => {
+                                self.set_terminator(else_block, Terminator::Unreachable);
+                            }
+                            (Some(else_block), Some(else_branch)) => {
+                                if let Some((_, b)) =
+                                    self.lower_expr_as_place(else_block, *else_branch, true)?
+                                {
+                                    self.set_terminator(b, Terminator::Unreachable);
+                                }
+                            }
+                        }
+                    }
+                }
+                hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+                    let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
+                        return Ok(None);
+                    };
+                    current = c;
+                }
+            }
+        }
+        match tail {
+            Some(tail) => self.lower_expr_to_place(tail, place, current),
+            None => Ok(Some(current)),
+        }
+    }
+}
+
+fn pattern_matching_dereference(
+    cond_ty: &mut Ty,
+    binding_mode: &mut BindingAnnotation,
+    cond_place: &mut Place,
+) {
+    while let Some((ty, _, mu)) = cond_ty.as_reference() {
+        if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
+            *binding_mode = BindingAnnotation::RefMut;
+        } else {
+            *binding_mode = BindingAnnotation::Ref;
+        }
+        *cond_ty = ty.clone();
+        cond_place.projection.push(ProjectionElem::Deref);
+    }
+}
+
+fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
+    Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
+        (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
+            (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat,
+            (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt,
+            (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
+            (_, _) => CastKind::IntToInt,
+        },
+        // Enum to int casts
+        (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
+            CastKind::IntToInt
+        }
+        (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"),
+    })
+}
+
+pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
+    let _p = profile::span("mir_body_query").detail(|| match def {
+        DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
+        DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
+        DefWithBodyId::ConstId(it) => {
+            db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
+        }
+        DefWithBodyId::VariantId(it) => {
+            db.enum_data(it.parent).variants[it.local_id].name.to_string()
+        }
+    });
+    let body = db.body(def);
+    let infer = db.infer(def);
+    let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+    Ok(Arc::new(result))
+}
+
+pub fn mir_body_recover(
+    _db: &dyn HirDatabase,
+    _cycle: &[String],
+    _def: &DefWithBodyId,
+) -> Result<Arc<MirBody>> {
+    Err(MirLowerError::Loop)
+}
+
+pub fn lower_to_mir(
+    db: &dyn HirDatabase,
+    owner: DefWithBodyId,
+    body: &Body,
+    infer: &InferenceResult,
+    // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we
+    // need to take this input explicitly.
+    root_expr: ExprId,
+) -> Result<MirBody> {
+    if let Some((_, x)) = infer.type_mismatches().next() {
+        return Err(MirLowerError::TypeMismatch(x.clone()));
+    }
+    let mut basic_blocks = Arena::new();
+    let start_block =
+        basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
+    let mut locals = Arena::new();
+    // 0 is return local
+    locals.alloc(Local { ty: infer[root_expr].clone() });
+    let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+    // 1 to param_len is for params
+    let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
+        let substs = TyBuilder::placeholder_subst(db, fid);
+        let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
+        body.params
+            .iter()
+            .zip(callable_sig.params().iter())
+            .map(|(&x, ty)| {
+                let local_id = locals.alloc(Local { ty: ty.clone() });
+                if let Pat::Bind { id, subpat: None } = body[x] {
+                    if matches!(
+                        body.bindings[id].mode,
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+                    ) {
+                        binding_locals.insert(id, local_id);
+                    }
+                }
+                local_id
+            })
+            .collect()
+    } else {
+        if !body.params.is_empty() {
+            return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
+        }
+        vec![]
+    };
+    // and then rest of bindings
+    for (id, _) in body.bindings.iter() {
+        if !binding_locals.contains_idx(id) {
+            binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
+        }
+    }
+    let mir = MirBody {
+        basic_blocks,
+        locals,
+        start_block,
+        binding_locals,
+        param_locals,
+        owner,
+        arg_count: body.params.len(),
+    };
+    let mut ctx = MirLowerCtx {
+        result: mir,
+        db,
+        infer,
+        body,
+        owner,
+        current_loop_blocks: None,
+        discr_temp: None,
+    };
+    let mut current = start_block;
+    for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
+        if let Pat::Bind { id, .. } = body[param] {
+            if local == ctx.result.binding_locals[id] {
+                continue;
+            }
+        }
+        let r = ctx.pattern_match(
+            current,
+            None,
+            local.into(),
+            ctx.result.locals[local].ty.clone(),
+            param,
+            BindingAnnotation::Unannotated,
+        )?;
+        if let Some(b) = r.1 {
+            ctx.set_terminator(b, Terminator::Unreachable);
+        }
+        current = r.0;
+    }
+    if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+        ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+    }
+    Ok(ctx.result)
+}
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
new file mode 100644
index 00000000000..fe8147dcd3e
--- /dev/null
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -0,0 +1,237 @@
+//! MIR lowering for places
+
+use super::*;
+use hir_expand::name;
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirLowerError::NotSupported(format!($x)))
+    };
+}
+
+impl MirLowerCtx<'_> {
+    fn lower_expr_to_some_place_without_adjust(
+        &mut self,
+        expr_id: ExprId,
+        prev_block: BasicBlockId,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let ty = self.expr_ty(expr_id);
+        let place = self.temp(ty)?;
+        let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
+            return Ok(None);
+        };
+        Ok(Some((place.into(), current)))
+    }
+
+    fn lower_expr_to_some_place_with_adjust(
+        &mut self,
+        expr_id: ExprId,
+        prev_block: BasicBlockId,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let ty =
+            adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
+        let place = self.temp(ty)?;
+        let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
+            return Ok(None);
+        };
+        Ok(Some((place.into(), current)))
+    }
+
+    pub(super) fn lower_expr_as_place_with_adjust(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+        adjustments: &[Adjustment],
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+            if !upgrade_rvalue {
+                return Err(MirLowerError::MutatingRvalue);
+            }
+            this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
+        };
+        if let Some((last, rest)) = adjustments.split_last() {
+            match last.kind {
+                Adjust::Deref(None) => {
+                    let Some(mut x) = self.lower_expr_as_place_with_adjust(
+                        current,
+                        expr_id,
+                        upgrade_rvalue,
+                        rest,
+                    )? else {
+                        return Ok(None);
+                    };
+                    x.0.projection.push(ProjectionElem::Deref);
+                    Ok(Some(x))
+                }
+                Adjust::Deref(Some(od)) => {
+                    let Some((r, current)) = self.lower_expr_as_place_with_adjust(
+                        current,
+                        expr_id,
+                        upgrade_rvalue,
+                        rest,
+                    )? else {
+                        return Ok(None);
+                    };
+                    self.lower_overloaded_deref(
+                        current,
+                        r,
+                        rest.last()
+                            .map(|x| x.target.clone())
+                            .unwrap_or_else(|| self.expr_ty(expr_id)),
+                        last.target.clone(),
+                        expr_id.into(),
+                        match od.0 {
+                            Some(Mutability::Mut) => true,
+                            Some(Mutability::Not) => false,
+                            None => {
+                                not_supported!("implicit overloaded deref with unknown mutability")
+                            }
+                        },
+                    )
+                }
+                Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
+            }
+        } else {
+            self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
+        }
+    }
+
+    pub(super) fn lower_expr_as_place(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        match self.infer.expr_adjustments.get(&expr_id) {
+            Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
+            None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
+        }
+    }
+
+    pub(super) fn lower_expr_as_place_without_adjust(
+        &mut self,
+        current: BasicBlockId,
+        expr_id: ExprId,
+        upgrade_rvalue: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let try_rvalue = |this: &mut MirLowerCtx<'_>| {
+            if !upgrade_rvalue {
+                return Err(MirLowerError::MutatingRvalue);
+            }
+            this.lower_expr_to_some_place_without_adjust(expr_id, current)
+        };
+        match &self.body.exprs[expr_id] {
+            Expr::Path(p) => {
+                let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+                let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
+                    return Err(MirLowerError::unresolved_path(self.db, p));
+                };
+                let pr = match pr {
+                    ResolveValueResult::ValueNs(v) => v,
+                    ResolveValueResult::Partial(..) => return try_rvalue(self),
+                };
+                match pr {
+                    ValueNs::LocalBinding(pat_id) => {
+                        Ok(Some((self.result.binding_locals[pat_id].into(), current)))
+                    }
+                    _ => try_rvalue(self),
+                }
+            }
+            Expr::UnaryOp { expr, op } => match op {
+                hir_def::expr::UnaryOp::Deref => {
+                    if !matches!(
+                        self.expr_ty(*expr).kind(Interner),
+                        TyKind::Ref(..) | TyKind::Raw(..)
+                    ) {
+                        let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+                            return Ok(None);
+                        };
+                        not_supported!("explicit overloaded deref");
+                    }
+                    let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                        return Ok(None);
+                    };
+                    r.projection.push(ProjectionElem::Deref);
+                    Ok(Some((r, current)))
+                }
+                _ => try_rvalue(self),
+            },
+            Expr::Field { expr, .. } => {
+                let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
+                    return Ok(None);
+                };
+                self.push_field_projection(&mut r, expr_id)?;
+                Ok(Some((r, current)))
+            }
+            Expr::Index { base, index } => {
+                let base_ty = self.expr_ty_after_adjustments(*base);
+                let index_ty = self.expr_ty_after_adjustments(*index);
+                if index_ty != TyBuilder::usize()
+                    || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
+                {
+                    not_supported!("overloaded index");
+                }
+                let Some((mut p_base, current)) =
+                    self.lower_expr_as_place(current, *base, true)? else {
+                    return Ok(None);
+                };
+                let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
+                let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
+                    return Ok(None);
+                };
+                p_base.projection.push(ProjectionElem::Index(l_index));
+                Ok(Some((p_base, current)))
+            }
+            _ => try_rvalue(self),
+        }
+    }
+
+    fn lower_overloaded_deref(
+        &mut self,
+        current: BasicBlockId,
+        place: Place,
+        source_ty: Ty,
+        target_ty: Ty,
+        span: MirSpan,
+        mutability: bool,
+    ) -> Result<Option<(Place, BasicBlockId)>> {
+        let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
+            (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared)
+        } else {
+            (
+                Mutability::Mut,
+                LangItem::DerefMut,
+                name![deref_mut],
+                BorrowKind::Mut { allow_two_phase_borrow: false },
+            )
+        };
+        let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
+        let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
+        let ref_place: Place = self.temp(ty_ref)?.into();
+        self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
+        let deref_trait = self
+            .resolve_lang_item(trait_lang_item)?
+            .as_trait()
+            .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+        let deref_fn = self
+            .db
+            .trait_data(deref_trait)
+            .method_by_name(&trait_method_name)
+            .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
+        let deref_fn_op = Operand::const_zst(
+            TyKind::FnDef(
+                self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
+                Substitution::from1(Interner, source_ty),
+            )
+            .intern(Interner),
+        );
+        let mut result: Place = self.temp(target_ty_ref)?.into();
+        let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
+            return Ok(None);
+        };
+        result.projection.push(ProjectionElem::Deref);
+        Ok(Some((result, current)))
+    }
+}
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
new file mode 100644
index 00000000000..ffc08b7e346
--- /dev/null
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -0,0 +1,348 @@
+//! A pretty-printer for MIR.
+
+use std::fmt::{Display, Write};
+
+use hir_def::{body::Body, expr::BindingId};
+use hir_expand::name::Name;
+use la_arena::ArenaMap;
+
+use crate::{
+    db::HirDatabase,
+    display::HirDisplay,
+    mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
+};
+
+use super::{
+    AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
+};
+
+impl MirBody {
+    pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
+        let hir_body = db.body(self.owner);
+        let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
+        ctx.for_body();
+        ctx.result
+    }
+}
+
+struct MirPrettyCtx<'a> {
+    body: &'a MirBody,
+    hir_body: &'a Body,
+    db: &'a dyn HirDatabase,
+    result: String,
+    ident: String,
+    local_to_binding: ArenaMap<LocalId, BindingId>,
+}
+
+macro_rules! w {
+    ($dst:expr, $($arg:tt)*) => {
+        { let _ = write!($dst, $($arg)*); }
+    };
+}
+
+macro_rules! wln {
+    ($dst:expr) => {
+        { let _ = writeln!($dst); }
+    };
+    ($dst:expr, $($arg:tt)*) => {
+        { let _ = writeln!($dst, $($arg)*); }
+    };
+}
+
+impl Write for MirPrettyCtx<'_> {
+    fn write_str(&mut self, s: &str) -> std::fmt::Result {
+        let mut it = s.split('\n'); // note: `.lines()` is wrong here
+        self.write(it.next().unwrap_or_default());
+        for line in it {
+            self.write_line();
+            self.write(line);
+        }
+        Ok(())
+    }
+}
+
+enum LocalName {
+    Unknown(LocalId),
+    Binding(Name, LocalId),
+}
+
+impl Display for LocalName {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
+            LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())),
+        }
+    }
+}
+
+impl<'a> MirPrettyCtx<'a> {
+    fn for_body(&mut self) {
+        self.with_block(|this| {
+            this.locals();
+            wln!(this);
+            this.blocks();
+        });
+    }
+
+    fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
+        self.ident += "    ";
+        wln!(self, "{{");
+        f(self);
+        for _ in 0..4 {
+            self.result.pop();
+            self.ident.pop();
+        }
+        wln!(self, "}}");
+    }
+
+    fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
+        let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect();
+        MirPrettyCtx {
+            body,
+            db,
+            result: String::new(),
+            ident: String::new(),
+            local_to_binding,
+            hir_body,
+        }
+    }
+
+    fn write_line(&mut self) {
+        self.result.push('\n');
+        self.result += &self.ident;
+    }
+
+    fn write(&mut self, line: &str) {
+        self.result += line;
+    }
+
+    fn locals(&mut self) {
+        for (id, local) in self.body.locals.iter() {
+            wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db));
+        }
+    }
+
+    fn local_name(&self, local: LocalId) -> LocalName {
+        match self.local_to_binding.get(local) {
+            Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local),
+            None => LocalName::Unknown(local),
+        }
+    }
+
+    fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String {
+        format!("'bb{}", u32::from(basic_block_id.into_raw()))
+    }
+
+    fn blocks(&mut self) {
+        for (id, block) in self.body.basic_blocks.iter() {
+            wln!(self);
+            w!(self, "{}: ", self.basic_block_id(id));
+            self.with_block(|this| {
+                for statement in &block.statements {
+                    match &statement.kind {
+                        StatementKind::Assign(l, r) => {
+                            this.place(l);
+                            w!(this, " = ");
+                            this.rvalue(r);
+                            wln!(this, ";");
+                        }
+                        StatementKind::StorageDead(p) => {
+                            wln!(this, "StorageDead({})", this.local_name(*p));
+                        }
+                        StatementKind::StorageLive(p) => {
+                            wln!(this, "StorageLive({})", this.local_name(*p));
+                        }
+                        StatementKind::Deinit(p) => {
+                            w!(this, "Deinit(");
+                            this.place(p);
+                            wln!(this, ");");
+                        }
+                        StatementKind::Nop => wln!(this, "Nop;"),
+                    }
+                }
+                match &block.terminator {
+                    Some(terminator) => match terminator {
+                        Terminator::Goto { target } => {
+                            wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
+                        }
+                        Terminator::SwitchInt { discr, targets } => {
+                            w!(this, "switch ");
+                            this.operand(discr);
+                            w!(this, " ");
+                            this.with_block(|this| {
+                                for (c, b) in targets.iter() {
+                                    wln!(this, "{c} => {},", this.basic_block_id(b));
+                                }
+                                wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
+                            });
+                        }
+                        Terminator::Call { func, args, destination, target, .. } => {
+                            w!(this, "Call ");
+                            this.with_block(|this| {
+                                w!(this, "func: ");
+                                this.operand(func);
+                                wln!(this, ",");
+                                w!(this, "args: [");
+                                this.operand_list(args);
+                                wln!(this, "],");
+                                w!(this, "destination: ");
+                                this.place(destination);
+                                wln!(this, ",");
+                                w!(this, "target: ");
+                                match target {
+                                    Some(t) => w!(this, "{}", this.basic_block_id(*t)),
+                                    None => w!(this, "<unreachable>"),
+                                }
+                                wln!(this, ",");
+                            });
+                        }
+                        _ => wln!(this, "{:?};", terminator),
+                    },
+                    None => wln!(this, "<no-terminator>;"),
+                }
+            })
+        }
+    }
+
+    fn place(&mut self, p: &Place) {
+        fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
+            let Some((last, head)) = projections.split_last() else {
+                // no projection
+                w!(this, "{}", this.local_name(local));
+                return;
+            };
+            match last {
+                ProjectionElem::Deref => {
+                    w!(this, "(*");
+                    f(this, local, head);
+                    w!(this, ")");
+                }
+                ProjectionElem::Field(field) => {
+                    let variant_data = field.parent.variant_data(this.db.upcast());
+                    let name = &variant_data.fields()[field.local_id].name;
+                    match field.parent {
+                        hir_def::VariantId::EnumVariantId(e) => {
+                            w!(this, "(");
+                            f(this, local, head);
+                            let variant_name =
+                                &this.db.enum_data(e.parent).variants[e.local_id].name;
+                            w!(this, " as {}).{}", variant_name, name);
+                        }
+                        hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
+                            f(this, local, head);
+                            w!(this, ".{name}");
+                        }
+                    }
+                }
+                ProjectionElem::TupleField(x) => {
+                    f(this, local, head);
+                    w!(this, ".{}", x);
+                }
+                ProjectionElem::Index(l) => {
+                    f(this, local, head);
+                    w!(this, "[{}]", this.local_name(*l));
+                }
+                x => {
+                    f(this, local, head);
+                    w!(this, ".{:?}", x);
+                }
+            }
+        }
+        f(self, p.local, &p.projection);
+    }
+
+    fn operand(&mut self, r: &Operand) {
+        match r {
+            Operand::Copy(p) | Operand::Move(p) => {
+                // MIR at the time of writing doesn't have difference between move and copy, so we show them
+                // equally. Feel free to change it.
+                self.place(p);
+            }
+            Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)),
+        }
+    }
+
+    fn rvalue(&mut self, r: &Rvalue) {
+        match r {
+            Rvalue::Use(op) => self.operand(op),
+            Rvalue::Ref(r, p) => {
+                match r {
+                    BorrowKind::Shared => w!(self, "&"),
+                    BorrowKind::Shallow => w!(self, "&shallow "),
+                    BorrowKind::Unique => w!(self, "&uniq "),
+                    BorrowKind::Mut { .. } => w!(self, "&mut "),
+                }
+                self.place(p);
+            }
+            Rvalue::Aggregate(AggregateKind::Tuple(_), x) => {
+                w!(self, "(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Aggregate(AggregateKind::Array(_), x) => {
+                w!(self, "[");
+                self.operand_list(x);
+                w!(self, "]");
+            }
+            Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => {
+                w!(self, "Adt(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
+                w!(self, "Union(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
+            Rvalue::Len(p) => {
+                w!(self, "Len(");
+                self.place(p);
+                w!(self, ")");
+            }
+            Rvalue::Cast(ck, op, ty) => {
+                w!(self, "Discriminant({ck:?}");
+                self.operand(op);
+                w!(self, "{})", ty.display(self.db));
+            }
+            Rvalue::CheckedBinaryOp(b, o1, o2) => {
+                self.operand(o1);
+                w!(self, " {b} ");
+                self.operand(o2);
+            }
+            Rvalue::UnaryOp(u, o) => {
+                let u = match u {
+                    UnOp::Not => "!",
+                    UnOp::Neg => "-",
+                };
+                w!(self, "{u} ");
+                self.operand(o);
+            }
+            Rvalue::Discriminant(p) => {
+                w!(self, "Discriminant(");
+                self.place(p);
+                w!(self, ")");
+            }
+            Rvalue::ShallowInitBox(op, _) => {
+                w!(self, "ShallowInitBox(");
+                self.operand(op);
+                w!(self, ")");
+            }
+            Rvalue::CopyForDeref(p) => {
+                w!(self, "CopyForDeref(");
+                self.place(p);
+                w!(self, ")");
+            }
+        }
+    }
+
+    fn operand_list(&mut self, x: &[Operand]) {
+        let mut it = x.iter();
+        if let Some(first) = it.next() {
+            self.operand(first);
+            for op in it {
+                w!(self, ", ");
+                self.operand(op);
+            }
+        }
+    }
+}
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index ba5d9c24126..bcd63d9472a 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -61,22 +61,27 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
     Some(tracing::subscriber::set_default(subscriber))
 }
 
+#[track_caller]
 fn check_types(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, false)
 }
 
+#[track_caller]
 fn check_types_source_code(ra_fixture: &str) {
     check_impl(ra_fixture, false, true, true)
 }
 
+#[track_caller]
 fn check_no_mismatches(ra_fixture: &str) {
     check_impl(ra_fixture, true, false, false)
 }
 
+#[track_caller]
 fn check(ra_fixture: &str) {
     check_impl(ra_fixture, false, false, false)
 }
 
+#[track_caller]
 fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
     let _tracing = setup_tracing();
     let (db, files) = TestDB::with_many_files(ra_fixture);
@@ -158,7 +163,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
                 } else {
                     ty.display_test(&db).to_string()
                 };
-                assert_eq!(actual, expected);
+                assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
         }
 
@@ -174,7 +179,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
                 } else {
                     ty.display_test(&db).to_string()
                 };
-                assert_eq!(actual, expected);
+                assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range);
             }
             if let Some(expected) = adjustments.remove(&range) {
                 let adjustments = inference_result
@@ -191,30 +196,11 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
             }
         }
 
-        for (pat, mismatch) in inference_result.pat_type_mismatches() {
-            let node = match pat_node(&body_source_map, pat, &db) {
-                Some(value) => value,
-                None => continue,
-            };
-            let range = node.as_ref().original_file_range(&db);
-            let actual = format!(
-                "expected {}, got {}",
-                mismatch.expected.display_test(&db),
-                mismatch.actual.display_test(&db)
-            );
-            match mismatches.remove(&range) {
-                Some(annotation) => assert_eq!(actual, annotation),
-                None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual),
-            }
-        }
-        for (expr, mismatch) in inference_result.expr_type_mismatches() {
-            let node = match body_source_map.expr_syntax(expr) {
-                Ok(sp) => {
-                    let root = db.parse_or_expand(sp.file_id).unwrap();
-                    sp.map(|ptr| ptr.to_node(&root).syntax().clone())
-                }
-                Err(SyntheticSyntax) => continue,
-            };
+        for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
+            let Some(node) = (match expr_or_pat {
+                hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
+                hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
+            }) else { continue; };
             let range = node.as_ref().original_file_range(&db);
             let actual = format!(
                 "expected {}, got {}",
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 3e110abaf4b..b524922b6cf 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -258,6 +258,7 @@ fn test() {
 
 #[test]
 fn coerce_autoderef_block() {
+    // FIXME: We should know mutability in overloaded deref
     check_no_mismatches(
         r#"
 //- minicore: deref
@@ -267,7 +268,7 @@ fn takes_ref_str(x: &str) {}
 fn returns_string() -> String { loop {} }
 fn test() {
     takes_ref_str(&{ returns_string() });
-               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
+               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
 }
 "#,
     );
diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs
index f00fa972948..1876be303ad 100644
--- a/crates/hir-ty/src/tests/diagnostics.rs
+++ b/crates/hir-ty/src/tests/diagnostics.rs
@@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str {
 "#,
     );
 }
+
+#[test]
+fn non_unit_block_expr_stmt_no_semi() {
+    check(
+        r#"
+fn test(x: bool) {
+    if x {
+        "notok"
+      //^^^^^^^ expected (), got &str
+    } else {
+        "ok"
+      //^^^^ expected (), got &str
+    }
+    match x { true => true, false => 0 }
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool
+                                   //^ expected bool, got i32
+    ()
+}
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 41c53701df6..e568e7013fa 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1167,7 +1167,6 @@ fn test() {
             123..167 '{     ...o(); }': ()
             133..134 's': &S
             137..151 'unsafe { f() }': &S
-            137..151 'unsafe { f() }': &S
             146..147 'f': fn f() -> &S
             146..149 'f()': &S
             157..158 's': &S
@@ -1253,6 +1252,7 @@ fn foo<T: Trait>(a: &T) {
 
 #[test]
 fn autoderef_visibility_field() {
+    // FIXME: We should know mutability in overloaded deref
     check(
         r#"
 //- minicore: deref
@@ -1274,7 +1274,7 @@ mod a {
 mod b {
     fn foo() {
         let x = super::a::Bar::new().0;
-             // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not)))
+             // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
              // ^^^^^^^^^^^^^^^^^^^^^^ type: char
     }
 }
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 9333e269352..74bcab6caa9 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -476,7 +476,7 @@ fn infer_adt_pattern() {
             183..184 'x': usize
             190..191 'x': usize
             201..205 'E::B': E
-            209..212 'foo': bool
+            209..212 'foo': {unknown}
             216..217 '1': usize
             227..231 'E::B': E
             235..237 '10': usize
@@ -953,9 +953,9 @@ fn main() {
             42..51 'true | ()': bool
             49..51 '()': ()
             57..59 '{}': ()
-            68..80 '(() | true,)': ((),)
+            68..80 '(() | true,)': (bool,)
             69..71 '()': ()
-            69..78 '() | true': ()
+            69..78 '() | true': bool
             74..78 'true': bool
             74..78 'true': bool
             84..86 '{}': ()
@@ -964,19 +964,15 @@ fn main() {
             96..102 '_ | ()': bool
             100..102 '()': ()
             108..110 '{}': ()
-            119..128 '(() | _,)': ((),)
+            119..128 '(() | _,)': (bool,)
             120..122 '()': ()
-            120..126 '() | _': ()
+            120..126 '() | _': bool
             125..126 '_': bool
             132..134 '{}': ()
             49..51: expected bool, got ()
-            68..80: expected (bool,), got ((),)
             69..71: expected bool, got ()
-            69..78: expected bool, got ()
             100..102: expected bool, got ()
-            119..128: expected (bool,), got ((),)
             120..122: expected bool, got ()
-            120..126: expected bool, got ()
         "#]],
     );
 }
@@ -1092,3 +1088,19 @@ fn my_fn(foo: ...) {}
 "#,
     );
 }
+
+#[test]
+fn ref_pat_mutability() {
+    check(
+        r#"
+fn foo() {
+    let &() = &();
+    let &mut () = &mut ();
+    let &mut () = &();
+      //^^^^^^^ expected &(), got &mut ()
+    let &() = &mut ();
+      //^^^ expected &mut (), got &()
+}
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index de6ae7fff8f..e6b4f13c8d1 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -270,7 +270,7 @@ fn infer_std_crash_5() {
             61..320 '{     ...     }': ()
             75..79 'name': &{unknown}
             82..166 'if doe...     }': &{unknown}
-            85..98 'doesnt_matter': bool
+            85..98 'doesnt_matter': {unknown}
             99..128 '{     ...     }': &{unknown}
             113..118 'first': &{unknown}
             134..166 '{     ...     }': &{unknown}
@@ -279,7 +279,7 @@ fn infer_std_crash_5() {
             181..188 'content': &{unknown}
             191..313 'if ICE...     }': &{unknown}
             194..231 'ICE_RE..._VALUE': {unknown}
-            194..247 'ICE_RE...&name)': bool
+            194..247 'ICE_RE...&name)': {unknown}
             241..246 '&name': &&{unknown}
             242..246 'name': &{unknown}
             248..276 '{     ...     }': &{unknown}
@@ -1015,9 +1015,9 @@ fn cfg_tail() {
             20..31 '{ "first" }': ()
             22..29 '"first"': &str
             72..190 '{     ...] 13 }': ()
-            78..88 '{ "fake" }': &str
+            78..88 '{ "fake" }': ()
             80..86 '"fake"': &str
-            93..103 '{ "fake" }': &str
+            93..103 '{ "fake" }': ()
             95..101 '"fake"': &str
             108..120 '{ "second" }': ()
             110..118 '"second"': &str
@@ -1744,3 +1744,15 @@ fn foo(b: Bar) {
 "#,
     );
 }
+
+#[test]
+fn regression_14305() {
+    check_no_mismatches(
+        r#"
+//- minicore: add
+trait Tr {}
+impl Tr for [u8; C] {}
+const C: usize = 2 + 2;
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 2e5787b701c..0e9c349afef 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -352,7 +352,6 @@ unsafe fn baz(u: MyUnion) {
             71..89 'MyUnio...o: 0 }': MyUnion
             86..87 '0': u32
             95..113 'unsafe...(u); }': ()
-            95..113 'unsafe...(u); }': ()
             104..107 'baz': fn baz(MyUnion)
             104..110 'baz(u)': ()
             108..109 'u': MyUnion
@@ -360,7 +359,6 @@ unsafe fn baz(u: MyUnion) {
             126..146 'MyUnio... 0.0 }': MyUnion
             141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
-            152..170 'unsafe...(u); }': ()
             161..164 'baz': fn baz(MyUnion)
             161..167 'baz(u)': ()
             165..166 'u': MyUnion
@@ -2077,22 +2075,17 @@ async fn main() {
             16..193 '{     ...2 }; }': ()
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
-            30..43 'unsafe { 92 }': i32
             39..41 '92': i32
             53..54 'y': impl Future<Output = ()>
-            57..85 'async ...wait }': ()
             57..85 'async ...wait }': impl Future<Output = ()>
-            65..77 'async { () }': ()
             65..77 'async { () }': impl Future<Output = ()>
             65..83 'async ....await': ()
             73..75 '()': ()
             95..96 'z': ControlFlow<(), ()>
-            130..140 'try { () }': ()
             130..140 'try { () }': ControlFlow<(), ()>
             136..138 '()': ()
             150..151 'w': i32
             154..166 'const { 92 }': i32
-            154..166 'const { 92 }': i32
             162..164 '92': i32
             176..177 't': i32
             180..190 ''a: { 92 }': i32
@@ -2122,7 +2115,6 @@ fn main() {
             83..84 'f': F
             89..91 '{}': ()
             103..231 '{     ... }); }': ()
-            109..161 'async ...     }': Result<(), ()>
             109..161 'async ...     }': impl Future<Output = Result<(), ()>>
             125..139 'return Err(())': !
             132..135 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2134,7 +2126,6 @@ fn main() {
             167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
             167..228 'test(|...    })': ()
             172..227 '|| asy...     }': || -> impl Future<Output = Result<(), ()>>
-            175..227 'async ...     }': Result<(), ()>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
             198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -3283,3 +3274,18 @@ fn func() {
         "#]],
     );
 }
+
+#[test]
+fn issue_14275() {
+    // FIXME: evaluate const generic
+    check_types(
+        r#"
+struct Foo<const T: bool>;
+fn main() {
+    const B: bool = false;
+    let foo = Foo::<B>;
+      //^^^ Foo<_>
+}
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 70d2d5efa6c..34d957e26ef 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -5,6 +5,7 @@ use std::iter;
 
 use base_db::CrateId;
 use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
+use either::Either;
 use hir_def::{
     db::DefDatabase,
     generics::{
@@ -19,7 +20,6 @@ use hir_def::{
 };
 use hir_expand::name::Name;
 use intern::Interned;
-use itertools::Either;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
 
@@ -315,7 +315,10 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
         GenericDefId::TypeAliasId(it) => it.lookup(db).container,
         GenericDefId::ConstId(it) => it.lookup(db).container,
         GenericDefId::EnumVariantId(it) => return Some(it.parent.into()),
-        GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None,
+        GenericDefId::AdtId(_)
+        | GenericDefId::TraitId(_)
+        | GenericDefId::ImplId(_)
+        | GenericDefId::TraitAliasId(_) => return None,
     };
 
     match container {
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 54425d69b6b..db0b84ef088 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -14,7 +14,8 @@ use syntax::{ast, AstNode};
 
 use crate::{
     Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
-    Macro, Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
+    Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, TypeParam, Union,
+    Variant,
 };
 
 pub trait HasAttrs {
@@ -60,6 +61,7 @@ impl_has_attrs![
     (Static, StaticId),
     (Const, ConstId),
     (Trait, TraitId),
+    (TraitAlias, TraitAliasId),
     (TypeAlias, TypeAliasId),
     (Macro, MacroId),
     (Function, FunctionId),
@@ -134,6 +136,7 @@ fn resolve_doc_path(
         AttrDefId::StaticId(it) => it.resolver(db.upcast()),
         AttrDefId::ConstId(it) => it.resolver(db.upcast()),
         AttrDefId::TraitId(it) => it.resolver(db.upcast()),
+        AttrDefId::TraitAliasId(it) => it.resolver(db.upcast()),
         AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
         AttrDefId::ImplId(it) => it.resolver(db.upcast()),
         AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()),
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index e25d8678458..cd465739139 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -5,8 +5,9 @@
 //! But we need this for at least LRU caching at the query level.
 pub use hir_def::db::*;
 pub use hir_expand::db::{
-    AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternMacroCallQuery,
-    MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery,
+    AstDatabase, AstDatabaseStorage, AstIdMapQuery, ExpandProcMacroQuery, HygieneFrameQuery,
+    InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
+    MacroExpandQuery, ParseMacroExpansionQuery,
 };
 pub use hir_ty::db::*;
 
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 54d43fa8dc7..8f019a81b2d 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -10,7 +10,7 @@ use hir_def::path::ModPath;
 use hir_expand::{name::Name, HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
 
-use crate::{AssocItem, Field, MacroKind, Type};
+use crate::{AssocItem, Field, Local, MacroKind, Type};
 
 macro_rules! diagnostics {
     ($($diag:ident,)*) => {
@@ -31,6 +31,7 @@ macro_rules! diagnostics {
 
 diagnostics![
     BreakOutsideOfLoop,
+    ExpectedFunction,
     InactiveCode,
     IncorrectCase,
     InvalidDeriveTarget,
@@ -40,6 +41,7 @@ diagnostics![
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
+    NeedMut,
     NoSuchField,
     PrivateAssocItem,
     PrivateField,
@@ -47,10 +49,13 @@ diagnostics![
     TypeMismatch,
     UnimplementedBuiltinMacro,
     UnresolvedExternCrate,
+    UnresolvedField,
     UnresolvedImport,
     UnresolvedMacroCall,
+    UnresolvedMethodCall,
     UnresolvedModule,
     UnresolvedProcMacro,
+    UnusedMut,
 ];
 
 #[derive(Debug)]
@@ -130,6 +135,28 @@ pub struct PrivateAssocItem {
     pub item: AssocItem,
 }
 
+#[derive(Debug)]
+pub struct ExpectedFunction {
+    pub call: InFile<AstPtr<ast::Expr>>,
+    pub found: Type,
+}
+
+#[derive(Debug)]
+pub struct UnresolvedField {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub receiver: Type,
+    pub name: Name,
+    pub method_with_same_name_exists: bool,
+}
+
+#[derive(Debug)]
+pub struct UnresolvedMethodCall {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub receiver: Type,
+    pub name: Name,
+    pub field_with_same_name: Option<Type>,
+}
+
 #[derive(Debug)]
 pub struct PrivateField {
     pub expr: InFile<AstPtr<ast::Expr>>,
@@ -140,6 +167,7 @@ pub struct PrivateField {
 pub struct BreakOutsideOfLoop {
     pub expr: InFile<AstPtr<ast::Expr>>,
     pub is_break: bool,
+    pub bad_value_break: bool,
 }
 
 #[derive(Debug)]
@@ -171,17 +199,26 @@ pub struct MismatchedArgCount {
 
 #[derive(Debug)]
 pub struct MissingMatchArms {
-    pub file: HirFileId,
-    pub match_expr: AstPtr<ast::Expr>,
+    pub scrutinee_expr: InFile<AstPtr<ast::Expr>>,
     pub uncovered_patterns: String,
 }
 
 #[derive(Debug)]
 pub struct TypeMismatch {
-    // FIXME: add mismatches in patterns as well
-    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub expr_or_pat: Either<InFile<AstPtr<ast::Expr>>, InFile<AstPtr<ast::Pat>>>,
     pub expected: Type,
     pub actual: Type,
 }
 
+#[derive(Debug)]
+pub struct NeedMut {
+    pub local: Local,
+    pub span: InFile<SyntaxNodePtr>,
+}
+
+#[derive(Debug)]
+pub struct UnusedMut {
+    pub local: Local,
+}
+
 pub use hir_ty::diagnostics::IncorrectCase;
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 0d19420127f..5aae92efd19 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -17,15 +17,23 @@ use hir_ty::{
 };
 
 use crate::{
-    Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
-    LifetimeParam, Macro, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias,
-    TypeOrConstParam, TypeParam, Union, Variant,
+    Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, Field, Function, GenericParam,
+    HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, Trait, TraitAlias,
+    TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
 };
 
 impl HirDisplay for Function {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        let data = f.db.function_data(self.id);
-        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        let db = f.db;
+        let data = db.function_data(self.id);
+        let container = self.as_assoc_item(db).map(|it| it.container(db));
+        let mut module = self.module(db);
+        if let Some(AssocItemContainer::Impl(_)) = container {
+            // Block-local impls are "hoisted" to the nearest (non-block) module.
+            module = module.nearest_non_block_module(db);
+        }
+        let module_id = module.id;
+        write_visibility(module_id, self.visibility(db), f)?;
         if data.has_default_kw() {
             f.write_str("default ")?;
         }
@@ -35,7 +43,7 @@ impl HirDisplay for Function {
         if data.has_async_kw() {
             f.write_str("async ")?;
         }
-        if self.is_unsafe_to_call(f.db) {
+        if self.is_unsafe_to_call(db) {
             f.write_str("unsafe ")?;
         }
         if let Some(abi) = &data.abi {
@@ -50,7 +58,7 @@ impl HirDisplay for Function {
 
         let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty {
             TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
-            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner,TypeRef::Path(p) if p.is_self_type()) =>
+            TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
             {
                 f.write_char('&')?;
                 if let Some(lifetime) = lifetime {
@@ -442,8 +450,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
 
 impl HirDisplay for Const {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
-        let data = f.db.const_data(self.id);
+        let db = f.db;
+        let container = self.as_assoc_item(db).map(|it| it.container(db));
+        let mut module = self.module(db);
+        if let Some(AssocItemContainer::Impl(_)) = container {
+            // Block-local impls are "hoisted" to the nearest (non-block) module.
+            module = module.nearest_non_block_module(db);
+        }
+        write_visibility(module.id, self.visibility(db), f)?;
+        let data = db.const_data(self.id);
         f.write_str("const ")?;
         match &data.name {
             Some(name) => write!(f, "{name}: ")?,
@@ -486,6 +501,22 @@ impl HirDisplay for Trait {
     }
 }
 
+impl HirDisplay for TraitAlias {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
+        let data = f.db.trait_alias_data(self.id);
+        write!(f, "trait {}", data.name)?;
+        let def_id = GenericDefId::TraitAliasId(self.id);
+        write_generic_params(def_id, f)?;
+        f.write_str(" = ")?;
+        // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e.
+        // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might
+        // be less readable.
+        write_where_clause(def_id, f)?;
+        Ok(())
+    }
+}
+
 impl HirDisplay for TypeAlias {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index f825a72c0f5..aaaa7abf386 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -4,7 +4,7 @@
 //! are splitting the hir.
 
 use hir_def::{
-    expr::{LabelId, PatId},
+    expr::{BindingId, LabelId},
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
     ModuleDefId, VariantId,
 };
@@ -37,6 +37,7 @@ from_id![
     (hir_def::EnumId, crate::Enum),
     (hir_def::TypeAliasId, crate::TypeAlias),
     (hir_def::TraitId, crate::Trait),
+    (hir_def::TraitAliasId, crate::TraitAlias),
     (hir_def::StaticId, crate::Static),
     (hir_def::ConstId, crate::Const),
     (hir_def::FunctionId, crate::Function),
@@ -110,6 +111,7 @@ impl From<ModuleDefId> for ModuleDef {
             ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()),
             ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()),
             ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()),
+            ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()),
             ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
             ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()),
             ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()),
@@ -127,6 +129,7 @@ impl From<ModuleDef> for ModuleDefId {
             ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()),
             ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()),
             ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()),
+            ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()),
             ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()),
             ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()),
             ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()),
@@ -172,6 +175,7 @@ impl From<GenericDef> for GenericDefId {
             GenericDef::Function(it) => GenericDefId::FunctionId(it.id),
             GenericDef::Adt(it) => GenericDefId::AdtId(it.into()),
             GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
+            GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id),
             GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
             GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
             GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
@@ -186,6 +190,7 @@ impl From<GenericDefId> for GenericDef {
             GenericDefId::FunctionId(it) => GenericDef::Function(it.into()),
             GenericDefId::AdtId(it) => GenericDef::Adt(it.into()),
             GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
+            GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()),
             GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
             GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
             GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
@@ -246,9 +251,9 @@ impl From<AssocItem> for GenericDefId {
     }
 }
 
-impl From<(DefWithBodyId, PatId)> for Local {
-    fn from((parent, pat_id): (DefWithBodyId, PatId)) -> Self {
-        Local { parent, pat_id }
+impl From<(DefWithBodyId, BindingId)> for Local {
+    fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self {
+        Local { parent, binding_id }
     }
 }
 
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index f8b01db3e32..9f6b5c0a9fc 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -10,8 +10,9 @@ use hir_expand::InFile;
 use syntax::ast;
 
 use crate::{
-    db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, Macro,
-    Module, Static, Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant,
+    db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
+    LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam,
+    Union, Variant,
 };
 
 pub trait HasSource {
@@ -122,6 +123,12 @@ impl HasSource for Trait {
         Some(self.id.lookup(db.upcast()).source(db.upcast()))
     }
 }
+impl HasSource for TraitAlias {
+    type Ast = ast::TraitAlias;
+    fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        Some(self.id.lookup(db.upcast()).source(db.upcast()))
+    }
+}
 impl HasSource for TypeAlias {
     type Ast = ast::TypeAlias;
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
@@ -158,7 +165,7 @@ impl HasSource for Impl {
 }
 
 impl HasSource for TypeOrConstParam {
-    type Ast = Either<ast::TypeOrConstParam, ast::Trait>;
+    type Ast = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
         let child_source = self.id.parent.child_source(db.upcast());
         Some(child_source.map(|it| it[self.id.local_id].clone()))
@@ -172,3 +179,11 @@ impl HasSource for LifetimeParam {
         Some(child_source.map(|it| it[self.id.local_id].clone()))
     }
 }
+
+impl HasSource for LocalSource {
+    type Ast = Either<ast::IdentPat, ast::SelfParam>;
+
+    fn source(self, _: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+        Some(self.source)
+    }
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 2cb4ed2c335..25c07a2fbd3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -41,34 +41,34 @@ use either::Either;
 use hir_def::{
     adt::VariantData,
     body::{BodyDiagnostic, SyntheticSyntax},
-    expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
-    generics::{TypeOrConstParamData, TypeParamProvenance},
+    expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+    generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     item_tree::ItemTreeNode,
     lang_item::{LangItem, LangItemTarget},
     layout::{Layout, LayoutError, ReprOptions},
-    nameres::{self, diagnostics::DefDiagnostic},
+    nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
-    type_ref::ConstScalar,
     AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
     EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
     LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+    TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 };
 use hir_expand::{name::name, MacroCallKind};
 use hir_ty::{
     all_super_traits, autoderef,
-    consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
+    consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
+    display::HexifiedConst,
     layout::layout_of_ty,
     method_resolution::{self, TyFingerprint},
+    mir::{self, interpret_mir},
     primitive::UintTy,
     traits::FnTrait,
     AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
-    ConcreteConst, ConstValue, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar,
-    Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind,
-    WhereClause,
+    GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
+    TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
 };
 use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
@@ -77,7 +77,7 @@ use rustc_hash::FxHashSet;
 use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasAttrs as _, HasDocComments, HasName},
-    AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
+    AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T,
 };
 
 use crate::db::{DefDatabase, HirDatabase};
@@ -85,12 +85,12 @@ use crate::db::{DefDatabase, HirDatabase};
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
-        AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
-        MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
+        AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
         ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
-        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
-        UnresolvedProcMacro,
+        UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
+        UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -130,6 +130,7 @@ pub use {
     },
     hir_ty::{
         display::{HirDisplay, HirDisplayError, HirWrite},
+        mir::MirEvalError,
         PointerCast, Safety,
     },
 };
@@ -272,6 +273,7 @@ pub enum ModuleDef {
     Const(Const),
     Static(Static),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     BuiltinType(BuiltinType),
     Macro(Macro),
@@ -284,6 +286,7 @@ impl_from!(
     Const,
     Static,
     Trait,
+    TraitAlias,
     TypeAlias,
     BuiltinType,
     Macro
@@ -310,6 +313,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => Some(it.module(db)),
             ModuleDef::Static(it) => Some(it.module(db)),
             ModuleDef::Trait(it) => Some(it.module(db)),
+            ModuleDef::TraitAlias(it) => Some(it.module(db)),
             ModuleDef::TypeAlias(it) => Some(it.module(db)),
             ModuleDef::Macro(it) => Some(it.module(db)),
             ModuleDef::BuiltinType(_) => None,
@@ -338,6 +342,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => it.name(db)?,
             ModuleDef::Adt(it) => it.name(db),
             ModuleDef::Trait(it) => it.name(db),
+            ModuleDef::TraitAlias(it) => it.name(db),
             ModuleDef::Function(it) => it.name(db),
             ModuleDef::Variant(it) => it.name(db),
             ModuleDef::TypeAlias(it) => it.name(db),
@@ -356,6 +361,7 @@ impl ModuleDef {
                 Adt::Union(it) => it.id.into(),
             },
             ModuleDef::Trait(it) => it.id.into(),
+            ModuleDef::TraitAlias(it) => it.id.into(),
             ModuleDef::Function(it) => it.id.into(),
             ModuleDef::TypeAlias(it) => it.id.into(),
             ModuleDef::Module(it) => it.id.into(),
@@ -398,6 +404,7 @@ impl ModuleDef {
             ModuleDef::Module(_)
             | ModuleDef::Adt(_)
             | ModuleDef::Trait(_)
+            | ModuleDef::TraitAlias(_)
             | ModuleDef::TypeAlias(_)
             | ModuleDef::Macro(_)
             | ModuleDef::BuiltinType(_) => None,
@@ -413,6 +420,7 @@ impl ModuleDef {
             ModuleDef::Const(it) => it.attrs(db),
             ModuleDef::Static(it) => it.attrs(db),
             ModuleDef::Trait(it) => it.attrs(db),
+            ModuleDef::TraitAlias(it) => it.attrs(db),
             ModuleDef::TypeAlias(it) => it.attrs(db),
             ModuleDef::Macro(it) => it.attrs(db),
             ModuleDef::BuiltinType(_) => return None,
@@ -429,6 +437,7 @@ impl HasVisibility for ModuleDef {
             ModuleDef::Const(it) => it.visibility(db),
             ModuleDef::Static(it) => it.visibility(db),
             ModuleDef::Trait(it) => it.visibility(db),
+            ModuleDef::TraitAlias(it) => it.visibility(db),
             ModuleDef::TypeAlias(it) => it.visibility(db),
             ModuleDef::Variant(it) => it.visibility(db),
             ModuleDef::Macro(it) => it.visibility(db),
@@ -488,6 +497,20 @@ impl Module {
         Some(Module { id: def_map.module_id(parent_id) })
     }
 
+    /// Finds nearest non-block ancestor `Module` (`self` included).
+    pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module {
+        let mut id = self.id;
+        loop {
+            let def_map = id.def_map(db.upcast());
+            let origin = def_map[id.local_id].origin;
+            if matches!(origin, ModuleOrigin::BlockExpr { .. }) {
+                id = id.containing_module(db.upcast()).expect("block without parent module")
+            } else {
+                return Module { id };
+            }
+        }
+    }
+
     pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
         let mut res = vec![self];
         let mut curr = self;
@@ -1092,8 +1115,8 @@ impl Variant {
         self.source(db)?.value.expr()
     }
 
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
-        db.const_eval_variant(self.into())
+    pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> {
+        db.const_eval_discriminant(self.into())
     }
 }
 
@@ -1170,6 +1193,25 @@ impl Adt {
         }
     }
 
+    /// Returns the lifetime of the DataType
+    pub fn lifetime(&self, db: &dyn HirDatabase) -> Option<LifetimeParamData> {
+        let resolver = match self {
+            Adt::Struct(s) => s.id.resolver(db.upcast()),
+            Adt::Union(u) => u.id.resolver(db.upcast()),
+            Adt::Enum(e) => e.id.resolver(db.upcast()),
+        };
+        resolver
+            .generic_params()
+            .and_then(|gp| {
+                (&gp.lifetimes)
+                    .iter()
+                    // there should only be a single lifetime
+                    // but `Arena` requires to use an iterator
+                    .nth(0)
+            })
+            .map(|arena| arena.1.clone())
+    }
+
     pub fn as_enum(&self) -> Option<Enum> {
         if let Self::Enum(v) = self {
             Some(*v)
@@ -1285,6 +1327,15 @@ impl DefWithBody {
         body.pretty_print(db.upcast(), self.id())
     }
 
+    /// A textual representation of the MIR of this def's body for debugging purposes.
+    pub fn debug_mir(self, db: &dyn HirDatabase) -> String {
+        let body = db.mir_body(self.id());
+        match body {
+            Ok(body) => body.pretty_print(db),
+            Err(e) => format!("error:\n{e:?}"),
+        }
+    }
+
     pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
         let krate = self.module(db).id.krate();
 
@@ -1334,42 +1385,35 @@ impl DefWithBody {
 
         let infer = db.infer(self.into());
         let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
+        let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic");
         for d in &infer.diagnostics {
             match d {
-                hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
-                    let field = source_map.field_syntax(*expr);
+                &hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
+                    let field = source_map.field_syntax(expr);
                     acc.push(NoSuchField { field }.into())
                 }
-                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => {
-                    let expr = source_map
-                        .expr_syntax(expr)
-                        .expect("break outside of loop in synthetic syntax");
-                    acc.push(BreakOutsideOfLoop { expr, is_break }.into())
+                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+                    expr,
+                    is_break,
+                    bad_value_break,
+                } => {
+                    let expr = expr_syntax(expr);
+                    acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
                 }
-                hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
-                    match source_map.expr_syntax(*call_expr) {
-                        Ok(source_ptr) => acc.push(
-                            MismatchedArgCount {
-                                call_expr: source_ptr,
-                                expected: *expected,
-                                found: *found,
-                            }
+                &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
+                    acc.push(
+                        MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
                             .into(),
-                        ),
-                        Err(SyntheticSyntax) => (),
-                    }
+                    )
                 }
                 &hir_ty::InferenceDiagnostic::PrivateField { expr, field } => {
-                    let expr = source_map.expr_syntax(expr).expect("unexpected synthetic");
+                    let expr = expr_syntax(expr);
                     let field = field.into();
                     acc.push(PrivateField { expr, field }.into())
                 }
                 &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
                     let expr_or_pat = match id {
-                        ExprOrPatId::ExprId(expr) => source_map
-                            .expr_syntax(expr)
-                            .expect("unexpected synthetic")
-                            .map(Either::Left),
+                        ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
                         ExprOrPatId::PatId(pat) => source_map
                             .pat_syntax(pat)
                             .expect("unexpected synthetic")
@@ -1378,16 +1422,76 @@ impl DefWithBody {
                     let item = item.into();
                     acc.push(PrivateAssocItem { expr_or_pat, item }.into())
                 }
+                hir_ty::InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
+                    let call_expr = expr_syntax(*call_expr);
+
+                    acc.push(
+                        ExpectedFunction {
+                            call: call_expr,
+                            found: Type::new(db, DefWithBodyId::from(self), found.clone()),
+                        }
+                        .into(),
+                    )
+                }
+                hir_ty::InferenceDiagnostic::UnresolvedField {
+                    expr,
+                    receiver,
+                    name,
+                    method_with_same_name_exists,
+                } => {
+                    let expr = expr_syntax(*expr);
+
+                    acc.push(
+                        UnresolvedField {
+                            expr,
+                            name: name.clone(),
+                            receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()),
+                            method_with_same_name_exists: *method_with_same_name_exists,
+                        }
+                        .into(),
+                    )
+                }
+                hir_ty::InferenceDiagnostic::UnresolvedMethodCall {
+                    expr,
+                    receiver,
+                    name,
+                    field_with_same_name,
+                } => {
+                    let expr = expr_syntax(*expr);
+
+                    acc.push(
+                        UnresolvedMethodCall {
+                            expr,
+                            name: name.clone(),
+                            receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()),
+                            field_with_same_name: field_with_same_name
+                                .clone()
+                                .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)),
+                        }
+                        .into(),
+                    )
+                }
             }
         }
-        for (expr, mismatch) in infer.expr_type_mismatches() {
-            let expr = match source_map.expr_syntax(expr) {
-                Ok(expr) => expr,
-                Err(SyntheticSyntax) => continue,
+        for (pat_or_expr, mismatch) in infer.type_mismatches() {
+            let expr_or_pat = match pat_or_expr {
+                ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
+                // FIXME: Re-enable these once we have less false positives
+                ExprOrPatId::PatId(_pat) => continue,
+                #[allow(unreachable_patterns)]
+                ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
             };
+            let expr_or_pat = match expr_or_pat {
+                Ok(Either::Left(expr)) => Either::Left(expr),
+                Ok(Either::Right(InFile { file_id, value: Either::Left(pat) })) => {
+                    Either::Right(InFile { file_id, value: pat })
+                }
+                Ok(Either::Right(_)) | Err(SyntheticSyntax) => continue,
+            };
+
             acc.push(
                 TypeMismatch {
-                    expr,
+                    expr_or_pat,
                     expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.clone()),
                     actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.clone()),
                 }
@@ -1405,6 +1509,41 @@ impl DefWithBody {
             }
         }
 
+        let hir_body = db.body(self.into());
+
+        if let Ok(borrowck_result) = db.borrowck(self.into()) {
+            let mir_body = &borrowck_result.mir_body;
+            let mol = &borrowck_result.mutability_of_locals;
+            for (binding_id, _) in hir_body.bindings.iter() {
+                let need_mut = &mol[mir_body.binding_locals[binding_id]];
+                let local = Local { parent: self.into(), binding_id };
+                match (need_mut, local.is_mut(db)) {
+                    (mir::MutabilityReason::Mut { .. }, true)
+                    | (mir::MutabilityReason::Not, false) => (),
+                    (mir::MutabilityReason::Mut { spans }, false) => {
+                        for span in spans {
+                            let span: InFile<SyntaxNodePtr> = match span {
+                                mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+                                    Ok(s) => s.map(|x| x.into()),
+                                    Err(_) => continue,
+                                },
+                                mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+                                    Ok(s) => s.map(|x| match x {
+                                        Either::Left(e) => e.into(),
+                                        Either::Right(e) => e.into(),
+                                    }),
+                                    Err(_) => continue,
+                                },
+                                mir::MirSpan::Unknown => continue,
+                            };
+                            acc.push(NeedMut { local, span }.into());
+                        }
+                    }
+                    (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
+                }
+            }
+        }
+
         for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) {
             match diagnostic {
                 BodyValidationDiagnostic::RecordMissingFields {
@@ -1489,11 +1628,13 @@ impl DefWithBody {
                             if let ast::Expr::MatchExpr(match_expr) =
                                 &source_ptr.value.to_node(&root)
                             {
-                                if let Some(match_expr) = match_expr.expr() {
+                                if let Some(scrut_expr) = match_expr.expr() {
                                     acc.push(
                                         MissingMatchArms {
-                                            file: source_ptr.file_id,
-                                            match_expr: AstPtr::new(&match_expr),
+                                            scrutinee_expr: InFile::new(
+                                                source_ptr.file_id,
+                                                AstPtr::new(&scrut_expr),
+                                            ),
                                             uncovered_patterns,
                                         }
                                         .into(),
@@ -1582,6 +1723,10 @@ impl Function {
             .collect()
     }
 
+    pub fn num_params(self, db: &dyn HirDatabase) -> usize {
+        db.function_data(self.id).params.len()
+    }
+
     pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
         if self.self_param(db).is_none() {
             return None;
@@ -1639,6 +1784,14 @@ impl Function {
         let def_map = db.crate_def_map(loc.krate(db).into());
         def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
     }
+
+    pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> {
+        let body = db
+            .mir_body(self.id.into())
+            .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
+        interpret_mir(db, &body, false)?;
+        Ok(())
+    }
 }
 
 // Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
@@ -1679,8 +1832,8 @@ impl Param {
         let parent = DefWithBodyId::FunctionId(self.func.into());
         let body = db.body(parent);
         let pat_id = body.params[self.idx];
-        if let Pat::Bind { .. } = &body[pat_id] {
-            Some(Local { parent, pat_id: body.params[self.idx] })
+        if let Pat::Bind { id, .. } = &body[pat_id] {
+            Some(Local { parent, binding_id: *id })
         } else {
             None
         }
@@ -1781,8 +1934,18 @@ impl Const {
         Type::new_with_resolver_inner(db, &resolver, ty)
     }
 
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
-        db.const_eval(self.id)
+    pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
+        let c = db.const_eval(self.id)?;
+        let r = format!("{}", HexifiedConst(c).display(db));
+        // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
+        // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
+        // the user.
+        if r.contains("not-supported>") {
+            return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
+                "rendering complex constants".to_string(),
+            )));
+        }
+        return Ok(r);
     }
 }
 
@@ -1893,6 +2056,27 @@ impl HasVisibility for Trait {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TraitAlias {
+    pub(crate) id: TraitAliasId,
+}
+
+impl TraitAlias {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
+        Module { id: self.id.lookup(db.upcast()).container }
+    }
+
+    pub fn name(self, db: &dyn HirDatabase) -> Name {
+        db.trait_alias_data(self.id).name.clone()
+    }
+}
+
+impl HasVisibility for TraitAlias {
+    fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
+        db.trait_alias_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TypeAlias {
     pub(crate) id: TypeAliasId,
@@ -2265,6 +2449,7 @@ pub enum GenericDef {
     Function(Function),
     Adt(Adt),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     Impl(Impl),
     // enum variants cannot have generics themselves, but their parent enums
@@ -2277,6 +2462,7 @@ impl_from!(
     Function,
     Adt(Struct, Enum, Union),
     Trait,
+    TraitAlias,
     TypeAlias,
     Impl,
     Variant,
@@ -2317,20 +2503,53 @@ impl GenericDef {
 }
 
 /// A single local definition.
-///
-/// If the definition of this is part of a "MultiLocal", that is a local that has multiple declarations due to or-patterns
-/// then this only references a single one of those.
-/// To retrieve the other locals you should use [`Local::associated_locals`]
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct Local {
     pub(crate) parent: DefWithBodyId,
-    pub(crate) pat_id: PatId,
+    pub(crate) binding_id: BindingId,
+}
+
+pub struct LocalSource {
+    pub local: Local,
+    pub source: InFile<Either<ast::IdentPat, ast::SelfParam>>,
+}
+
+impl LocalSource {
+    pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> {
+        match &self.source.value {
+            Either::Left(x) => Some(x),
+            Either::Right(_) => None,
+        }
+    }
+
+    pub fn into_ident_pat(self) -> Option<ast::IdentPat> {
+        match self.source.value {
+            Either::Left(x) => Some(x),
+            Either::Right(_) => None,
+        }
+    }
+
+    pub fn original_file(&self, db: &dyn HirDatabase) -> FileId {
+        self.source.file_id.original_file(db.upcast())
+    }
+
+    pub fn name(&self) -> Option<ast::Name> {
+        self.source.value.name()
+    }
+
+    pub fn syntax(&self) -> &SyntaxNode {
+        self.source.value.syntax()
+    }
+
+    pub fn syntax_ptr(self) -> InFile<SyntaxNodePtr> {
+        self.source.map(|x| SyntaxNodePtr::new(x.syntax()))
+    }
 }
 
 impl Local {
     pub fn is_param(self, db: &dyn HirDatabase) -> bool {
-        let src = self.source(db);
-        match src.value {
+        let src = self.primary_source(db);
+        match src.source.value {
             Either::Left(pat) => pat
                 .syntax()
                 .ancestors()
@@ -2350,13 +2569,7 @@ impl Local {
 
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let body = db.body(self.parent);
-        match &body[self.pat_id] {
-            Pat::Bind { name, .. } => name.clone(),
-            _ => {
-                stdx::never!("hir::Local is missing a name!");
-                Name::missing()
-            }
-        }
+        body[self.binding_id].name.clone()
     }
 
     pub fn is_self(self, db: &dyn HirDatabase) -> bool {
@@ -2365,15 +2578,12 @@ impl Local {
 
     pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
         let body = db.body(self.parent);
-        matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
+        body[self.binding_id].mode == BindingAnnotation::Mutable
     }
 
     pub fn is_ref(self, db: &dyn HirDatabase) -> bool {
         let body = db.body(self.parent);
-        matches!(
-            &body[self.pat_id],
-            Pat::Bind { mode: BindingAnnotation::Ref | BindingAnnotation::RefMut, .. }
-        )
+        matches!(body[self.binding_id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
     }
 
     pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
@@ -2387,34 +2597,33 @@ impl Local {
     pub fn ty(self, db: &dyn HirDatabase) -> Type {
         let def = self.parent;
         let infer = db.infer(def);
-        let ty = infer[self.pat_id].clone();
+        let ty = infer[self.binding_id].clone();
         Type::new(db, def, ty)
     }
 
-    pub fn associated_locals(self, db: &dyn HirDatabase) -> Box<[Local]> {
-        let body = db.body(self.parent);
-        body.ident_patterns_for(&self.pat_id)
+    /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
+    pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
+        let (body, source_map) = db.body_with_source_map(self.parent);
+        body[self.binding_id]
+            .definitions
             .iter()
-            .map(|&pat_id| Local { parent: self.parent, pat_id })
+            .map(|&definition| {
+                let src = source_map.pat_syntax(definition).unwrap(); // Hmm...
+                let root = src.file_syntax(db.upcast());
+                src.map(|ast| match ast {
+                    // Suspicious unwrap
+                    Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
+                    Either::Right(it) => Either::Right(it.to_node(&root)),
+                })
+            })
+            .map(|source| LocalSource { local: self, source })
             .collect()
     }
 
-    /// If this local is part of a multi-local, retrieve the representative local.
-    /// That is the local that references are being resolved to.
-    pub fn representative(self, db: &dyn HirDatabase) -> Local {
-        let body = db.body(self.parent);
-        Local { pat_id: body.pattern_representative(self.pat_id), ..self }
-    }
-
-    pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> {
-        let (_body, source_map) = db.body_with_source_map(self.parent);
-        let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
-        let root = src.file_syntax(db.upcast());
-        src.map(|ast| match ast {
-            // Suspicious unwrap
-            Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
-            Either::Right(it) => Either::Right(it.to_node(&root)),
-        })
+    /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
+    pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
+        let all_sources = self.sources(db);
+        all_sources.into_iter().next().unwrap()
     }
 }
 
@@ -3190,6 +3399,14 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Raw(..))
     }
 
+    pub fn remove_raw_ptr(&self) -> Option<Type> {
+        if let TyKind::Raw(_, ty) = self.ty.kind(Interner) {
+            Some(self.derived(ty.clone()))
+        } else {
+            None
+        }
+    }
+
     pub fn contains_unknown(&self) -> bool {
         // FIXME: When we get rid of `ConstScalar::Unknown`, we can just look at precomputed
         // `TypeFlags` in `TyData`.
@@ -3260,12 +3477,7 @@ impl Type {
 
     pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> {
         if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
-            match len.data(Interner).value {
-                ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(len) }) => {
-                    Some((self.derived(ty.clone()), len as usize))
-                }
-                _ => None,
-            }
+            try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize))
         } else {
             None
         }
@@ -3321,6 +3533,24 @@ impl Type {
         }
     }
 
+    /// Iterates its type arguments
+    ///
+    /// It iterates the actual type arguments when concrete types are used
+    /// and otherwise the generic names.
+    /// It does not include `const` arguments.
+    ///
+    /// For code, such as:
+    /// ```text
+    /// struct Foo<T, U>
+    ///
+    /// impl<U> Foo<String, U>
+    /// ```
+    ///
+    /// It iterates:
+    /// ```text
+    /// - "String"
+    /// - "U"
+    /// ```
     pub fn type_arguments(&self) -> impl Iterator<Item = Type> + '_ {
         self.ty
             .strip_references()
@@ -3331,12 +3561,62 @@ impl Type {
             .map(move |ty| self.derived(ty))
     }
 
-    pub fn iterate_method_candidates<T>(
+    /// Iterates its type and const arguments
+    ///
+    /// It iterates the actual type and const arguments when concrete types
+    /// are used and otherwise the generic names.
+    ///
+    /// For code, such as:
+    /// ```text
+    /// struct Foo<T, const U: usize, const X: usize>
+    ///
+    /// impl<U> Foo<String, U, 12>
+    /// ```
+    ///
+    /// It iterates:
+    /// ```text
+    /// - "String"
+    /// - "U"
+    /// - "12"
+    /// ```
+    pub fn type_and_const_arguments<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+    ) -> impl Iterator<Item = SmolStr> + 'a {
+        self.ty
+            .strip_references()
+            .as_adt()
+            .into_iter()
+            .flat_map(|(_, substs)| substs.iter(Interner))
+            .filter_map(|arg| {
+                // arg can be either a `Ty` or `constant`
+                if let Some(ty) = arg.ty(Interner) {
+                    Some(SmolStr::new(ty.display(db).to_string()))
+                } else if let Some(const_) = arg.constant(Interner) {
+                    Some(SmolStr::new_inline(&const_.display(db).to_string()))
+                } else {
+                    None
+                }
+            })
+    }
+
+    /// Combines lifetime indicators, type and constant parameters into a single `Iterator`
+    pub fn generic_parameters<'a>(
+        &'a self,
+        db: &'a dyn HirDatabase,
+    ) -> impl Iterator<Item = SmolStr> + 'a {
+        // iterate the lifetime
+        self.as_adt()
+            .and_then(|a| a.lifetime(db).and_then(|lt| Some((&lt.name).to_smol_str())))
+            .into_iter()
+            // add the type and const paramaters
+            .chain(self.type_and_const_arguments(db))
+    }
+
+    pub fn iterate_method_candidates_with_traits<T>(
         &self,
         db: &dyn HirDatabase,
         scope: &SemanticsScope<'_>,
-        // FIXME this can be retrieved from `scope`, except autoimport uses this
-        // to specify a different set, so the method needs to be split
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
         name: Option<&Name>,
@@ -3364,6 +3644,24 @@ impl Type {
         slot
     }
 
+    pub fn iterate_method_candidates<T>(
+        &self,
+        db: &dyn HirDatabase,
+        scope: &SemanticsScope<'_>,
+        with_local_impls: Option<Module>,
+        name: Option<&Name>,
+        callback: impl FnMut(Function) -> Option<T>,
+    ) -> Option<T> {
+        self.iterate_method_candidates_with_traits(
+            db,
+            scope,
+            &scope.visible_traits().0,
+            with_local_impls,
+            name,
+            callback,
+        )
+    }
+
     fn iterate_method_candidates_dyn(
         &self,
         db: &dyn HirDatabase,
@@ -3632,11 +3930,13 @@ impl Type {
     }
 }
 
+// FIXME: Document this
 #[derive(Debug)]
 pub struct Callable {
     ty: Type,
     sig: CallableSig,
     callee: Callee,
+    /// Whether this is a method that was called with method call syntax.
     pub(crate) is_bound_method: bool,
 }
 
@@ -3670,14 +3970,14 @@ impl Callable {
             Other => CallableKind::Other,
         }
     }
-    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
+    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
         let func = match self.callee {
             Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
             _ => return None,
         };
         let src = func.lookup(db.upcast()).source(db.upcast());
         let param_list = src.value.param_list()?;
-        param_list.self_param()
+        Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
     }
     pub fn n_params(&self) -> usize {
         self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
@@ -3936,6 +4236,12 @@ impl HasCrate for Trait {
     }
 }
 
+impl HasCrate for TraitAlias {
+    fn krate(&self, db: &dyn HirDatabase) -> Crate {
+        self.module(db).krate()
+    }
+}
+
 impl HasCrate for Static {
     fn krate(&self, db: &dyn HirDatabase) -> Crate {
         self.module(db).krate()
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 486b7ee62ed..8bd905d0113 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -12,7 +12,7 @@ use hir_def::{
     macro_id_to_def_id,
     resolver::{self, HasResolver, Resolver, TypeNs},
     type_ref::Mutability,
-    AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
+    AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
     db::AstDatabase,
@@ -68,7 +68,8 @@ impl PathResolution {
                 | ModuleDef::Function(_)
                 | ModuleDef::Module(_)
                 | ModuleDef::Static(_)
-                | ModuleDef::Trait(_),
+                | ModuleDef::Trait(_)
+                | ModuleDef::TraitAlias(_),
             ) => None,
             PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
                 Some(TypeNs::TypeAliasId((*alias).into()))
@@ -365,6 +366,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_method_call(call).map(Function::from)
     }
 
+    /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
+    pub fn resolve_method_call_field_fallback(
+        &self,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<Function, Field>> {
+        self.imp
+            .resolve_method_call_fallback(call)
+            .map(|it| it.map_left(Function::from).map_right(Field::from))
+    }
+
     pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
         self.imp.resolve_await_to_poll(await_expr).map(Function::from)
     }
@@ -527,8 +538,8 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
-        let src = self.wrap_node_infile(attr.clone());
         let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+        let src = self.wrap_node_infile(attr.clone());
         let call_id = self.with_ctx(|ctx| {
             ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
         })?;
@@ -1092,7 +1103,10 @@ impl<'db> SemanticsImpl<'db> {
                     let kind = match adjust.kind {
                         hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
                         hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
-                            Adjust::Deref(Some(OverloadedDeref(mutability(m))))
+                            // FIXME: Should we handle unknown mutability better?
+                            Adjust::Deref(Some(OverloadedDeref(
+                                m.map(mutability).unwrap_or(Mutability::Shared),
+                            )))
                         }
                         hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
                         hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
@@ -1145,6 +1159,13 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(call.syntax())?.resolve_method_call(self.db, call)
     }
 
+    fn resolve_method_call_fallback(
+        &self,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<FunctionId, FieldId>> {
+        self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
+    }
+
     fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
         self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
     }
@@ -1330,6 +1351,7 @@ impl<'db> SemanticsImpl<'db> {
                 })
             }
             ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
+            ChildContainer::TraitAliasId(it) => it.resolver(self.db.upcast()),
             ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
             ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()),
             ChildContainer::EnumId(it) => it.resolver(self.db.upcast()),
@@ -1556,6 +1578,7 @@ to_def_impls![
     (crate::Enum, ast::Enum, enum_to_def),
     (crate::Union, ast::Union, union_to_def),
     (crate::Trait, ast::Trait, trait_to_def),
+    (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def),
     (crate::Impl, ast::Impl, impl_to_def),
     (crate::TypeAlias, ast::TypeAlias, type_alias_to_def),
     (crate::Const, ast::Const, const_to_def),
@@ -1634,8 +1657,8 @@ impl<'a> SemanticsScope<'a> {
                     resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
                     resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
                     resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
-                    resolver::ScopeDef::Local(pat_id) => match self.resolver.body_owner() {
-                        Some(parent) => ScopeDef::Local(Local { parent, pat_id }),
+                    resolver::ScopeDef::Local(binding_id) => match self.resolver.body_owner() {
+                        Some(parent) => ScopeDef::Local(Local { parent, binding_id }),
                         None => continue,
                     },
                     resolver::ScopeDef::Label(label_id) => match self.resolver.body_owner() {
@@ -1673,6 +1696,7 @@ impl<'a> SemanticsScope<'a> {
     }
 }
 
+#[derive(Debug)]
 pub struct VisibleTraits(pub FxHashSet<TraitId>);
 
 impl ops::Deref for VisibleTraits {
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 2b5bfda1d43..f6f8c9a250f 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -89,16 +89,16 @@ use base_db::FileId;
 use hir_def::{
     child_by_source::ChildBySource,
     dyn_map::DynMap,
-    expr::{LabelId, PatId},
+    expr::{BindingId, LabelId},
     keys::{self, Key},
     AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
     GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
+    TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use stdx::impl_from;
+use stdx::{impl_from, never};
 use syntax::{
     ast::{self, HasName},
     AstNode, SyntaxNode,
@@ -159,6 +159,12 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
         self.to_def(src, keys::TRAIT)
     }
+    pub(super) fn trait_alias_to_def(
+        &mut self,
+        src: InFile<ast::TraitAlias>,
+    ) -> Option<TraitAliasId> {
+        self.to_def(src, keys::TRAIT_ALIAS)
+    }
     pub(super) fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
         self.to_def(src, keys::IMPL)
     }
@@ -210,14 +216,14 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn bind_pat_to_def(
         &mut self,
         src: InFile<ast::IdentPat>,
-    ) -> Option<(DefWithBodyId, PatId)> {
+    ) -> Option<(DefWithBodyId, BindingId)> {
         let container = self.find_pat_or_label_container(src.syntax())?;
         let (body, source_map) = self.db.body_with_source_map(container);
         let src = src.map(ast::Pat::from);
         let pat_id = source_map.node_pat(src.as_ref())?;
         // the pattern could resolve to a constant, verify that that is not the case
-        if let crate::Pat::Bind { .. } = body[pat_id] {
-            Some((container, pat_id))
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+            Some((container, id))
         } else {
             None
         }
@@ -225,11 +231,16 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn self_param_to_def(
         &mut self,
         src: InFile<ast::SelfParam>,
-    ) -> Option<(DefWithBodyId, PatId)> {
+    ) -> Option<(DefWithBodyId, BindingId)> {
         let container = self.find_pat_or_label_container(src.syntax())?;
-        let (_body, source_map) = self.db.body_with_source_map(container);
+        let (body, source_map) = self.db.body_with_source_map(container);
         let pat_id = source_map.node_self_param(src.as_ref())?;
-        Some((container, pat_id))
+        if let crate::Pat::Bind { id, .. } = body[pat_id] {
+            Some((container, id))
+        } else {
+            never!();
+            None
+        }
     }
     pub(super) fn label_to_def(
         &mut self,
@@ -353,6 +364,9 @@ impl SourceToDefCtx<'_, '_> {
             match item {
                 ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
                 ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
+                ast::Item::TraitAlias(it) => {
+                    self.trait_alias_to_def(container.with_value(it))?.into()
+                }
                 ast::Item::Impl(it) => self.impl_to_def(container.with_value(it))?.into(),
                 ast::Item::Enum(it) => self.enum_to_def(container.with_value(it))?.into(),
                 ast::Item::TypeAlias(it) => {
@@ -400,6 +414,9 @@ impl SourceToDefCtx<'_, '_> {
                 ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(),
                 ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(),
                 ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(),
+                ast::Item::TraitAlias(it) => {
+                    self.trait_alias_to_def(InFile::new(file_id, it))?.into()
+                }
                 ast::Item::TypeAlias(it) => {
                     self.type_alias_to_def(InFile::new(file_id, it))?.into()
                 }
@@ -435,6 +452,7 @@ pub(crate) enum ChildContainer {
     DefWithBodyId(DefWithBodyId),
     ModuleId(ModuleId),
     TraitId(TraitId),
+    TraitAliasId(TraitAliasId),
     ImplId(ImplId),
     EnumId(EnumId),
     VariantId(VariantId),
@@ -447,6 +465,7 @@ impl_from! {
     DefWithBodyId,
     ModuleId,
     TraitId,
+    TraitAliasId,
     ImplId,
     EnumId,
     VariantId,
@@ -462,6 +481,7 @@ impl ChildContainer {
             ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
             ChildContainer::ModuleId(it) => it.child_by_source(db, file_id),
             ChildContainer::TraitId(it) => it.child_by_source(db, file_id),
+            ChildContainer::TraitAliasId(_) => DynMap::default(),
             ChildContainer::ImplId(it) => it.child_by_source(db, file_id),
             ChildContainer::EnumId(it) => it.child_by_source(db, file_id),
             ChildContainer::VariantId(it) => it.child_by_source(db, file_id),
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 3b39e9fa919..133fa810d66 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -10,6 +10,7 @@ use std::{
     sync::Arc,
 };
 
+use either::Either;
 use hir_def::{
     body::{
         self,
@@ -51,7 +52,7 @@ use syntax::{
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
     BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
-    Struct, ToolModule, Trait, Type, TypeAlias, Variant,
+    Struct, ToolModule, Trait, TraitAlias, Type, TypeAlias, Variant,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -266,6 +267,21 @@ impl SourceAnalyzer {
         Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
     }
 
+    pub(crate) fn resolve_method_call_fallback(
+        &self,
+        db: &dyn HirDatabase,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Either<FunctionId, FieldId>> {
+        let expr_id = self.expr_id(db, &call.clone().into())?;
+        let inference_result = self.infer.as_ref()?;
+        match inference_result.method_resolution(expr_id) {
+            Some((f_in_trait, substs)) => {
+                Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)))
+            }
+            None => inference_result.field_resolution(expr_id).map(Either::Right),
+        }
+    }
+
     pub(crate) fn resolve_await_to_poll(
         &self,
         db: &dyn HirDatabase,
@@ -406,8 +422,8 @@ impl SourceAnalyzer {
             // Shorthand syntax, resolve to the local
             let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
             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()? })
+                Some(ValueNs::LocalBinding(binding_id)) => {
+                    Some(Local { binding_id, parent: self.resolver.body_owner()? })
                 }
                 _ => None,
             }
@@ -791,7 +807,7 @@ impl SourceAnalyzer {
             || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
             |d| db.trait_environment(d),
         );
-        method_resolution::lookup_impl_method(db, env, func, substs)
+        method_resolution::lookup_impl_method(db, env, func, substs).0
     }
 
     fn resolve_impl_const_or_trait_def(
@@ -809,7 +825,7 @@ impl SourceAnalyzer {
             || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
             |d| db.trait_environment(d),
         );
-        method_resolution::lookup_impl_const(db, env, const_id, subs)
+        method_resolution::lookup_impl_const(db, env, const_id, subs).0
     }
 
     fn lang_trait_fn(
@@ -943,17 +959,17 @@ fn resolve_hir_path_(
                 res.map(|ty_ns| (ty_ns, path.segments().first()))
             }
             None => {
-                let (ty, remaining) =
+                let (ty, remaining_idx) =
                     resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
-                match remaining {
-                    Some(remaining) if remaining > 1 => {
-                        if remaining + 1 == path.segments().len() {
+                match remaining_idx {
+                    Some(remaining_idx) => {
+                        if remaining_idx + 1 == path.segments().len() {
                             Some((ty, path.segments().last()))
                         } else {
                             None
                         }
                     }
-                    _ => Some((ty, path.segments().get(1))),
+                    None => Some((ty, None)),
                 }
             }
         }?;
@@ -978,6 +994,7 @@ fn resolve_hir_path_(
             TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
             TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
             TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+            TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
         };
         match unresolved {
             Some(unresolved) => resolver
@@ -1001,8 +1018,8 @@ fn resolve_hir_path_(
     let values = || {
         resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
             let res = match val {
-                ValueNs::LocalBinding(pat_id) => {
-                    let var = Local { parent: body_owner?, pat_id };
+                ValueNs::LocalBinding(binding_id) => {
+                    let var = Local { parent: body_owner?, binding_id };
                     PathResolution::Local(var)
                 }
                 ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
@@ -1065,6 +1082,7 @@ fn resolve_hir_path_qualifier(
             TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
             TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
             TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
+            TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()),
         })
         .or_else(|| {
             resolver
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index fd78decda4e..a9afa1c6f45 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -68,6 +68,7 @@ pub enum FileSymbolKind {
     Static,
     Struct,
     Trait,
+    TraitAlias,
     TypeAlias,
     Union,
 }
@@ -153,6 +154,9 @@ impl<'a> SymbolCollector<'a> {
                     self.push_decl(id, FileSymbolKind::Trait);
                     self.collect_from_trait(id);
                 }
+                ModuleDefId::TraitAliasId(id) => {
+                    self.push_decl(id, FileSymbolKind::TraitAlias);
+                }
                 ModuleDefId::TypeAliasId(id) => {
                     self.push_decl_assoc(id, FileSymbolKind::TypeAlias);
                 }
diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs
index 0057f439f1a..785ae3d09c6 100644
--- a/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -211,10 +211,8 @@ fn main() {
         check_assist_not_applicable(
             add_explicit_type,
             r#"
-//- minicore: option
-
 fn main() {
-    let $0l = [0.0; Some(2).unwrap()];
+    let $0l = [0.0; unresolved_function(5)];
 }
 "#,
         );
diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index f32ef2d59d8..9e1d9a702a1 100644
--- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -157,19 +157,12 @@ fn is_ref_and_impls_iter_method(
     let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 
     let has_wanted_method = ty
-        .iterate_method_candidates(
-            sema.db,
-            &scope,
-            &scope.visible_traits().0,
-            None,
-            Some(&wanted_method),
-            |func| {
-                if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
-                    return Some(());
-                }
-                None
-            },
-        )
+        .iterate_method_candidates(sema.db, &scope, None, Some(&wanted_method), |func| {
+            if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
+                return Some(());
+            }
+            None
+        })
         .is_some();
     if !has_wanted_method {
         return None;
diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index 65c2479e9f2..7f2c01772ba 100644
--- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -1,6 +1,6 @@
 use ide_db::defs::{Definition, NameRefClass};
 use syntax::{
-    ast::{self, HasName},
+    ast::{self, HasName, Name},
     ted, AstNode, SyntaxNode,
 };
 
@@ -48,7 +48,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
         other => format!("{{ {other} }}"),
     };
     let extracting_arm_pat = extracting_arm.pat()?;
-    let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?;
+    let extracted_variable_positions = find_extracted_variable(ctx, &extracting_arm)?;
 
     acc.add(
         AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite),
@@ -56,7 +56,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
         let_stmt.syntax().text_range(),
         |builder| {
             let extracting_arm_pat =
-                rename_variable(&extracting_arm_pat, extracted_variable, binding);
+                rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding);
             builder.replace(
                 let_stmt.syntax().text_range(),
                 format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
@@ -95,14 +95,15 @@ fn find_arms(
 }
 
 // Given an extracting arm, find the extracted variable.
-fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<ast::Name> {
+fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<Vec<Name>> {
     match arm.expr()? {
         ast::Expr::PathExpr(path) => {
             let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
             match NameRefClass::classify(&ctx.sema, &name_ref)? {
                 NameRefClass::Definition(Definition::Local(local)) => {
-                    let source = local.source(ctx.db()).value.left()?;
-                    Some(source.name()?)
+                    let source =
+                        local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name());
+                    source.collect()
                 }
                 _ => None,
             }
@@ -115,27 +116,34 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
 }
 
 // Rename `extracted` with `binding` in `pat`.
-fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::Pat) -> SyntaxNode {
+fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
     let syntax = pat.syntax().clone_for_update();
-    let extracted_syntax = syntax.covering_element(extracted.syntax().text_range());
+    let extracted = extracted
+        .iter()
+        .map(|e| syntax.covering_element(e.syntax().text_range()))
+        .collect::<Vec<_>>();
+    for extracted_syntax in extracted {
+        // If `extracted` variable is a record field, we should rename it to `binding`,
+        // otherwise we just need to replace `extracted` with `binding`.
 
-    // If `extracted` variable is a record field, we should rename it to `binding`,
-    // otherwise we just need to replace `extracted` with `binding`.
-
-    if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
-    {
-        if let Some(name_ref) = record_pat_field.field_name() {
-            ted::replace(
-                record_pat_field.syntax(),
-                ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding)
+        if let Some(record_pat_field) =
+            extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
+        {
+            if let Some(name_ref) = record_pat_field.field_name() {
+                ted::replace(
+                    record_pat_field.syntax(),
+                    ast::make::record_pat_field(
+                        ast::make::name_ref(&name_ref.text()),
+                        binding.clone(),
+                    )
                     .syntax()
                     .clone_for_update(),
-            );
+                );
+            }
+        } else {
+            ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update());
         }
-    } else {
-        ted::replace(extracted_syntax, binding.syntax().clone_for_update());
     }
-
     syntax
 }
 
@@ -162,6 +170,39 @@ fn foo(opt: Option<()>) {
         );
     }
 
+    #[test]
+    fn or_pattern_multiple_binding() {
+        check_assist(
+            convert_match_to_let_else,
+            r#"
+//- minicore: option
+enum Foo {
+    A(u32),
+    B(u32),
+    C(String),
+}
+
+fn foo(opt: Option<Foo>) -> Result<u32, ()> {
+    let va$0lue = match opt {
+        Some(Foo::A(it) | Foo::B(it)) => it,
+        _ => return Err(()),
+    };
+}
+    "#,
+            r#"
+enum Foo {
+    A(u32),
+    B(u32),
+    C(String),
+}
+
+fn foo(opt: Option<Foo>) -> Result<u32, ()> {
+    let Some(Foo::A(value) | Foo::B(value)) = opt else { return Err(()) };
+}
+    "#,
+        );
+    }
+
     #[test]
     fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() {
         cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2);
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index e04a1dabb2c..0b90c9ba34f 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -3,7 +3,8 @@ use std::iter;
 use ast::make;
 use either::Either;
 use hir::{
-    HasSource, HirDisplay, InFile, Local, ModuleDef, PathResolution, Semantics, TypeInfo, TypeParam,
+    HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
+    TypeInfo, TypeParam,
 };
 use ide_db::{
     defs::{Definition, NameRefClass},
@@ -710,7 +711,7 @@ impl FunctionBody {
                     ) => local_ref,
                     _ => return,
                 };
-            let InFile { file_id, value } = local_ref.source(sema.db);
+            let InFile { file_id, value } = local_ref.primary_source(sema.db).source;
             // locals defined inside macros are not relevant to us
             if !file_id.is_macro() {
                 match value {
@@ -972,11 +973,11 @@ impl FunctionBody {
         locals: impl Iterator<Item = Local>,
     ) -> Vec<Param> {
         locals
-            .map(|local| (local, local.source(ctx.db())))
+            .map(|local| (local, local.primary_source(ctx.db())))
             .filter(|(_, src)| is_defined_outside_of_body(ctx, self, src))
-            .filter_map(|(local, src)| match src.value {
-                Either::Left(src) => Some((local, src)),
-                Either::Right(_) => {
+            .filter_map(|(local, src)| match src.into_ident_pat() {
+                Some(src) => Some((local, src)),
+                None => {
                     stdx::never!(false, "Local::is_self returned false, but source is SelfParam");
                     None
                 }
@@ -1238,17 +1239,9 @@ fn local_outlives_body(
 fn is_defined_outside_of_body(
     ctx: &AssistContext<'_>,
     body: &FunctionBody,
-    src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
+    src: &LocalSource,
 ) -> bool {
-    src.file_id.original_file(ctx.db()) == ctx.file_id()
-        && !body.contains_node(either_syntax(&src.value))
-}
-
-fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
-    match value {
-        Either::Left(pat) => pat.syntax(),
-        Either::Right(it) => it.syntax(),
-    }
+    src.original_file(ctx.db()) == ctx.file_id() && !body.contains_node(src.syntax())
 }
 
 /// find where to put extracted function definition
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index a738deffb95..16356141288 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -287,7 +287,7 @@ fn foo() {
             extract_variable,
             r"
 fn foo() {
-    $0{ let x = 0; x }$0
+    $0{ let x = 0; x }$0;
     something_else();
 }",
             r"
diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs
index d9e00435ecf..4c61678eab4 100644
--- a/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -192,6 +192,10 @@ fn target_data_for_def(
             target_name = Some(t.name(db));
             offset_target_and_file_id(db, t)?
         }
+        hir::ModuleDef::TraitAlias(t) => {
+            target_name = Some(t.name(db));
+            offset_target_and_file_id(db, t)?
+        }
         hir::ModuleDef::TypeAlias(t) => {
             target_name = Some(t.name(db));
             offset_target_and_file_id(db, t)?
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 45b27a63ce2..eef037f9875 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -5,6 +5,7 @@ use ide_db::{
     base_db::FileId,
     defs::{Definition, NameRefClass},
     famous_defs::FamousDefs,
+    helpers::is_editable_crate,
     path_transform::PathTransform,
     FxHashMap, FxHashSet, RootDatabase, SnippetCap,
 };
@@ -65,6 +66,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let fn_name = &*name_ref.text();
     let TargetInfo { target_module, adt_name, target, file, insert_offset } =
         fn_target_info(ctx, path, &call, fn_name)?;
+
+    if let Some(m) = target_module {
+        if !is_editable_crate(m.krate(), ctx.db()) {
+            return None;
+        }
+    }
+
     let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
     let text_range = call.syntax().text_range();
     let label = format!("Generate {} function", function_builder.fn_name);
@@ -141,12 +149,11 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let receiver_ty = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references();
     let adt = receiver_ty.as_adt()?;
 
-    let current_module = ctx.sema.scope(call.syntax())?.module();
     let target_module = adt.module(ctx.sema.db);
-
-    if current_module.krate() != target_module.krate() {
+    if !is_editable_crate(target_module.krate(), ctx.db()) {
         return None;
     }
+
     let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
     let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
 
@@ -253,7 +260,7 @@ struct FunctionBuilder {
     params: ast::ParamList,
     ret_type: Option<ast::RetType>,
     should_focus_return_type: bool,
-    needs_pub: bool,
+    visibility: Visibility,
     is_async: bool,
 }
 
@@ -264,12 +271,14 @@ impl FunctionBuilder {
         ctx: &AssistContext<'_>,
         call: &ast::CallExpr,
         fn_name: &str,
-        target_module: Option<hir::Module>,
+        target_module: Option<Module>,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
-        let needs_pub = target_module.is_some();
         let target_module =
             target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?;
+
+        let current_module = ctx.sema.scope(call.syntax())?.module();
+        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
         let fn_name = make::name(fn_name);
         let mut necessary_generic_params = FxHashSet::default();
         let params = fn_args(
@@ -300,7 +309,7 @@ impl FunctionBuilder {
             params,
             ret_type,
             should_focus_return_type,
-            needs_pub,
+            visibility,
             is_async,
         })
     }
@@ -313,8 +322,9 @@ impl FunctionBuilder {
         target_module: Module,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
-        let needs_pub =
-            !module_is_descendant(&ctx.sema.scope(call.syntax())?.module(), &target_module, ctx);
+        let current_module = ctx.sema.scope(call.syntax())?.module();
+        let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
+
         let fn_name = make::name(&name.text());
         let mut necessary_generic_params = FxHashSet::default();
         necessary_generic_params.extend(receiver_ty.generic_params(ctx.db()));
@@ -346,7 +356,7 @@ impl FunctionBuilder {
             params,
             ret_type,
             should_focus_return_type,
-            needs_pub,
+            visibility,
             is_async,
         })
     }
@@ -354,7 +364,11 @@ impl FunctionBuilder {
     fn render(self, is_method: bool) -> FunctionTemplate {
         let placeholder_expr = make::ext::expr_todo();
         let fn_body = make::block_expr(vec![], Some(placeholder_expr));
-        let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
+        let visibility = match self.visibility {
+            Visibility::None => None,
+            Visibility::Crate => Some(make::visibility_pub_crate()),
+            Visibility::Pub => Some(make::visibility_pub()),
+        };
         let mut fn_def = make::fn_(
             visibility,
             self.fn_name,
@@ -527,7 +541,7 @@ impl GeneratedFunctionTarget {
 /// Computes parameter list for the generated function.
 fn fn_args(
     ctx: &AssistContext<'_>,
-    target_module: hir::Module,
+    target_module: Module,
     call: ast::CallableExpr,
     necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
 ) -> Option<ast::ParamList> {
@@ -957,13 +971,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
 
 fn fn_arg_type(
     ctx: &AssistContext<'_>,
-    target_module: hir::Module,
+    target_module: Module,
     fn_arg: &ast::Expr,
     generic_params: &mut FxHashSet<hir::GenericParam>,
 ) -> String {
     fn maybe_displayed_type(
         ctx: &AssistContext<'_>,
-        target_module: hir::Module,
+        target_module: Module,
         fn_arg: &ast::Expr,
         generic_params: &mut FxHashSet<hir::GenericParam>,
     ) -> Option<String> {
@@ -1048,16 +1062,29 @@ fn next_space_for_fn_in_impl(impl_: &ast::Impl) -> Option<GeneratedFunctionTarge
     }
 }
 
-fn module_is_descendant(module: &hir::Module, ans: &hir::Module, ctx: &AssistContext<'_>) -> bool {
-    if module == ans {
-        return true;
+#[derive(Clone, Copy)]
+enum Visibility {
+    None,
+    Crate,
+    Pub,
+}
+
+fn calculate_necessary_visibility(
+    current_module: Module,
+    target_module: Module,
+    ctx: &AssistContext<'_>,
+) -> Visibility {
+    let db = ctx.db();
+    let current_module = current_module.nearest_non_block_module(db);
+    let target_module = target_module.nearest_non_block_module(db);
+
+    if target_module.krate() != current_module.krate() {
+        Visibility::Pub
+    } else if current_module.path_to_root(db).contains(&target_module) {
+        Visibility::None
+    } else {
+        Visibility::Crate
     }
-    for c in ans.children(ctx.sema.db) {
-        if module_is_descendant(module, &c, ctx) {
-            return true;
-        }
-    }
-    false
 }
 
 // This is never intended to be used as a generic graph strucuture. If there's ever another need of
@@ -2656,4 +2683,79 @@ fn main() {
 ",
         )
     }
+
+    #[test]
+    fn applicable_in_different_local_crate() {
+        check_assist(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:local
+fn dummy() {}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::foo$0();
+}
+",
+            r"
+fn dummy() {}
+
+pub fn foo() ${0:-> _} {
+    todo!()
+}
+",
+        );
+    }
+
+    #[test]
+    fn applicable_in_different_local_crate_method() {
+        check_assist(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:local
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::S.foo$0();
+}
+",
+            r"
+pub struct S;
+impl S {
+    pub fn foo(&self) ${0:-> _} {
+        todo!()
+    }
+}
+",
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_different_library_crate() {
+        check_assist_not_applicable(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:library
+fn dummy() {}
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::foo$0();
+}
+",
+        );
+    }
+
+    #[test]
+    fn not_applicable_in_different_library_crate_method() {
+        check_assist_not_applicable(
+            generate_function,
+            r"
+//- /lib.rs crate:lib new_source_root:library
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+fn main() {
+    lib::S.foo$0();
+}
+",
+        );
+    }
 }
diff --git a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
index 9ce525ca375..44291861960 100644
--- a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
@@ -95,14 +95,7 @@ fn get_impl_method(
 
     let scope = ctx.sema.scope(impl_.syntax())?;
     let ty = impl_def.self_ty(db);
-    ty.iterate_method_candidates(
-        db,
-        &scope,
-        &scope.visible_traits().0,
-        None,
-        Some(fn_name),
-        |func| Some(func),
-    )
+    ty.iterate_method_candidates(db, &scope, None, Some(fn_name), |func| Some(func))
 }
 
 #[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 8d311262a75..e30a3e942c4 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,5 +1,5 @@
 use ide_db::{
-    imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
+    imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
 };
 use itertools::Itertools;
 use stdx::format_to;
diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs
index ce44100e34b..e69d1a29677 100644
--- a/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,3 @@
-use either::Either;
 use hir::{PathResolution, Semantics};
 use ide_db::{
     base_db::FileId,
@@ -205,12 +204,14 @@ fn inline_usage(
         return None;
     }
 
-    // FIXME: Handle multiple local definitions
-    let bind_pat = match local.source(sema.db).value {
-        Either::Left(ident) => ident,
-        _ => return None,
+    let sources = local.sources(sema.db);
+    let [source] = sources.as_slice() else {
+        // Not applicable with locals with multiple definitions (i.e. or patterns)
+        return None;
     };
 
+    let bind_pat = source.as_ident_pat()?;
+
     let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?;
 
     let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all();
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 52dd670ec2a..1361cdf00cc 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,7 +1,7 @@
 use itertools::Itertools;
 use syntax::{
-    ast::{self, AstNode, AstToken},
-    match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
+    ast::{self, make, AstNode, AstToken},
+    match_ast, ted, NodeOrToken, SyntaxElement, TextRange, TextSize, T,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -12,24 +12,28 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 //
 // ```
 // fn main() {
-//     $0dbg!(92);
+//     let x = $0dbg!(42 * dbg!(4 + 2));$0
 // }
 // ```
 // ->
 // ```
 // fn main() {
-//     92;
+//     let x = 42 * (4 + 2);
 // }
 // ```
 pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let macro_calls = if ctx.has_empty_selection() {
-        vec![ctx.find_node_at_offset::<ast::MacroCall>()?]
+        vec![ctx.find_node_at_offset::<ast::MacroExpr>()?]
     } else {
         ctx.covering_element()
             .as_node()?
             .descendants()
             .filter(|node| ctx.selection_trimmed().contains_range(node.text_range()))
+            // When the selection exactly covers the macro call to be removed, `covering_element()`
+            // returns `ast::MacroCall` instead of its parent `ast::MacroExpr` that we want. So
+            // first try finding `ast::MacroCall`s and then retrieve their parent.
             .filter_map(ast::MacroCall::cast)
+            .filter_map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast))
             .collect()
     };
 
@@ -44,14 +48,25 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
         "Remove dbg!()",
         ctx.selection_trimmed(),
         |builder| {
-            for (range, text) in replacements {
-                builder.replace(range, text);
+            for (range, expr) in replacements {
+                if let Some(expr) = expr {
+                    builder.replace(range, expr.to_string());
+                } else {
+                    builder.delete(range);
+                }
             }
         },
     )
 }
 
-fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> {
+/// Returns `None` when either
+/// - macro call is not `dbg!()`
+/// - any node inside `dbg!()` could not be parsed as an expression
+/// - (`macro_expr` has no parent - is that possible?)
+///
+/// Returns `Some(_, None)` when the macro call should just be removed.
+fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> {
+    let macro_call = macro_expr.macro_call()?;
     let tt = macro_call.token_tree()?;
     let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
     if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
@@ -68,20 +83,19 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
         .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
         .collect::<Option<Vec<ast::Expr>>>()?;
 
-    let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
     let parent = macro_expr.syntax().parent()?;
     Some(match &*input_expressions {
         // dbg!()
         [] => {
             match_ast! {
                 match parent {
-                    ast::StmtList(__) => {
+                    ast::StmtList(_) => {
                         let range = macro_expr.syntax().text_range();
                         let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
                             Some(start) => range.cover_offset(start),
                             None => range,
                         };
-                        (range, String::new())
+                        (range, None)
                     },
                     ast::ExprStmt(it) => {
                         let range = it.syntax().text_range();
@@ -89,19 +103,23 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                             Some(start) => range.cover_offset(start),
                             None => range,
                         };
-                        (range, String::new())
+                        (range, None)
                     },
-                    _ => (macro_call.syntax().text_range(), "()".to_owned())
+                    _ => (macro_call.syntax().text_range(), Some(make::expr_unit())),
                 }
             }
         }
         // dbg!(expr0)
         [expr] => {
+            // dbg!(expr, &parent);
             let wrap = match ast::Expr::cast(parent) {
                 Some(parent) => match (expr, parent) {
                     (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
                     (
-                        ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
+                        ast::Expr::BoxExpr(_)
+                        | ast::Expr::PrefixExpr(_)
+                        | ast::Expr::RefExpr(_)
+                        | ast::Expr::MacroExpr(_),
                         ast::Expr::AwaitExpr(_)
                         | ast::Expr::CallExpr(_)
                         | ast::Expr::CastExpr(_)
@@ -112,7 +130,10 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                         | ast::Expr::TryExpr(_),
                     ) => true,
                     (
-                        ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
+                        ast::Expr::BinExpr(_)
+                        | ast::Expr::CastExpr(_)
+                        | ast::Expr::RangeExpr(_)
+                        | ast::Expr::MacroExpr(_),
                         ast::Expr::AwaitExpr(_)
                         | ast::Expr::BinExpr(_)
                         | ast::Expr::CallExpr(_)
@@ -129,16 +150,61 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str
                 },
                 None => false,
             };
-            (
-                macro_call.syntax().text_range(),
-                if wrap { format!("({expr})") } else { expr.to_string() },
-            )
+            let expr = replace_nested_dbgs(expr.clone());
+            let expr = if wrap { make::expr_paren(expr) } else { expr.clone_subtree() };
+            (macro_call.syntax().text_range(), Some(expr))
         }
         // dbg!(expr0, expr1, ...)
-        exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
+        exprs => {
+            let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
+            let expr = make::expr_tuple(exprs);
+            (macro_call.syntax().text_range(), Some(expr))
+        }
     })
 }
 
+fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
+    if let ast::Expr::MacroExpr(mac) = &expanded {
+        // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree
+        // with `ted`. It should be fairly rare as it means the user wrote `dbg!(dbg!(..))` but you
+        // never know how code ends up being!
+        let replaced = if let Some((_, expr_opt)) = compute_dbg_replacement(mac.clone()) {
+            match expr_opt {
+                Some(expr) => expr,
+                None => {
+                    stdx::never!("dbg! inside dbg! should not be just removed");
+                    expanded
+                }
+            }
+        } else {
+            expanded
+        };
+
+        return replaced;
+    }
+
+    let expanded = expanded.clone_for_update();
+
+    // We need to collect to avoid mutation during traversal.
+    let macro_exprs: Vec<_> =
+        expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
+
+    for mac in macro_exprs {
+        let expr_opt = match compute_dbg_replacement(mac.clone()) {
+            Some((_, expr)) => expr,
+            None => continue,
+        };
+
+        if let Some(expr) = expr_opt {
+            ted::replace(mac.syntax(), expr.syntax().clone_for_update());
+        } else {
+            ted::remove(mac.syntax());
+        }
+    }
+
+    expanded
+}
+
 fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
     Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
 }
@@ -287,4 +353,32 @@ fn f() {
         check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#);
         check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#);
     }
+
+    #[test]
+    fn test_nested_dbg() {
+        check(
+            r#"$0let x = dbg!(dbg!(dbg!(dbg!(0 + 1)) * 2) + dbg!(3));$0"#,
+            r#"let x = ((0 + 1) * 2) + 3;"#,
+        );
+        check(r#"$0dbg!(10, dbg!(), dbg!(20, 30))$0"#, r#"(10, (), (20, 30))"#);
+    }
+
+    #[test]
+    fn test_multiple_nested_dbg() {
+        check(
+            r#"
+fn f() {
+    $0dbg!();
+    let x = dbg!(dbg!(dbg!(0 + 1)) + 2) + dbg!(3);
+    dbg!(10, dbg!(), dbg!(20, 30));$0
+}
+"#,
+            r#"
+fn f() {
+    let x = ((0 + 1) + 2) + 3;
+    (10, (), (20, 30));
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index 457559656a4..5e31d38fbd6 100644
--- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -102,9 +102,11 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
         return None;
     }
 
+    let let_ = if pat_seen { " let" } else { "" };
+
     acc.add(
         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
-        "Replace if let with match",
+        format!("Replace if{let_} with match"),
         available_range,
         move |edit| {
             let match_expr = {
@@ -210,8 +212,17 @@ fn make_else_arm(
 // ```
 pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
+    let match_arm_list = match_expr.match_arm_list()?;
+    let available_range = TextRange::new(
+        match_expr.syntax().text_range().start(),
+        match_arm_list.syntax().text_range().start(),
+    );
+    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
+    if !cursor_in_range {
+        return None;
+    }
 
-    let mut arms = match_expr.match_arm_list()?.arms();
+    let mut arms = match_arm_list.arms();
     let (first_arm, second_arm) = (arms.next()?, arms.next()?);
     if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() {
         return None;
@@ -226,10 +237,20 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
     )?;
     let scrutinee = match_expr.expr()?;
 
+    let let_ = match &if_let_pat {
+        ast::Pat::LiteralPat(p)
+            if p.literal()
+                .map(|it| it.token().kind())
+                .map_or(false, |it| it == T![true] || it == T![false]) =>
+        {
+            ""
+        }
+        _ => " let",
+    };
     let target = match_expr.syntax().text_range();
     acc.add(
         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
-        "Replace match with if let",
+        format!("Replace match with if{let_}"),
         target,
         move |edit| {
             fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
new file mode 100644
index 00000000000..a7e3ed793f1
--- /dev/null
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -0,0 +1,310 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::{
+    ast::{self, make, Expr, HasArgList},
+    AstNode,
+};
+
+use crate::{AssistContext, Assists};
+
+// Assist: replace_with_lazy_method
+//
+// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+//     let a = Some(1);
+//     a.unwra$0p_or(2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+//     let a = Some(1);
+//     a.unwrap_or_else(|| 2);
+// }
+// ```
+pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+    let scope = ctx.sema.scope(call.syntax())?;
+
+    let last_arg = call.arg_list()?.args().next()?;
+    let method_name = call.name_ref()?;
+
+    let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+    let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+    let n_params = callable.n_params() + 1;
+
+    let method_name_lazy = format!(
+        "{method_name}{}",
+        if method_name.text().ends_with("or") { "_else" } else { "_with" }
+    );
+
+    receiver_ty.iterate_method_candidates_with_traits(
+        ctx.sema.db,
+        &scope,
+        &scope.visible_traits().0,
+        None,
+        None,
+        |func| {
+            let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_lazy)
+                && func.num_params(ctx.sema.db) == n_params
+                && {
+                    let params = func.params_without_self(ctx.sema.db);
+                    let last_p = params.first()?;
+                    // FIXME: Check that this has the form of `() -> T` where T is the current type of the argument
+                    last_p.ty().impls_fnonce(ctx.sema.db)
+                };
+            valid.then_some(func)
+        },
+    )?;
+
+    acc.add(
+        AssistId("replace_with_lazy_method", AssistKind::RefactorRewrite),
+        format!("Replace {method_name} with {method_name_lazy}"),
+        call.syntax().text_range(),
+        |builder| {
+            builder.replace(method_name.syntax().text_range(), method_name_lazy);
+            let closured = into_closure(&last_arg);
+            builder.replace_ast(last_arg, closured);
+        },
+    )
+}
+
+fn into_closure(param: &Expr) -> Expr {
+    (|| {
+        if let ast::Expr::CallExpr(call) = param {
+            if call.arg_list()?.args().count() == 0 {
+                Some(call.expr()?)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    })()
+    .unwrap_or_else(|| make::expr_closure(None, param.clone()))
+}
+
+// Assist: replace_with_eager_method
+//
+// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
+//
+// ```
+// # //- minicore:option, fn
+// fn foo() {
+//     let a = Some(1);
+//     a.unwra$0p_or_else(|| 2);
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+//     let a = Some(1);
+//     a.unwrap_or(2);
+// }
+// ```
+pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+    let scope = ctx.sema.scope(call.syntax())?;
+
+    let last_arg = call.arg_list()?.args().next()?;
+    let method_name = call.name_ref()?;
+
+    let callable = ctx.sema.resolve_method_call_as_callable(&call)?;
+    let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
+    let n_params = callable.n_params() + 1;
+    let params = callable.params(ctx.sema.db);
+
+    // FIXME: Check that the arg is of the form `() -> T`
+    if !params.first()?.1.impls_fnonce(ctx.sema.db) {
+        return None;
+    }
+
+    let method_name_text = method_name.text();
+    let method_name_eager = method_name_text
+        .strip_suffix("_else")
+        .or_else(|| method_name_text.strip_suffix("_with"))?;
+
+    receiver_ty.iterate_method_candidates_with_traits(
+        ctx.sema.db,
+        &scope,
+        &scope.visible_traits().0,
+        None,
+        None,
+        |func| {
+            let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_eager)
+                && func.num_params(ctx.sema.db) == n_params;
+            valid.then_some(func)
+        },
+    )?;
+
+    acc.add(
+        AssistId("replace_with_eager_method", AssistKind::RefactorRewrite),
+        format!("Replace {method_name} with {method_name_eager}"),
+        call.syntax().text_range(),
+        |builder| {
+            builder.replace(method_name.syntax().text_range(), method_name_eager);
+            let called = into_call(&last_arg);
+            builder.replace_ast(last_arg, called);
+        },
+    )
+}
+
+fn into_call(param: &Expr) -> Expr {
+    (|| {
+        if let ast::Expr::ClosureExpr(closure) = param {
+            if closure.param_list()?.params().count() == 0 {
+                Some(closure.body()?)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    })()
+    .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_assist;
+
+    use super::*;
+
+    #[test]
+    fn replace_or_with_or_else_simple() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or(2);
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(|| 2);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_with_or_else_call() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or(x());
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(x);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_with_or_else_block() {
+        check_assist(
+            replace_with_lazy_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or({
+        let mut x = bar();
+        for i in 0..10 {
+            x += i;
+        }
+        x
+    });
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or_else(|| {
+        let mut x = bar();
+        for i in 0..10 {
+            x += i;
+        }
+        x
+    });
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_simple() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or_else(|| 2);
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or(2);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_call() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_$0or_else(x);
+}
+
+fn x() -> i32 { 0 }
+"#,
+            r#"
+fn foo() {
+    let foo = Some(1);
+    return foo.unwrap_or(x());
+}
+
+fn x() -> i32 { 0 }
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_or_else_with_or_map() {
+        check_assist(
+            replace_with_eager_method,
+            r#"
+//- minicore: option, fn
+fn foo() {
+    let foo = Some("foo");
+    return foo.map$0_or_else(|| 42, |v| v.len());
+}
+"#,
+            r#"
+fn foo() {
+    let foo = Some("foo");
+    return foo.map_or(42, |v| v.len());
+}
+"#,
+        )
+    }
+}
diff --git a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
deleted file mode 100644
index f0ed3c4fe6f..00000000000
--- a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs
+++ /dev/null
@@ -1,364 +0,0 @@
-use ide_db::{
-    assists::{AssistId, AssistKind},
-    famous_defs::FamousDefs,
-};
-use syntax::{
-    ast::{self, make, Expr, HasArgList},
-    AstNode,
-};
-
-use crate::{AssistContext, Assists};
-
-// Assist: replace_or_with_or_else
-//
-// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-//     let a = Some(1);
-//     a.unwra$0p_or(2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-//     let a = Some(1);
-//     a.unwrap_or_else(|| 2);
-// }
-// ```
-pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
-    let kind = is_option_or_result(call.receiver()?, ctx)?;
-
-    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
-    let mut map_or = false;
-
-    let replace = match &*name.text() {
-        "unwrap_or" => "unwrap_or_else".to_string(),
-        "or" => "or_else".to_string(),
-        "ok_or" if kind == Kind::Option => "ok_or_else".to_string(),
-        "map_or" => {
-            map_or = true;
-            "map_or_else".to_string()
-        }
-        _ => return None,
-    };
-
-    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
-        [] => make::arg_list(Vec::new()),
-        [first] => {
-            let param = into_closure(first);
-            make::arg_list(vec![param])
-        }
-        [first, second] if map_or => {
-            let param = into_closure(first);
-            make::arg_list(vec![param, second.clone()])
-        }
-        _ => return None,
-    };
-
-    acc.add(
-        AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite),
-        format!("Replace {name} with {replace}"),
-        call.syntax().text_range(),
-        |builder| {
-            builder.replace(name.syntax().text_range(), replace);
-            builder.replace_ast(arg_list, arg)
-        },
-    )
-}
-
-fn into_closure(param: &Expr) -> Expr {
-    (|| {
-        if let ast::Expr::CallExpr(call) = param {
-            if call.arg_list()?.args().count() == 0 {
-                Some(call.expr()?)
-            } else {
-                None
-            }
-        } else {
-            None
-        }
-    })()
-    .unwrap_or_else(|| make::expr_closure(None, param.clone()))
-}
-
-// Assist: replace_or_else_with_or
-//
-// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`.
-//
-// ```
-// # //- minicore:option
-// fn foo() {
-//     let a = Some(1);
-//     a.unwra$0p_or_else(|| 2);
-// }
-// ```
-// ->
-// ```
-// fn foo() {
-//     let a = Some(1);
-//     a.unwrap_or(2);
-// }
-// ```
-pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
-
-    let kind = is_option_or_result(call.receiver()?, ctx)?;
-
-    let (name, arg_list) = (call.name_ref()?, call.arg_list()?);
-
-    let mut map_or = false;
-    let replace = match &*name.text() {
-        "unwrap_or_else" => "unwrap_or".to_string(),
-        "or_else" => "or".to_string(),
-        "ok_or_else" if kind == Kind::Option => "ok_or".to_string(),
-        "map_or_else" => {
-            map_or = true;
-            "map_or".to_string()
-        }
-        _ => return None,
-    };
-
-    let arg = match arg_list.args().collect::<Vec<_>>().as_slice() {
-        [] => make::arg_list(Vec::new()),
-        [first] => {
-            let param = into_call(first);
-            make::arg_list(vec![param])
-        }
-        [first, second] if map_or => {
-            let param = into_call(first);
-            make::arg_list(vec![param, second.clone()])
-        }
-        _ => return None,
-    };
-
-    acc.add(
-        AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite),
-        format!("Replace {name} with {replace}"),
-        call.syntax().text_range(),
-        |builder| {
-            builder.replace(name.syntax().text_range(), replace);
-            builder.replace_ast(arg_list, arg)
-        },
-    )
-}
-
-fn into_call(param: &Expr) -> Expr {
-    (|| {
-        if let ast::Expr::ClosureExpr(closure) = param {
-            if closure.param_list()?.params().count() == 0 {
-                Some(closure.body()?)
-            } else {
-                None
-            }
-        } else {
-            None
-        }
-    })()
-    .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())))
-}
-
-#[derive(PartialEq, Eq)]
-enum Kind {
-    Option,
-    Result,
-}
-
-fn is_option_or_result(receiver: Expr, ctx: &AssistContext<'_>) -> Option<Kind> {
-    let ty = ctx.sema.type_of_expr(&receiver)?.adjusted().as_adt()?.as_enum()?;
-    let option_enum =
-        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_option_Option();
-
-    if let Some(option_enum) = option_enum {
-        if ty == option_enum {
-            return Some(Kind::Option);
-        }
-    }
-
-    let result_enum =
-        FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_result_Result();
-
-    if let Some(result_enum) = result_enum {
-        if ty == result_enum {
-            return Some(Kind::Result);
-        }
-    }
-
-    None
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn replace_or_with_or_else_simple() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or(2);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(|| 2);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_with_or_else_call() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or(x());
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(x);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_with_or_else_block() {
-        check_assist(
-            replace_or_with_or_else,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or({
-        let mut x = bar();
-        for i in 0..10 {
-            x += i;
-        }
-        x
-    });
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or_else(|| {
-        let mut x = bar();
-        for i in 0..10 {
-            x += i;
-        }
-        x
-    });
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_simple() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or_else(|| 2);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or(2);
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_call() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: option
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Some(1);
-    return foo.unwrap_or(x());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_result() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: result
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_or(x());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_map() {
-        check_assist(
-            replace_or_else_with_or,
-            r#"
-//- minicore: result
-fn foo() {
-    let foo = Ok("foo");
-    return foo.map$0_or_else(|| 42, |v| v.len());
-}
-"#,
-            r#"
-fn foo() {
-    let foo = Ok("foo");
-    return foo.map_or(42, |v| v.len());
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn replace_or_else_with_or_not_applicable() {
-        check_assist_not_applicable(
-            replace_or_else_with_or,
-            r#"
-fn foo() {
-    let foo = Ok(1);
-    return foo.unwrap_$0or_else(x);
-}
-"#,
-        )
-    }
-}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 276cf5f5dd0..4d489b62b5c 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -188,7 +188,7 @@ mod handlers {
     mod replace_try_expr_with_match;
     mod replace_derive_with_manual_impl;
     mod replace_if_let_with_match;
-    mod replace_or_with_or_else;
+    mod replace_method_eager_lazy;
     mod replace_arith_op;
     mod introduce_named_generic;
     mod replace_let_with_if_let;
@@ -297,8 +297,8 @@ mod handlers {
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
             replace_let_with_if_let::replace_let_with_if_let,
-            replace_or_with_or_else::replace_or_else_with_or,
-            replace_or_with_or_else::replace_or_with_or_else,
+            replace_method_eager_lazy::replace_with_eager_method,
+            replace_method_eager_lazy::replace_with_lazy_method,
             replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
             replace_arith_op::replace_arith_with_wrapping,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 8a25e1f648a..e5a8d675a9e 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2006,12 +2006,12 @@ fn doctest_remove_dbg() {
         "remove_dbg",
         r#####"
 fn main() {
-    $0dbg!(92);
+    let x = $0dbg!(42 * dbg!(4 + 2));$0
 }
 "#####,
         r#####"
 fn main() {
-    92;
+    let x = 42 * (4 + 2);
 }
 "#####,
     )
@@ -2313,46 +2313,6 @@ fn handle(action: Action) {
     )
 }
 
-#[test]
-fn doctest_replace_or_else_with_or() {
-    check_doc_test(
-        "replace_or_else_with_or",
-        r#####"
-//- minicore:option
-fn foo() {
-    let a = Some(1);
-    a.unwra$0p_or_else(|| 2);
-}
-"#####,
-        r#####"
-fn foo() {
-    let a = Some(1);
-    a.unwrap_or(2);
-}
-"#####,
-    )
-}
-
-#[test]
-fn doctest_replace_or_with_or_else() {
-    check_doc_test(
-        "replace_or_with_or_else",
-        r#####"
-//- minicore:option
-fn foo() {
-    let a = Some(1);
-    a.unwra$0p_or(2);
-}
-"#####,
-        r#####"
-fn foo() {
-    let a = Some(1);
-    a.unwrap_or_else(|| 2);
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_qualified_name_with_use() {
     check_doc_test(
@@ -2427,6 +2387,46 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_replace_with_eager_method() {
+    check_doc_test(
+        "replace_with_eager_method",
+        r#####"
+//- minicore:option, fn
+fn foo() {
+    let a = Some(1);
+    a.unwra$0p_or_else(|| 2);
+}
+"#####,
+        r#####"
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or(2);
+}
+"#####,
+    )
+}
+
+#[test]
+fn doctest_replace_with_lazy_method() {
+    check_doc_test(
+        "replace_with_lazy_method",
+        r#####"
+//- minicore:option, fn
+fn foo() {
+    let a = Some(1);
+    a.unwra$0p_or(2);
+}
+"#####,
+        r#####"
+fn foo() {
+    let a = Some(1);
+    a.unwrap_or_else(|| 2);
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_sort_items() {
     check_doc_test(
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 7c6e5e100f6..09ac57153ae 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -122,7 +122,7 @@ fn complete_methods(
     mut f: impl FnMut(hir::Function),
 ) {
     let mut seen_methods = FxHashSet::default();
-    receiver.iterate_method_candidates(
+    receiver.iterate_method_candidates_with_traits(
         ctx.db,
         &ctx.scope,
         &ctx.traits_in_scope(),
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 364969af9c9..0979f6a6dfc 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -5,10 +5,7 @@ use ide_db::imports::{
     insert_use::ImportScope,
 };
 use itertools::Itertools;
-use syntax::{
-    ast::{self},
-    AstNode, SyntaxNode, T,
-};
+use syntax::{ast, AstNode, SyntaxNode, T};
 
 use crate::{
     context::{
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 37849c251a4..69c05a76df4 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -33,7 +33,9 @@ pub(crate) fn complete_type_path(
             // Don't suggest attribute macros and derives.
             ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
             // Type things are fine
-            ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_))
+            ScopeDef::ModuleDef(
+                BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_),
+            )
             | ScopeDef::AdtSelfType(_)
             | ScopeDef::Unknown
             | ScopeDef::GenericParam(TypeParam(_)) => true,
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index ea54068b0f8..7dc29c3d5ac 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -6,13 +6,13 @@ mod tests;
 
 use std::iter;
 
-use base_db::SourceDatabaseExt;
 use hir::{
     HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
 };
 use ide_db::{
     base_db::{FilePosition, SourceDatabase},
     famous_defs::FamousDefs,
+    helpers::is_editable_crate,
     FxHashMap, FxHashSet, RootDatabase,
 };
 use syntax::{
@@ -387,8 +387,7 @@ pub(crate) struct CompletionContext<'a> {
 impl<'a> CompletionContext<'a> {
     /// The range of the identifier that is being completed.
     pub(crate) fn source_range(&self) -> TextRange {
-        // check kind of macro-expanded token, but use range of original token
-        let kind = self.token.kind();
+        let kind = self.original_token.kind();
         match kind {
             CHAR => {
                 // assume we are completing a lifetime but the user has only typed the '
@@ -416,6 +415,7 @@ impl<'a> CompletionContext<'a> {
                 hir::ModuleDef::Const(it) => self.is_visible(it),
                 hir::ModuleDef::Static(it) => self.is_visible(it),
                 hir::ModuleDef::Trait(it) => self.is_visible(it),
+                hir::ModuleDef::TraitAlias(it) => self.is_visible(it),
                 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
                 hir::ModuleDef::Macro(it) => self.is_visible(it),
                 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
@@ -525,10 +525,11 @@ impl<'a> CompletionContext<'a> {
                 return Visible::No;
             }
             // If the definition location is editable, also show private items
-            let root_file = defining_crate.root_file(self.db);
-            let source_root_id = self.db.file_source_root(root_file);
-            let is_editable = !self.db.source_root(source_root_id).is_library;
-            return if is_editable { Visible::Editable } else { Visible::No };
+            return if is_editable_crate(defining_crate, self.db) {
+                Visible::Editable
+            } else {
+                Visible::No
+            };
         }
 
         if self.is_doc_hidden(attrs, defining_crate) {
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 2f65491d85e..bb9fa7ccacc 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -288,7 +288,7 @@ impl_from!(SymbolKind for CompletionItemKind);
 
 impl CompletionItemKind {
     #[cfg(test)]
-    pub(crate) fn tag(&self) -> &'static str {
+    pub(crate) fn tag(self) -> &'static str {
         match self {
             CompletionItemKind::SymbolKind(kind) => match kind {
                 SymbolKind::Attribute => "at",
@@ -312,6 +312,7 @@ impl CompletionItemKind {
                 SymbolKind::Struct => "st",
                 SymbolKind::ToolModule => "tm",
                 SymbolKind::Trait => "tt",
+                SymbolKind::TraitAlias => "tr",
                 SymbolKind::TypeAlias => "ta",
                 SymbolKind::TypeParam => "tp",
                 SymbolKind::Union => "un",
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index d99ad5f9f04..c1f51aabb96 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -367,6 +367,9 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {
         ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
         ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
         ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
+        ScopeDef::ModuleDef(TraitAlias(..)) => {
+            CompletionItemKind::SymbolKind(SymbolKind::TraitAlias)
+        }
         ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),
         ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
         ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index ffcad1185aa..44e88607633 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -264,6 +264,65 @@ macro_rules! foo {
 fn main() {
     foo!($0)
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn complete_missing_macro_arg() {
+        // Regression test for https://github.com/rust-lang/rust-analyzer/issues/14246
+        check_edit(
+            "BAR",
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR, $0)
+}
+"#,
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR, BAR)
+}
+"#,
+        );
+        check_edit(
+            "BAR",
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!($0)
+}
+"#,
+            r#"
+macro_rules! foo {
+    ($val:ident,  $val2: ident) => {
+        $val $val2
+    };
+}
+
+const BAR: u32 = 9;
+fn main() {
+    foo!(BAR)
+}
 "#,
         );
     }
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index 2b6b60547b3..0da4e729a8d 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -96,6 +96,7 @@ pub fn generic_def_for_node(
                     hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
+                    hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
                     hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs
index b1ee9b58d5b..244e99fe2e2 100644
--- a/crates/ide-db/src/apply_change.rs
+++ b/crates/ide-db/src/apply_change.rs
@@ -73,68 +73,96 @@ impl RootDatabase {
 
             // AstDatabase
             hir::db::AstIdMapQuery
+            hir::db::ParseMacroExpansionQuery
+            hir::db::InternMacroCallQuery
             hir::db::MacroArgTextQuery
             hir::db::MacroDefQuery
-            hir::db::ParseMacroExpansionQuery
             hir::db::MacroExpandQuery
+            hir::db::ExpandProcMacroQuery
+            hir::db::MacroExpandErrorQuery
             hir::db::HygieneFrameQuery
-            hir::db::InternMacroCallQuery
 
             // DefDatabase
             hir::db::FileItemTreeQuery
-            hir::db::BlockDefMapQuery
             hir::db::CrateDefMapQueryQuery
-            hir::db::FieldsAttrsQuery
-            hir::db::VariantsAttrsQuery
-            hir::db::FieldsAttrsSourceMapQuery
-            hir::db::VariantsAttrsSourceMapQuery
+            hir::db::BlockDefMapQuery
             hir::db::StructDataQuery
+            hir::db::StructDataWithDiagnosticsQuery
             hir::db::UnionDataQuery
+            hir::db::UnionDataWithDiagnosticsQuery
             hir::db::EnumDataQuery
+            hir::db::EnumDataWithDiagnosticsQuery
             hir::db::ImplDataQuery
+            hir::db::ImplDataWithDiagnosticsQuery
             hir::db::TraitDataQuery
+            hir::db::TraitDataWithDiagnosticsQuery
+            hir::db::TraitAliasDataQuery
             hir::db::TypeAliasDataQuery
             hir::db::FunctionDataQuery
             hir::db::ConstDataQuery
             hir::db::StaticDataQuery
+            hir::db::Macro2DataQuery
+            hir::db::MacroRulesDataQuery
+            hir::db::ProcMacroDataQuery
             hir::db::BodyWithSourceMapQuery
             hir::db::BodyQuery
             hir::db::ExprScopesQuery
             hir::db::GenericParamsQuery
+            hir::db::VariantsAttrsQuery
+            hir::db::FieldsAttrsQuery
+            hir::db::VariantsAttrsSourceMapQuery
+            hir::db::FieldsAttrsSourceMapQuery
             hir::db::AttrsQuery
             hir::db::CrateLangItemsQuery
             hir::db::LangItemQuery
             hir::db::ImportMapQuery
+            hir::db::FieldVisibilitiesQuery
+            hir::db::FunctionVisibilityQuery
+            hir::db::ConstVisibilityQuery
+            hir::db::CrateSupportsNoStdQuery
 
             // HirDatabase
             hir::db::InferQueryQuery
+            hir::db::MirBodyQuery
+            hir::db::BorrowckQuery
             hir::db::TyQuery
             hir::db::ValueTyQuery
             hir::db::ImplSelfTyQuery
+            hir::db::ConstParamTyQuery
+            hir::db::ConstEvalQuery
+            hir::db::ConstEvalDiscriminantQuery
             hir::db::ImplTraitQuery
             hir::db::FieldTypesQuery
+            hir::db::LayoutOfAdtQuery
+            hir::db::TargetDataLayoutQuery
             hir::db::CallableItemSignatureQuery
+            hir::db::ReturnTypeImplTraitsQuery
             hir::db::GenericPredicatesForParamQuery
             hir::db::GenericPredicatesQuery
+            hir::db::TraitEnvironmentQuery
             hir::db::GenericDefaultsQuery
             hir::db::InherentImplsInCrateQuery
-            hir::db::TraitEnvironmentQuery
+            hir::db::InherentImplsInBlockQuery
+            hir::db::IncoherentInherentImplCratesQuery
             hir::db::TraitImplsInCrateQuery
+            hir::db::TraitImplsInBlockQuery
             hir::db::TraitImplsInDepsQuery
-            hir::db::AssociatedTyDataQuery
+            hir::db::InternCallableDefQuery
+            hir::db::InternLifetimeParamIdQuery
+            hir::db::InternImplTraitIdQuery
+            hir::db::InternTypeOrConstParamIdQuery
+            hir::db::InternClosureQuery
+            hir::db::InternGeneratorQuery
             hir::db::AssociatedTyDataQuery
             hir::db::TraitDatumQuery
             hir::db::StructDatumQuery
             hir::db::ImplDatumQuery
             hir::db::FnDefDatumQuery
-            hir::db::ReturnTypeImplTraitsQuery
-            hir::db::InternCallableDefQuery
-            hir::db::InternTypeOrConstParamIdQuery
-            hir::db::InternImplTraitIdQuery
-            hir::db::InternClosureQuery
+            hir::db::FnDefVarianceQuery
+            hir::db::AdtVarianceQuery
             hir::db::AssociatedTyValueQuery
             hir::db::TraitSolveQueryQuery
-            hir::db::InternTypeOrConstParamIdQuery
+            hir::db::ProgramClausesForChalkEnvQuery
 
             // SymbolsDatabase
             crate::symbol_index::ModuleSymbolsQuery
@@ -153,8 +181,14 @@ impl RootDatabase {
             hir::db::InternConstQuery
             hir::db::InternStaticQuery
             hir::db::InternTraitQuery
+            hir::db::InternTraitAliasQuery
             hir::db::InternTypeAliasQuery
             hir::db::InternImplQuery
+            hir::db::InternExternBlockQuery
+            hir::db::InternBlockQuery
+            hir::db::InternMacro2Query
+            hir::db::InternProcMacroQuery
+            hir::db::InternMacroRulesQuery
         ];
 
         acc.sort_by_key(|it| std::cmp::Reverse(it.1));
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index ed7f04fd8e7..1322f5228e8 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -9,7 +9,8 @@ use arrayvec::ArrayVec;
 use hir::{
     Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
     Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
-    Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility,
+    Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
+    Visibility,
 };
 use stdx::impl_from;
 use syntax::{
@@ -31,6 +32,7 @@ pub enum Definition {
     Const(Const),
     Static(Static),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     BuiltinType(BuiltinType),
     SelfType(Impl),
@@ -64,6 +66,7 @@ impl Definition {
             Definition::Const(it) => it.module(db),
             Definition::Static(it) => it.module(db),
             Definition::Trait(it) => it.module(db),
+            Definition::TraitAlias(it) => it.module(db),
             Definition::TypeAlias(it) => it.module(db),
             Definition::Variant(it) => it.module(db),
             Definition::SelfType(it) => it.module(db),
@@ -87,6 +90,7 @@ impl Definition {
             Definition::Const(it) => it.visibility(db),
             Definition::Static(it) => it.visibility(db),
             Definition::Trait(it) => it.visibility(db),
+            Definition::TraitAlias(it) => it.visibility(db),
             Definition::TypeAlias(it) => it.visibility(db),
             Definition::Variant(it) => it.visibility(db),
             Definition::BuiltinType(_) => Visibility::Public,
@@ -113,6 +117,7 @@ impl Definition {
             Definition::Const(it) => it.name(db)?,
             Definition::Static(it) => it.name(db),
             Definition::Trait(it) => it.name(db),
+            Definition::TraitAlias(it) => it.name(db),
             Definition::TypeAlias(it) => it.name(db),
             Definition::BuiltinType(it) => it.name(),
             Definition::SelfType(_) => return None,
@@ -300,6 +305,7 @@ impl NameClass {
                 ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?),
                 ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?),
                 ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?),
+                ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?),
                 ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?),
                 ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
                 ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
@@ -463,9 +469,12 @@ impl NameRefClass {
         match_ast! {
             match parent {
                 ast::MethodCallExpr(method_call) => {
-                    sema.resolve_method_call(&method_call)
-                        .map(Definition::Function)
-                        .map(NameRefClass::Definition)
+                    sema.resolve_method_call_field_fallback(&method_call)
+                        .map(|it| {
+                            it.map_left(Definition::Function)
+                                .map_right(Definition::Field)
+                                .either(NameRefClass::Definition, NameRefClass::Definition)
+                        })
                 },
                 ast::FieldExpr(field_expr) => {
                     sema.resolve_field(&field_expr)
@@ -542,7 +551,7 @@ impl NameRefClass {
 }
 
 impl_from!(
-    Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local,
+    Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local,
     GenericParam, Label, Macro
     for Definition
 );
@@ -599,6 +608,7 @@ impl From<ModuleDef> for Definition {
             ModuleDef::Const(it) => Definition::Const(it),
             ModuleDef::Static(it) => Definition::Static(it),
             ModuleDef::Trait(it) => Definition::Trait(it),
+            ModuleDef::TraitAlias(it) => Definition::TraitAlias(it),
             ModuleDef::TypeAlias(it) => Definition::TypeAlias(it),
             ModuleDef::Macro(it) => Definition::Macro(it),
             ModuleDef::BuiltinType(it) => Definition::BuiltinType(it),
@@ -616,6 +626,7 @@ impl From<Definition> for Option<ItemInNs> {
             Definition::Const(it) => ModuleDef::Const(it),
             Definition::Static(it) => ModuleDef::Static(it),
             Definition::Trait(it) => ModuleDef::Trait(it),
+            Definition::TraitAlias(it) => ModuleDef::TraitAlias(it),
             Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
             Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
             _ => return None,
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs
index 6e56efe344d..8e3b1eef15b 100644
--- a/crates/ide-db/src/helpers.rs
+++ b/crates/ide-db/src/helpers.rs
@@ -2,8 +2,8 @@
 
 use std::collections::VecDeque;
 
-use base_db::FileId;
-use hir::{ItemInNs, ModuleDef, Name, Semantics};
+use base_db::{FileId, SourceDatabaseExt};
+use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
 use syntax::{
     ast::{self, make},
     AstToken, SyntaxKind, SyntaxToken, TokenAtOffset,
@@ -103,3 +103,9 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
         false
     }
 }
+
+pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
+    let root_file = krate.root_file(db);
+    let source_root_id = db.file_source_root(root_file);
+    !db.source_root(source_root_id).is_library
+}
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 994d48385a0..b26b0a9087e 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -528,7 +528,7 @@ fn trait_applicable_items(
             },
         )
     } else {
-        trait_candidate.receiver_ty.iterate_method_candidates(
+        trait_candidate.receiver_ty.iterate_method_candidates_with_traits(
             db,
             scope,
             &trait_candidates,
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 156bbb634e4..ae120470047 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -22,7 +22,7 @@ pub mod source_change;
 pub mod symbol_index;
 pub mod traits;
 pub mod ty_filter;
-pub mod use_trivial_contructor;
+pub mod use_trivial_constructor;
 
 pub mod imports {
     pub mod import_assets;
@@ -191,6 +191,7 @@ pub enum SymbolKind {
     Struct,
     ToolModule,
     Trait,
+    TraitAlias,
     TypeAlias,
     TypeParam,
     Union,
@@ -221,6 +222,7 @@ impl From<FileSymbolKind> for SymbolKind {
             FileSymbolKind::Static => SymbolKind::Static,
             FileSymbolKind::Struct => SymbolKind::Struct,
             FileSymbolKind::Trait => SymbolKind::Trait,
+            FileSymbolKind::TraitAlias => SymbolKind::TraitAlias,
             FileSymbolKind::TypeAlias => SymbolKind::TypeAlias,
             FileSymbolKind::Union => SymbolKind::Union,
         }
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 84d70b258ff..f710211c8cb 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -119,15 +119,9 @@ impl Definition {
             Definition::Const(it) => name_range(it, sema),
             Definition::Static(it) => name_range(it, sema),
             Definition::Trait(it) => name_range(it, sema),
+            Definition::TraitAlias(it) => name_range(it, sema),
             Definition::TypeAlias(it) => name_range(it, sema),
-            Definition::Local(local) => {
-                let src = local.source(sema.db);
-                let name = match &src.value {
-                    Either::Left(bind_pat) => bind_pat.name()?,
-                    Either::Right(_) => return None,
-                };
-                src.with_value(name.syntax()).original_file_range_opt(sema.db)
-            }
+            Definition::Local(it) => name_range(it.primary_source(sema.db), sema),
             Definition::GenericParam(generic_param) => match generic_param {
                 hir::GenericParam::LifetimeParam(lifetime_param) => {
                     let src = lifetime_param.source(sema.db)?;
@@ -301,13 +295,7 @@ fn rename_reference(
         source_change.insert_source_edit(file_id, edit);
         Ok(())
     };
-    match def {
-        Definition::Local(l) => l
-            .associated_locals(sema.db)
-            .iter()
-            .try_for_each(|&local| insert_def_edit(Definition::Local(local))),
-        def => insert_def_edit(def),
-    }?;
+    insert_def_edit(def)?;
     Ok(source_change)
 }
 
@@ -470,59 +458,64 @@ fn source_edit_from_def(
     def: Definition,
     new_name: &str,
 ) -> Result<(FileId, TextEdit)> {
-    let FileRange { file_id, range } = def
-        .range_for_rename(sema)
-        .ok_or_else(|| format_err!("No identifier available to rename"))?;
-
     let mut edit = TextEdit::builder();
     if let Definition::Local(local) = def {
-        if let Either::Left(pat) = local.source(sema.db).value {
-            // special cases required for renaming fields/locals in Record patterns
-            if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
+        let mut file_id = None;
+        for source in local.sources(sema.db) {
+            let source = source.source;
+            file_id = source.file_id.file_id();
+            if let Either::Left(pat) = source.value {
                 let name_range = pat.name().unwrap().syntax().text_range();
-                if let Some(name_ref) = pat_field.name_ref() {
-                    if new_name == name_ref.text() && pat.at_token().is_none() {
-                        // Foo { field: ref mut local } -> Foo { ref mut field }
-                        //       ^^^^^^ delete this
-                        //                      ^^^^^ replace this with `field`
-                        cov_mark::hit!(test_rename_local_put_init_shorthand_pat);
-                        edit.delete(
-                            name_ref
-                                .syntax()
-                                .text_range()
-                                .cover_offset(pat.syntax().text_range().start()),
-                        );
-                        edit.replace(name_range, name_ref.text().to_string());
+                // special cases required for renaming fields/locals in Record patterns
+                if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
+                    if let Some(name_ref) = pat_field.name_ref() {
+                        if new_name == name_ref.text() && pat.at_token().is_none() {
+                            // Foo { field: ref mut local } -> Foo { ref mut field }
+                            //       ^^^^^^ delete this
+                            //                      ^^^^^ replace this with `field`
+                            cov_mark::hit!(test_rename_local_put_init_shorthand_pat);
+                            edit.delete(
+                                name_ref
+                                    .syntax()
+                                    .text_range()
+                                    .cover_offset(pat.syntax().text_range().start()),
+                            );
+                            edit.replace(name_range, name_ref.text().to_string());
+                        } else {
+                            // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
+                            // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
+                            //                      ^^^^^ replace this with `new_name`
+                            edit.replace(name_range, new_name.to_string());
+                        }
                     } else {
-                        // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
-                        // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
-                        //                      ^^^^^ replace this with `new_name`
+                        // Foo { ref mut field } -> Foo { field: ref mut new_name }
+                        //      ^ insert `field: `
+                        //               ^^^^^ replace this with `new_name`
+                        edit.insert(
+                            pat.syntax().text_range().start(),
+                            format!("{}: ", pat_field.field_name().unwrap()),
+                        );
                         edit.replace(name_range, new_name.to_string());
                     }
                 } else {
-                    // Foo { ref mut field } -> Foo { field: ref mut new_name }
-                    //      ^ insert `field: `
-                    //               ^^^^^ replace this with `new_name`
-                    edit.insert(
-                        pat.syntax().text_range().start(),
-                        format!("{}: ", pat_field.field_name().unwrap()),
-                    );
                     edit.replace(name_range, new_name.to_string());
                 }
             }
         }
+        let Some(file_id) = file_id else { bail!("No file available to rename") };
+        return Ok((file_id, edit.finish()));
     }
-    if edit.is_empty() {
-        let (range, new_name) = match def {
-            Definition::GenericParam(hir::GenericParam::LifetimeParam(_))
-            | Definition::Label(_) => (
-                TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
-                new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
-            ),
-            _ => (range, new_name.to_owned()),
-        };
-        edit.replace(range, new_name);
-    }
+    let FileRange { file_id, range } = def
+        .range_for_rename(sema)
+        .ok_or_else(|| format_err!("No identifier available to rename"))?;
+    let (range, new_name) = match def {
+        Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) => (
+            TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
+            new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
+        ),
+        _ => (range, new_name.to_owned()),
+    };
+    edit.replace(range, new_name);
     Ok((file_id, edit.finish()))
 }
 
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index c18a27f17d2..12f5e4e2a23 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -244,14 +244,14 @@ impl Definition {
                 DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
 
         if let Definition::SelfType(impl_) = self {
             return match impl_.source(db).map(|src| src.syntax().cloned()) {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
@@ -261,13 +261,14 @@ impl Definition {
                 hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
+                hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()),
                 hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
             };
             return match def {
-                Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
+                Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
                 None => SearchScope::single_file(file_id),
             };
         }
@@ -318,10 +319,6 @@ impl Definition {
             sema,
             scope: None,
             include_self_kw_refs: None,
-            local_repr: match self {
-                Definition::Local(local) => Some(local.representative(sema.db)),
-                _ => None,
-            },
             search_self_mod: false,
         }
     }
@@ -336,9 +333,6 @@ pub struct FindUsages<'a> {
     assoc_item_container: Option<hir::AssocItemContainer>,
     /// whether to search for the `Self` type of the definition
     include_self_kw_refs: Option<hir::Type>,
-    /// the local representative for the local definition we are searching for
-    /// (this is required for finding all local declarations in a or-pattern)
-    local_repr: Option<hir::Local>,
     /// whether to search for the `self` module
     search_self_mod: bool,
 }
@@ -643,19 +637,6 @@ impl<'a> FindUsages<'a> {
         sink: &mut dyn FnMut(FileId, FileReference) -> bool,
     ) -> bool {
         match NameRefClass::classify(self.sema, name_ref) {
-            Some(NameRefClass::Definition(def @ Definition::Local(local)))
-                if matches!(
-                    self.local_repr, Some(repr) if repr == local.representative(self.sema.db)
-                ) =>
-            {
-                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
-                let reference = FileReference {
-                    range,
-                    name: ast::NameLike::NameRef(name_ref.clone()),
-                    category: ReferenceCategory::new(&def, name_ref),
-                };
-                sink(file_id, reference)
-            }
             Some(NameRefClass::Definition(def))
                 if self.def == def
                     // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait
@@ -700,14 +681,16 @@ impl<'a> FindUsages<'a> {
                 }
             }
             Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
-                let field = Definition::Field(field);
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
+
+                let field = Definition::Field(field);
+                let local = Definition::Local(local);
                 let access = match self.def {
                     Definition::Field(_) if field == self.def => {
                         ReferenceCategory::new(&field, name_ref)
                     }
-                    Definition::Local(_) if matches!(self.local_repr, Some(repr) if repr == local.representative(self.sema.db)) => {
-                        ReferenceCategory::new(&Definition::Local(local), name_ref)
+                    Definition::Local(_) if local == self.def => {
+                        ReferenceCategory::new(&local, name_ref)
                     }
                     _ => return false,
                 };
@@ -751,21 +734,6 @@ impl<'a> FindUsages<'a> {
                 };
                 sink(file_id, reference)
             }
-            Some(NameClass::Definition(def @ Definition::Local(local))) if def != self.def => {
-                if matches!(
-                    self.local_repr,
-                    Some(repr) if local.representative(self.sema.db) == repr
-                ) {
-                    let FileRange { file_id, range } = self.sema.original_range(name.syntax());
-                    let reference = FileReference {
-                        range,
-                        name: ast::NameLike::Name(name.clone()),
-                        category: None,
-                    };
-                    return sink(file_id, reference);
-                }
-                false
-            }
             Some(NameClass::Definition(def)) if def != self.def => {
                 match (&self.assoc_item_container, self.def) {
                     // for type aliases we always want to reference the trait def and all the trait impl counterparts
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 8e338061df4..936354f2961 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -83,6 +83,14 @@ impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
     }
 }
 
+impl FromIterator<(FileId, TextEdit)> for SourceChange {
+    fn from_iter<T: IntoIterator<Item = (FileId, TextEdit)>>(iter: T) -> Self {
+        let mut this = SourceChange::default();
+        this.extend(iter);
+        this
+    }
+}
+
 pub struct SourceChangeBuilder {
     pub edit: TextEditBuilder,
     pub file_id: FileId,
diff --git a/crates/ide-db/src/use_trivial_contructor.rs b/crates/ide-db/src/use_trivial_constructor.rs
similarity index 100%
rename from crates/ide-db/src/use_trivial_contructor.rs
rename to crates/ide-db/src/use_trivial_constructor.rs
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 10e637979f2..114face2dca 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -7,10 +7,15 @@ pub(crate) fn break_outside_of_loop(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::BreakOutsideOfLoop,
 ) -> Diagnostic {
-    let construct = if d.is_break { "break" } else { "continue" };
+    let message = if d.bad_value_break {
+        "can't break with a value in this position".to_owned()
+    } else {
+        let construct = if d.is_break { "break" } else { "continue" };
+        format!("{construct} outside of loop")
+    };
     Diagnostic::new(
         "break-outside-of-loop",
-        format!("{construct} outside of loop"),
+        message,
         ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
     )
 }
@@ -132,6 +137,20 @@ fn foo() {
       //^^^^^^^^^^^ error: continue outside of loop
     }
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn value_break_in_for_loop() {
+        check_diagnostics(
+            r#"
+fn test() {
+    for _ in [()] {
+        break 3;
+     // ^^^^^^^ error: can't break with a value in this position
+    }
+}
 "#,
         );
     }
diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs
new file mode 100644
index 00000000000..d2f27664d6f
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -0,0 +1,39 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: expected-function
+//
+// This diagnostic is triggered if a call is made on something that is not callable.
+pub(crate) fn expected_function(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::ExpectedFunction,
+) -> Diagnostic {
+    Diagnostic::new(
+        "expected-function",
+        format!("expected function, found {}", d.found.display(ctx.sema.db)),
+        ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range,
+    )
+    .experimental()
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    let x = 3;
+    x();
+ // ^^^ error: expected function, found i32
+    ""();
+ // ^^^^ error: expected function, found &str
+    foo();
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 43af4d4f16a..14039087b3f 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -5,7 +5,7 @@ use hir::{
 };
 use ide_db::{
     assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
-    source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap,
+    source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap,
 };
 use stdx::format_to;
 use syntax::{
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index c24430ce604..ac4463331f2 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -1,5 +1,3 @@
-use hir::InFile;
-
 use crate::{Diagnostic, DiagnosticsContext};
 
 // Diagnostic: missing-match-arm
@@ -12,7 +10,7 @@ pub(crate) fn missing_match_arms(
     Diagnostic::new(
         "missing-match-arm",
         format!("missing match arm: {}", d.uncovered_patterns),
-        ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
+        ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range,
     )
 }
 
@@ -1038,7 +1036,6 @@ fn main() {
         #[test]
         fn reference_patterns_in_fields() {
             cov_mark::check_count!(validate_match_bailed_out, 2);
-
             check_diagnostics(
                 r#"
 fn main() {
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
new file mode 100644
index 00000000000..84189a5d560
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -0,0 +1,625 @@
+use ide_db::source_change::SourceChange;
+use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
+use text_edit::TextEdit;
+
+use crate::{fix, Diagnostic, DiagnosticsContext, Severity};
+
+// Diagnostic: need-mut
+//
+// This diagnostic is triggered on mutating an immutable variable.
+pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic {
+    let fixes = (|| {
+        if d.local.is_ref(ctx.sema.db) {
+            // There is no simple way to add `mut` to `ref x` and `ref mut x`
+            return None;
+        }
+        let file_id = d.span.file_id.file_id()?;
+        let mut edit_builder = TextEdit::builder();
+        let use_range = d.span.value.text_range();
+        for source in d.local.sources(ctx.sema.db) {
+            let Some(ast) = source.name() else { continue };
+            edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
+        }
+        let edit = edit_builder.finish();
+        Some(vec![fix(
+            "add_mut",
+            "Change it to be mutable",
+            SourceChange::from_text_edit(file_id, edit),
+            use_range,
+        )])
+    })();
+    Diagnostic::new(
+        "need-mut",
+        format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
+        ctx.sema.diagnostics_display_range(d.span.clone()).range,
+    )
+    .with_fixes(fixes)
+}
+
+// Diagnostic: unused-mut
+//
+// This diagnostic is triggered when a mutable variable isn't actually mutated.
+pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic {
+    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
+    let fixes = (|| {
+        let file_id = ast.file_id.file_id()?;
+        let mut edit_builder = TextEdit::builder();
+        let use_range = ast.value.text_range();
+        for source in d.local.sources(ctx.sema.db) {
+            let ast = source.syntax();
+            let Some(mut_token) = token(ast, T![mut]) else { continue };
+            edit_builder.delete(mut_token.text_range());
+            if let Some(token) = mut_token.next_token() {
+                if token.kind() == SyntaxKind::WHITESPACE {
+                    edit_builder.delete(token.text_range());
+                }
+            }
+        }
+        let edit = edit_builder.finish();
+        Some(vec![fix(
+            "remove_mut",
+            "Remove unnecessary `mut`",
+            SourceChange::from_text_edit(file_id, edit),
+            use_range,
+        )])
+    })();
+    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
+    Diagnostic::new(
+        "unused-mut",
+        "variable does not need to be mutable",
+        ctx.sema.diagnostics_display_range(ast).range,
+    )
+    .severity(Severity::WeakWarning)
+    .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
+    .with_fixes(fixes)
+}
+
+pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
+    parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn unused_mut_simple() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_false_positive_simple() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+    x = 5;
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn multiple_errors_for_single_variable() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    x = 10;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    x = 5;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+    &mut x;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unused_mut_fix() {
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mu$0t x = 2;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    f(x);
+}
+"#,
+        );
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((mu$0t x, _) | (_, mut x)) = (2, 3);
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((x, _) | (_, x)) = (2, 3);
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn need_mut_fix() {
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = 2;
+    x$0 = 5;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = 2;
+    x = 5;
+    f(x);
+}
+"#,
+        );
+        check_fix(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((x, _) | (_, x)) = (2, 3);
+    x =$0 4;
+    f(x);
+}
+"#,
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let ((mut x, _) | (_, mut x)) = (2, 3);
+    x = 4;
+    f(x);
+}
+"#,
+        );
+
+        check_fix(
+            r#"
+struct Foo(i32);
+
+impl Foo {
+    fn foo(self) {
+        self = Fo$0o(5);
+    }
+}
+"#,
+            r#"
+struct Foo(i32);
+
+impl Foo {
+    fn foo(mut self) {
+        self = Foo(5);
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn need_mut_fix_not_applicable_on_ref() {
+        check_diagnostics(
+            r#"
+fn main() {
+    let ref x = 2;
+    x = &5;
+  //^^^^^^ error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let ref mut x = 2;
+    x = &mut 5;
+  //^^^^^^^^^^ error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn field_mutate() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = (2, 7);
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x.1);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x = (2, 7);
+    x.0 = 5;
+    f(x.1);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x = (2, 7);
+    x.0 = 5;
+  //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x.1);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn mutable_reference() {
+        check_diagnostics(
+            r#"
+fn main() {
+    let mut x = &mut 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    *x = 5;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let x = 2;
+    &mut x;
+  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    let x_own = 2;
+    let ref mut x_ref = x_own;
+      //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+struct Foo;
+impl Foo {
+    fn method(&mut self, x: i32) {}
+}
+fn main() {
+    let x = Foo;
+    x.method(2);
+  //^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn regression_14310() {
+        check_diagnostics(
+            r#"
+            fn clone(mut i: &!) -> ! {
+                   //^^^^^ 💡 weak: variable does not need to be mutable
+                *i
+            }
+        "#,
+        );
+    }
+
+    #[test]
+    fn match_bindings() {
+        check_diagnostics(
+            r#"
+fn main() {
+    match (2, 3) {
+        (x, mut y) => {
+          //^^^^^ 💡 weak: variable does not need to be mutable
+            x = 7;
+          //^^^^^ 💡 error: cannot mutate immutable variable `x`
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn mutation_in_dead_code() {
+        // This one is interesting. Dead code is not represented at all in the MIR, so
+        // there would be no mutablility error for locals in dead code. Rustc tries to
+        // not emit `unused_mut` in this case, but since it works without `mut`, and
+        // special casing it is not trivial, we emit it.
+        check_diagnostics(
+            r#"
+fn main() {
+    return;
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main() {
+    loop {}
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+enum X {}
+fn g() -> X {
+    loop {}
+}
+fn f() -> ! {
+    loop {}
+}
+fn main(b: bool) {
+    if b {
+        f();
+    } else {
+        g();
+    }
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn main(b: bool) {
+    if b {
+        loop {}
+    } else {
+        return;
+    }
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    &mut x;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn initialization_is_not_mutation() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut x;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    x = 5;
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main(b: bool) {
+    let mut x;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    if b {
+        x = 1;
+    } else {
+        x = 3;
+    }
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main(b: bool) {
+    let x;
+    if b {
+        x = 1;
+    }
+    x = 3;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+    f(x);
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let x;
+    loop {
+        x = 1;
+      //^^^^^ 💡 error: cannot mutate immutable variable `x`
+        f(x);
+    }
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    loop {
+        let mut x = 1;
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        f(x);
+        if let mut y = 2 {
+             //^^^^^ 💡 weak: variable does not need to be mutable
+            f(y);
+        }
+        match 3 {
+            mut z => f(z),
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn function_arguments_are_initialized() {
+        check_diagnostics(
+            r#"
+fn f(mut x: i32) {
+   //^^^^^ 💡 weak: variable does not need to be mutable
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+fn f(x: i32) {
+   x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn for_loop() {
+        check_diagnostics(
+            r#"
+//- minicore: iterators
+fn f(x: [(i32, u8); 10]) {
+    for (a, mut b) in x {
+          //^^^^^ 💡 weak: variable does not need to be mutable
+        a = 2;
+      //^^^^^ 💡 error: cannot mutate immutable variable `a`
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn overloaded_deref() {
+        // FIXME: check for false negative
+        check_diagnostics(
+            r#"
+//- minicore: deref_mut
+use core::ops::{Deref, DerefMut};
+
+struct Foo;
+impl Deref for Foo {
+    type Target = i32;
+    fn deref(&self) -> &i32 {
+        &5
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut i32 {
+        &mut 5
+    }
+}
+fn f() {
+    let x = Foo;
+    let y = &*x;
+    let x = Foo;
+    let mut x = Foo;
+    let y: &mut i32 = &mut x;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn or_pattern() {
+        check_diagnostics(
+            r#"
+//- minicore: option
+fn f(_: i32) {}
+fn main() {
+    let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7));
+             //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn or_pattern_no_terminator() {
+        check_diagnostics(
+            r#"
+enum Foo {
+    A, B, C, D
+}
+
+use Foo::*;
+
+fn f(inp: (Foo, Foo, Foo, Foo)) {
+    let ((A, B, _, x) | (B, C | D, x, _)) = inp else {
+        return;
+    };
+    x = B;
+  //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn respect_allow_unused_mut() {
+        // FIXME: respect
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    #[allow(unused_mut)]
+    let mut x = 2;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    f(x);
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index 0b3121c765d..67da5c7f27d 100644
--- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -115,6 +115,44 @@ mod module {
 fn main(s: module::Struct) {
     s.method();
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn can_see_through_top_level_anonymous_const() {
+        // regression test for #14046.
+        check_diagnostics(
+            r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) fn method(self) {}
+            pub(crate) const A: usize = 42;
+        }
+    };
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(crate) fn method2(self) {}
+                pub(crate) const B: usize = 42;
+                pub(super) fn private(self) {}
+                pub(super) const PRIVATE: usize = 42;
+            }
+        };
+    }
+}
+fn main() {
+    S.method();
+    S::A;
+    S.method2();
+    S::B;
+    S.private();
+  //^^^^^^^^^^^ error: function `private` is private
+    S::PRIVATE;
+  //^^^^^^^^^^ error: const `PRIVATE` is private
+}
 "#,
         );
     }
diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 9826e1c707e..a0c276cc332 100644
--- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -55,7 +55,18 @@ fn fixes(
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::{check_diagnostics, check_fix};
+    use crate::{
+        tests::{check_diagnostics_with_config, check_fix},
+        DiagnosticsConfig,
+    };
+
+    #[track_caller]
+    pub(crate) fn check_diagnostics(ra_fixture: &str) {
+        let mut config = DiagnosticsConfig::test_sample();
+        config.disabled.insert("inactive-code".to_string());
+        config.disabled.insert("unresolved-method".to_string());
+        check_diagnostics_with_config(config, ra_fixture)
+    }
 
     #[test]
     fn replace_filter_map_next_with_find_map2() {
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 2adae165e4d..b57a13e53e6 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,8 +1,9 @@
-use hir::{db::AstDatabase, HirDisplay, Type};
+use either::Either;
+use hir::{db::AstDatabase, HirDisplay, InFile, Type};
 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
 use syntax::{
     ast::{self, BlockExpr, ExprStmt},
-    AstNode,
+    AstNode, AstPtr,
 };
 use text_edit::TextEdit;
 
@@ -10,19 +11,23 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}
 
 // Diagnostic: type-mismatch
 //
-// This diagnostic is triggered when the type of an expression does not match
+// This diagnostic is triggered when the type of an expression or pattern does not match
 // the expected type.
 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
-    let display_range = adjusted_display_range::<ast::BlockExpr>(
-        ctx,
-        d.expr.clone().map(|it| it.into()),
-        &|block| {
-            let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
-            cov_mark::hit!(type_mismatch_on_block);
-            Some(r_curly_range)
-        },
-    );
-
+    let display_range = match &d.expr_or_pat {
+        Either::Left(expr) => adjusted_display_range::<ast::BlockExpr>(
+            ctx,
+            expr.clone().map(|it| it.into()),
+            &|block| {
+                let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
+                cov_mark::hit!(type_mismatch_on_block);
+                Some(r_curly_range)
+            },
+        ),
+        Either::Right(pat) => {
+            ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
+        }
+    };
     let mut diag = Diagnostic::new(
         "type-mismatch",
         format!(
@@ -42,10 +47,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
 
-    add_reference(ctx, d, &mut fixes);
-    add_missing_ok_or_some(ctx, d, &mut fixes);
-    remove_semicolon(ctx, d, &mut fixes);
-    str_ref_to_owned(ctx, d, &mut fixes);
+    match &d.expr_or_pat {
+        Either::Left(expr_ptr) => {
+            add_reference(ctx, d, expr_ptr, &mut fixes);
+            add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
+            remove_semicolon(ctx, d, expr_ptr, &mut fixes);
+            str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
+        }
+        Either::Right(_pat_ptr) => {}
+    }
 
     if fixes.is_empty() {
         None
@@ -57,9 +67,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assi
 fn add_reference(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
+    let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range;
 
     let (_, mutability) = d.expected.as_reference()?;
     let actual_with_ref = Type::reference(&d.actual, mutability);
@@ -71,7 +82,7 @@ fn add_reference(
 
     let edit = TextEdit::insert(range.start(), ampersands);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
     acc.push(fix("add_reference_here", "Add reference here", source_change, range));
     Some(())
 }
@@ -79,10 +90,11 @@ fn add_reference(
 fn add_missing_ok_or_some(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     let expr_range = expr.syntax().text_range();
     let scope = ctx.sema.scope(expr.syntax())?;
 
@@ -109,7 +121,7 @@ fn add_missing_ok_or_some(
     builder.insert(expr.syntax().text_range().start(), format!("{variant_name}("));
     builder.insert(expr.syntax().text_range().end(), ")".to_string());
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), builder.finish());
     let name = format!("Wrap in {variant_name}");
     acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
     Some(())
@@ -118,10 +130,11 @@ fn add_missing_ok_or_some(
 fn remove_semicolon(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     if !d.actual.is_unit() {
         return None;
     }
@@ -136,7 +149,7 @@ fn remove_semicolon(
 
     let edit = TextEdit::delete(semicolon_range);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
 
     acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
     Some(())
@@ -145,24 +158,26 @@ fn remove_semicolon(
 fn str_ref_to_owned(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::TypeMismatch,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
     let expected = d.expected.display(ctx.sema.db);
     let actual = d.actual.display(ctx.sema.db);
 
+    // FIXME do this properly
     if expected.to_string() != "String" || actual.to_string() != "&str" {
         return None;
     }
 
-    let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
-    let expr = d.expr.value.to_node(&root);
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
     let expr_range = expr.syntax().text_range();
 
     let to_owned = format!(".to_owned()");
 
     let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned);
     let source_change =
-        SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+        SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
     acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range));
 
     Some(())
@@ -592,6 +607,21 @@ fn f() -> i32 {
     let _ = x + y;
   }
 //^ error: expected i32, found ()
+"#,
+        );
+    }
+
+    #[test]
+    fn type_mismatch_pat_smoke_test() {
+        check_diagnostics(
+            r#"
+fn f() {
+    let &() = &mut ();
+    match &() {
+        &9 => ()
+       //^ error: expected (), found i32
+    }
+}
 "#,
         );
     }
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
new file mode 100644
index 00000000000..7de03416e56
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -0,0 +1,148 @@
+use hir::{db::AstDatabase, HirDisplay, InFile};
+use ide_db::{
+    assists::{Assist, AssistId, AssistKind},
+    base_db::FileRange,
+    label::Label,
+    source_change::SourceChange,
+};
+use syntax::{ast, AstNode, AstPtr};
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unresolved-field
+//
+// This diagnostic is triggered if a field does not exist on a given type.
+pub(crate) fn unresolved_field(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedField,
+) -> Diagnostic {
+    let method_suffix = if d.method_with_same_name_exists {
+        ", but a method with a similar name exists"
+    } else {
+        ""
+    };
+    Diagnostic::new(
+        "unresolved-field",
+        format!(
+            "no field `{}` on type `{}`{method_suffix}",
+            d.name,
+            d.receiver.display(ctx.sema.db)
+        ),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+    .experimental()
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
+    if d.method_with_same_name_exists {
+        method_fix(ctx, &d.expr)
+    } else {
+        // FIXME: add quickfix
+
+        None
+    }
+}
+
+// FIXME: We should fill out the call here, mvoe the cursor and trigger signature help
+fn method_fix(
+    ctx: &DiagnosticsContext<'_>,
+    expr_ptr: &InFile<AstPtr<ast::Expr>>,
+) -> Option<Vec<Assist>> {
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
+    let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
+    Some(vec![Assist {
+        id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix),
+        label: Label::new("Use parentheses to call the method".to_string()),
+        group: None,
+        target: range,
+        source_change: Some(SourceChange::from_text_edit(
+            file_id,
+            TextEdit::insert(range.end(), "()".to_owned()),
+        )),
+        trigger_signature_help: false,
+    }])
+}
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn main() {
+    ().foo;
+ // ^^^^^^ error: no field `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_clash() {
+        check_diagnostics(
+            r#"
+struct Foo;
+impl Foo {
+    fn bar(&self) {}
+}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_trait_() {
+        check_diagnostics(
+            r#"
+struct Foo;
+trait Bar {
+    fn bar(&self) {}
+}
+impl Bar for Foo {}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn method_trait_2() {
+        check_diagnostics(
+            r#"
+struct Foo;
+trait Bar {
+    fn bar(&self);
+}
+impl Bar for Foo {
+    fn bar(&self) {}
+}
+fn foo() {
+    Foo.bar;
+ // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_diagnostic_on_unknown() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    x.foo;
+    (&x).foo;
+    (&((x,),),).foo;
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
new file mode 100644
index 00000000000..4b0e64cb896
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -0,0 +1,131 @@
+use hir::{db::AstDatabase, HirDisplay};
+use ide_db::{
+    assists::{Assist, AssistId, AssistKind},
+    base_db::FileRange,
+    label::Label,
+    source_change::SourceChange,
+};
+use syntax::{ast, AstNode, TextRange};
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unresolved-method
+//
+// This diagnostic is triggered if a method does not exist on a given type.
+pub(crate) fn unresolved_method(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedMethodCall,
+) -> Diagnostic {
+    let field_suffix = if d.field_with_same_name.is_some() {
+        ", but a field with a similar name exists"
+    } else {
+        ""
+    };
+    Diagnostic::new(
+        "unresolved-method",
+        format!(
+            "no method `{}` on type `{}`{field_suffix}",
+            d.name,
+            d.receiver.display(ctx.sema.db)
+        ),
+        ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+    )
+    .with_fixes(fixes(ctx, d))
+    .experimental()
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Vec<Assist>> {
+    if let Some(ty) = &d.field_with_same_name {
+        field_fix(ctx, d, ty)
+    } else {
+        // FIXME: add quickfix
+        None
+    }
+}
+
+fn field_fix(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedMethodCall,
+    ty: &hir::Type,
+) -> Option<Vec<Assist>> {
+    if !ty.impls_fnonce(ctx.sema.db) {
+        return None;
+    }
+    let expr_ptr = &d.expr;
+    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+    let expr = expr_ptr.value.to_node(&root);
+    let (file_id, range) = match expr {
+        ast::Expr::MethodCallExpr(mcall) => {
+            let FileRange { range, file_id } =
+                ctx.sema.original_range_opt(mcall.receiver()?.syntax())?;
+            let FileRange { range: range2, file_id: file_id2 } =
+                ctx.sema.original_range_opt(mcall.name_ref()?.syntax())?;
+            if file_id != file_id2 {
+                return None;
+            }
+            (file_id, TextRange::new(range.start(), range2.end()))
+        }
+        _ => return None,
+    };
+    Some(vec![Assist {
+        id: AssistId("expected-method-found-field-fix", AssistKind::QuickFix),
+        label: Label::new("Use parentheses to call the value of the field".to_string()),
+        group: None,
+        target: range,
+        source_change: Some(SourceChange::from_iter([
+            (file_id, TextEdit::insert(range.start(), "(".to_owned())),
+            (file_id, TextEdit::insert(range.end(), ")".to_owned())),
+        ])),
+        trigger_signature_help: false,
+    }])
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_fix};
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn main() {
+    ().foo();
+ // ^^^^^^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn field() {
+        check_diagnostics(
+            r#"
+struct Foo { bar: i32 }
+fn foo() {
+    Foo { bar: i32 }.bar();
+ // ^^^^^^^^^^^^^^^^^^^^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn callable_field() {
+        check_fix(
+            r#"
+//- minicore: fn
+struct Foo { bar: fn() }
+fn foo() {
+    Foo { bar: foo }.b$0ar();
+}
+"#,
+            r#"
+struct Foo { bar: fn() }
+fn foo() {
+    (Foo { bar: foo }.bar)();
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 64ba08ac883..f6c9b79c30c 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -27,6 +27,7 @@
 
 mod handlers {
     pub(crate) mod break_outside_of_loop;
+    pub(crate) mod expected_function;
     pub(crate) mod inactive_code;
     pub(crate) mod incorrect_case;
     pub(crate) mod invalid_derive_target;
@@ -36,6 +37,7 @@ mod handlers {
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
     pub(crate) mod missing_unsafe;
+    pub(crate) mod mutability_errors;
     pub(crate) mod no_such_field;
     pub(crate) mod private_assoc_item;
     pub(crate) mod private_field;
@@ -43,6 +45,8 @@ mod handlers {
     pub(crate) mod type_mismatch;
     pub(crate) mod unimplemented_builtin_macro;
     pub(crate) mod unresolved_extern_crate;
+    pub(crate) mod unresolved_field;
+    pub(crate) mod unresolved_method;
     pub(crate) mod unresolved_import;
     pub(crate) mod unresolved_macro_call;
     pub(crate) mod unresolved_module;
@@ -248,6 +252,7 @@ pub fn diagnostics(
         #[rustfmt::skip]
         let d = match diag {
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
+            AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
@@ -267,7 +272,10 @@ pub fn diagnostics(
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
             AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
-
+            AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
+            AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
+            AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
+            AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
             AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                 Some(it) => it,
                 None => continue,
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index 57b5ab6abda..a8e88369088 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -561,7 +561,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
                             .sema
                             .resolve_method_call_as_callable(code)
                             .and_then(|callable| callable.receiver_param(self.sema.db))
-                            .map(|self_param| self_param.kind())
+                            .map(|(self_param, _)| self_param.kind())
                             .unwrap_or(ast::SelfParamKind::Owned);
                     }
                 }
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index b4a7f2b918a..fae25f31023 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -107,7 +107,18 @@ pub(crate) fn remove_links(markdown: &str) -> String {
     out
 }
 
-/// Retrieve a link to documentation for the given symbol.
+// Feature: Open Docs
+//
+// Retrieve a link to documentation for the given symbol.
+//
+// The simplest way to use this feature is via the context menu. Right-click on
+// the selected item. The context menu opens. Select **Open Docs**.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: Open Docs**
+// |===
 pub(crate) fn external_docs(
     db: &RootDatabase,
     position: &FilePosition,
@@ -181,6 +192,7 @@ pub(crate) fn resolve_doc_path_for_def(
         Definition::Const(it) => it.resolve_doc_path(db, link, ns),
         Definition::Static(it) => it.resolve_doc_path(db, link, ns),
         Definition::Trait(it) => it.resolve_doc_path(db, link, ns),
+        Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns),
         Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
         Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, link, ns),
@@ -493,6 +505,7 @@ fn filename_and_frag_for_def(
             None => String::from("index.html"),
         },
         Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
+        Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
         Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
         Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
         Definition::Function(f) => format!("fn.{}.html", f.name(db)),
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index b23763dce86..a32ac35496a 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -149,6 +149,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
             ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
             ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
             ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
+            ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)),
             ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
             ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
             ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
@@ -262,6 +263,8 @@ enum E { X, Y(i32) }
 type T = ();
 static S: i32 = 92;
 const C: i32 = 92;
+trait Tr {}
+trait Alias = Tr;
 
 impl E {}
 
@@ -457,11 +460,33 @@ fn g() {}
                         ),
                         deprecated: false,
                     },
+                    StructureNode {
+                        parent: None,
+                        label: "Tr",
+                        navigation_range: 239..241,
+                        node_range: 233..244,
+                        kind: SymbolKind(
+                            Trait,
+                        ),
+                        detail: None,
+                        deprecated: false,
+                    },
+                    StructureNode {
+                        parent: None,
+                        label: "Alias",
+                        navigation_range: 251..256,
+                        node_range: 245..262,
+                        kind: SymbolKind(
+                            TraitAlias,
+                        ),
+                        detail: None,
+                        deprecated: false,
+                    },
                     StructureNode {
                         parent: None,
                         label: "impl E",
-                        navigation_range: 239..240,
-                        node_range: 234..243,
+                        navigation_range: 269..270,
+                        node_range: 264..273,
                         kind: SymbolKind(
                             Impl,
                         ),
@@ -471,8 +496,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "impl fmt::Debug for E",
-                        navigation_range: 265..266,
-                        node_range: 245..269,
+                        navigation_range: 295..296,
+                        node_range: 275..299,
                         kind: SymbolKind(
                             Impl,
                         ),
@@ -482,8 +507,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mc",
-                        navigation_range: 284..286,
-                        node_range: 271..303,
+                        navigation_range: 314..316,
+                        node_range: 301..333,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -493,8 +518,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mcexp",
-                        navigation_range: 334..339,
-                        node_range: 305..356,
+                        navigation_range: 364..369,
+                        node_range: 335..386,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -504,8 +529,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "mcexp",
-                        navigation_range: 387..392,
-                        node_range: 358..409,
+                        navigation_range: 417..422,
+                        node_range: 388..439,
                         kind: SymbolKind(
                             Macro,
                         ),
@@ -515,8 +540,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "obsolete",
-                        navigation_range: 428..436,
-                        node_range: 411..441,
+                        navigation_range: 458..466,
+                        node_range: 441..471,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -528,8 +553,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "very_obsolete",
-                        navigation_range: 481..494,
-                        node_range: 443..499,
+                        navigation_range: 511..524,
+                        node_range: 473..529,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -541,8 +566,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "Some region name",
-                        navigation_range: 501..528,
-                        node_range: 501..528,
+                        navigation_range: 531..558,
+                        node_range: 531..558,
                         kind: Region,
                         detail: None,
                         deprecated: false,
@@ -550,8 +575,8 @@ fn g() {}
                     StructureNode {
                         parent: None,
                         label: "m",
-                        navigation_range: 568..569,
-                        node_range: 543..606,
+                        navigation_range: 598..599,
+                        node_range: 573..636,
                         kind: SymbolKind(
                             Module,
                         ),
@@ -560,22 +585,22 @@ fn g() {}
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "dontpanic",
-                        navigation_range: 543..563,
-                        node_range: 543..563,
+                        navigation_range: 573..593,
+                        node_range: 573..593,
                         kind: Region,
                         detail: None,
                         deprecated: false,
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "f",
-                        navigation_range: 575..576,
-                        node_range: 572..581,
+                        navigation_range: 605..606,
+                        node_range: 602..611,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -586,11 +611,11 @@ fn g() {}
                     },
                     StructureNode {
                         parent: Some(
-                            20,
+                            22,
                         ),
                         label: "g",
-                        navigation_range: 598..599,
-                        node_range: 582..604,
+                        navigation_range: 628..629,
+                        node_range: 612..634,
                         kind: SymbolKind(
                             Function,
                         ),
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 93019527f44..cf0819a2524 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -764,6 +764,13 @@ trait Foo$0 { }
 "#,
         );
 
+        check(
+            r#"
+trait Foo$0 = ;
+    //^^^
+"#,
+        );
+
         check(
             r#"
 mod bar$0 { }
@@ -1065,6 +1072,23 @@ fn f() -> impl Sub<Item$0 = u8> {}
         );
     }
 
+    #[test]
+    fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() {
+        check(
+            r#"
+mod bar {
+    pub struct Foo {}
+             //^^^
+    pub fn Foo() {}
+}
+
+fn baz() {
+    let _foo_enum: bar::Foo$0 = bar::Foo {};
+}
+        "#,
+        )
+    }
+
     #[test]
     fn unknown_assoc_ty() {
         check_unresolved(
@@ -1406,7 +1430,6 @@ include!("included.rs$0");
         );
     }
 
-    #[cfg(test)]
     mod goto_impl_of_trait_fn {
         use super::check;
         #[test]
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index c889eb930f3..d88ffd25c40 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -14,7 +14,7 @@ use syntax::{
     SyntaxNode, SyntaxToken, TextRange, T,
 };
 
-use crate::{references, NavigationTarget, TryToNav};
+use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
 
 #[derive(PartialEq, Eq, Hash)]
 pub struct HighlightedRange {
@@ -98,32 +98,39 @@ fn highlight_references(
             category: access,
         });
     let mut res = FxHashSet::default();
-
-    let mut def_to_hl_range = |def| {
-        let hl_range = match def {
-            Definition::Module(module) => {
-                Some(NavigationTarget::from_module_to_decl(sema.db, module))
-            }
-            def => def.try_to_nav(sema.db),
-        }
-        .filter(|decl| decl.file_id == file_id)
-        .and_then(|decl| decl.focus_range)
-        .map(|range| {
-            let category =
-                references::decl_mutability(&def, node, range).then_some(ReferenceCategory::Write);
-            HighlightedRange { range, category }
-        });
-        if let Some(hl_range) = hl_range {
-            res.insert(hl_range);
-        }
-    };
     for &def in &defs {
         match def {
-            Definition::Local(local) => local
-                .associated_locals(sema.db)
-                .iter()
-                .for_each(|&local| def_to_hl_range(Definition::Local(local))),
-            def => def_to_hl_range(def),
+            Definition::Local(local) => {
+                let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+                local
+                    .sources(sema.db)
+                    .into_iter()
+                    .map(|x| x.to_nav(sema.db))
+                    .filter(|decl| decl.file_id == file_id)
+                    .filter_map(|decl| decl.focus_range)
+                    .map(|range| HighlightedRange { range, category })
+                    .for_each(|x| {
+                        res.insert(x);
+                    });
+            }
+            def => {
+                let hl_range = match def {
+                    Definition::Module(module) => {
+                        Some(NavigationTarget::from_module_to_decl(sema.db, module))
+                    }
+                    def => def.try_to_nav(sema.db),
+                }
+                .filter(|decl| decl.file_id == file_id)
+                .and_then(|decl| decl.focus_range)
+                .map(|range| {
+                    let category = references::decl_mutability(&def, node, range)
+                        .then_some(ReferenceCategory::Write);
+                    HighlightedRange { range, category }
+                });
+                if let Some(hl_range) = hl_range {
+                    res.insert(hl_range);
+                }
+            }
         }
     }
 
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5f2c61f5b5f..64b2221bdea 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -30,6 +30,7 @@ pub struct HoverConfig {
     pub documentation: bool,
     pub keywords: bool,
     pub format: HoverDocFormat,
+    pub interpret_tests: bool,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 22611cfb892..da725ce502b 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -3,7 +3,8 @@ use std::fmt::Display;
 
 use either::Either;
 use hir::{
-    Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
+    db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
+    MirEvalError, Semantics, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -402,7 +403,20 @@ pub(super) fn definition(
             ))
         }),
         Definition::Module(it) => label_and_docs(db, it),
-        Definition::Function(it) => label_and_docs(db, it),
+        Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
+            if !config.interpret_tests {
+                return None;
+            }
+            match it.eval(db) {
+                Ok(()) => Some("pass".into()),
+                Err(MirEvalError::Panic) => Some("fail".into()),
+                Err(MirEvalError::MirLowerError(f, e)) => {
+                    let name = &db.function_data(f).name;
+                    Some(format!("error: fail to lower {name} due {e:?}"))
+                }
+                Err(e) => Some(format!("error: {e:?}")),
+            }
+        }),
         Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
             let layout = it.layout(db).ok()?;
             Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
@@ -410,7 +424,7 @@ pub(super) fn definition(
         Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
             if !it.parent_enum(db).is_data_carrying(db) {
                 match it.eval(db) {
-                    Ok(x) => Some(format!("{x}")),
+                    Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
                     Err(_) => it.value(db).map(|x| format!("{x:?}")),
                 }
             } else {
@@ -418,9 +432,9 @@ pub(super) fn definition(
             }
         }),
         Definition::Const(it) => label_value_and_docs(db, it, |it| {
-            let body = it.eval(db);
+            let body = it.render_eval(db);
             match body {
-                Ok(x) => Some(format!("{x}")),
+                Ok(x) => Some(x),
                 Err(_) => {
                     let source = it.source(db)?;
                     let mut body = source.value.body()?.syntax().clone();
@@ -440,6 +454,7 @@ pub(super) fn definition(
             Some(body.to_string())
         }),
         Definition::Trait(it) => label_and_docs(db, it),
+        Definition::TraitAlias(it) => label_and_docs(db, it),
         Definition::TypeAlias(it) => label_and_docs(db, it),
         Definition::BuiltinType(it) => {
             return famous_defs
@@ -620,8 +635,8 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
     let ty = it.ty(db);
     let ty = ty.display_truncated(db, None);
     let is_mut = if it.is_mut(db) { "mut " } else { "" };
-    let desc = match it.source(db).value {
-        Either::Left(ident) => {
+    let desc = match it.primary_source(db).into_ident_pat() {
+        Some(ident) => {
             let name = it.name(db);
             let let_kw = if ident
                 .syntax()
@@ -634,7 +649,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
             };
             format!("{let_kw}{is_mut}{name}: {ty}")
         }
-        Either::Right(_) => format!("{is_mut}self: {ty}"),
+        None => format!("{is_mut}self: {ty}"),
     };
     markup(None, desc, None)
 }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index bd7ce2f1d0d..57bf0f9ad5f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -4,16 +4,19 @@ use syntax::TextRange;
 
 use crate::{fixture, HoverConfig, HoverDocFormat};
 
+const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
+    links_in_hover: false,
+    documentation: true,
+    format: HoverDocFormat::Markdown,
+    keywords: true,
+    interpret_tests: false,
+};
+
 fn check_hover_no_result(ra_fixture: &str) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap();
@@ -25,12 +28,7 @@ fn check(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap()
@@ -47,12 +45,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HOVER_BASE_CONFIG,
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
         .unwrap()
@@ -71,9 +64,8 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
         .hover(
             &HoverConfig {
                 links_in_hover: true,
-                documentation: true,
-                keywords: true,
                 format: HoverDocFormat::PlainText,
+                ..HOVER_BASE_CONFIG
             },
             FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
         )
@@ -91,12 +83,7 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
     let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
     let hover = analysis
         .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
+            &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
             FileRange { file_id, range: position.range_or_empty() },
         )
         .unwrap()
@@ -106,34 +93,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
 
 fn check_hover_range(ra_fixture: &str, expect: Expect) {
     let (analysis, range) = fixture::range(ra_fixture);
-    let hover = analysis
-        .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
-            range,
-        )
-        .unwrap()
-        .unwrap();
+    let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap();
     expect.assert_eq(hover.info.markup.as_str())
 }
 
 fn check_hover_range_no_results(ra_fixture: &str) {
     let (analysis, range) = fixture::range(ra_fixture);
-    let hover = analysis
-        .hover(
-            &HoverConfig {
-                links_in_hover: false,
-                documentation: true,
-                keywords: true,
-                format: HoverDocFormat::Markdown,
-            },
-            range,
-        )
-        .unwrap();
+    let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
     assert!(hover.is_none());
 }
 
@@ -490,7 +456,6 @@ fn hover_field_offset() {
     // Hovering over the field when instantiating
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
 "#,
         expect![[r#"
@@ -512,7 +477,6 @@ fn hover_shows_struct_field_info() {
     // Hovering over the field when instantiating
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { field_a: u32 }
 
 fn main() {
@@ -535,7 +499,6 @@ fn main() {
     // Hovering over the field in the definition
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
 struct Foo { field_a$0: u32 }
 
 fn main() {
@@ -610,6 +573,27 @@ const foo$0: u32 = {
     );
 }
 
+#[test]
+fn hover_eval_complex_constants() {
+    check(
+        r#"
+        struct X { f1: (), f2: i32 }
+        const foo$0: (i8, X, i64) = (1, X { f2: 5 - 1, f1: () }, 1 - 2);
+        "#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const foo: (i8, X, i64) = (1, X { f1: (), f2: 4 }, -1)
+            ```
+        "#]],
+    );
+}
+
 #[test]
 fn hover_default_generic_types() {
     check(
@@ -1467,8 +1451,6 @@ fn my() {}
 fn test_hover_struct_doc_comment() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 /// This is an example
 /// multiline doc
 ///
@@ -1527,7 +1509,7 @@ fn foo() { let bar = Ba$0r; }
             ```
 
             ```rust
-            struct Bar
+            struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1556,7 +1538,7 @@ fn foo() { let bar = Ba$0r; }
             ```
 
             ```rust
-            struct Bar
+            struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1584,7 +1566,7 @@ pub struct B$0ar
             ```
 
             ```rust
-            pub struct Bar
+            pub struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -1611,7 +1593,7 @@ pub struct B$0ar
             ```
 
             ```rust
-            pub struct Bar
+            pub struct Bar // size = 0, align = 1
             ```
 
             ---
@@ -2913,8 +2895,6 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
 fn hover_field_pat_shorthand_ref_match_ergonomics() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 struct S {
     f: i32,
 }
@@ -3506,8 +3486,8 @@ impl<const LEN: usize> Foo<LEN$0> {}
 }
 
 #[test]
-fn hover_const_eval_variant() {
-    // show hex for <10
+fn hover_const_eval_discriminant() {
+    // Don't show hex for <10
     check(
         r#"
 #[repr(u8)]
@@ -3532,7 +3512,7 @@ enum E {
             This is a doc
         "#]],
     );
-    // show hex for >10
+    // Show hex for >10
     check(
         r#"
 #[repr(u8)]
@@ -3656,7 +3636,7 @@ trait T {
 }
 impl T for i32 {
     const AA: A = A {
-        i: 2
+        i: 2 + 3
     }
 }
 fn main() {
@@ -3671,9 +3651,7 @@ fn main() {
         ```
 
         ```rust
-        const AA: A = A {
-                i: 2
-            }
+        const AA: A = A { i: 5 }
         ```
     "#]],
     );
@@ -3792,7 +3770,6 @@ const FOO$0: usize = 1 << 3;
             This is a doc
         "#]],
     );
-    // show hex for >10
     check(
         r#"
 /// This is a doc
@@ -3850,7 +3827,7 @@ const FOO$0: i32 = 2 - 3;
             ```
 
             ```rust
-            const FOO: i32 = -1
+            const FOO: i32 = -1 (0xFFFFFFFF)
             ```
 
             ---
@@ -4011,6 +3988,28 @@ const FOO$0: f32 = 1f32;
             This is a doc
         "#]],
     );
+    // Don't show `<ref-not-supported>` in const hover
+    check(
+        r#"
+/// This is a doc
+const FOO$0: &i32 = &2;
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: &i32 = &2
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
     //show f64 typecasted from float
     check(
         r#"
@@ -4354,8 +4353,6 @@ fn main() {
 fn hover_intra_doc_links() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 pub mod theitem {
     /// This is the item. Cool!
     pub struct TheItem;
@@ -4496,7 +4493,7 @@ trait A where
 fn string_shadowed_with_inner_items() {
     check(
         r#"
-//- /main.rs crate:main deps:alloc target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
+//- /main.rs crate:main deps:alloc
 
 /// Custom `String` type.
 struct String;
@@ -5191,7 +5188,7 @@ foo_macro!(
             ```
 
             ```rust
-            pub struct Foo
+            pub struct Foo // size = 0, align = 1
             ```
 
             ---
@@ -5205,8 +5202,6 @@ foo_macro!(
 fn hover_intra_in_attr() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 #[doc = "Doc comment for [`Foo$0`]"]
 pub struct Foo(i32);
 "#,
@@ -5295,7 +5290,7 @@ pub struct Type;
             ```
 
             ```rust
-            const KONST: dep::Type = $crate::Type
+            const KONST: dep::Type = Type
             ```
         "#]],
     );
@@ -5327,8 +5322,6 @@ enum Enum {
 fn hover_record_variant_field() {
     check(
         r#"
-//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128
-
 enum Enum {
     RecordV { field$0: u32 }
 }
@@ -5647,3 +5640,204 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn assoc_fn_in_block_local_impl() {
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) fn foo() {}
+        }
+    };
+}
+fn test() {
+    S::foo$0();
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(crate) fn foo()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        const _: () = {
+            impl crate::S {
+                pub(crate) fn foo() {}
+            }
+        };
+    };
+}
+fn test() {
+    S::foo$0();
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(crate) fn foo()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(super) fn foo() {}
+            }
+        };
+    }
+
+    fn test() {
+        crate::S::foo$0();
+    }
+}
+"#,
+        expect![[r#"
+            *foo*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            pub(super) fn foo()
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn assoc_const_in_block_local_impl() {
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        impl crate::S {
+            pub(crate) const A: () = ();
+        }
+    };
+}
+fn test() {
+    S::A$0;
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(crate) const A: () = ()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    const _: () = {
+        const _: () = {
+            impl crate::S {
+                pub(crate) const A: () = ();
+            }
+        };
+    };
+}
+fn test() {
+    S::A$0;
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(crate) const A: () = ()
+            ```
+        "#]],
+    );
+
+    check(
+        r#"
+struct S;
+mod m {
+    mod inner {
+        const _: () = {
+            impl crate::S {
+                pub(super) const A: () = ();
+            }
+        };
+    }
+
+    fn test() {
+        crate::S::A$0;
+    }
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            pub(super) const A: () = ()
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn field_as_method_call_fallback() {
+    check(
+        r#"
+struct S { f: u32 }
+fn test() {
+    S { f: 0 }.f$0();
+}
+"#,
+        expect![[r#"
+            *f*
+
+            ```rust
+            test::S
+            ```
+
+            ```rust
+            f: u32 // size = 4, align = 4, offset = 0
+            ```
+        "#]],
+    );
+}
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 188eb7f977b..729780fa0c9 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -606,14 +606,13 @@ fn a() {
     }
 
     #[test]
-    fn bug() {
+    fn let_stmt_explicit_ty() {
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
 fn main() {
-    // These should be identical, but they are not...
-
     let () = return;
+           //^^^^^^<never-to-any>
     let (): () = return;
                //^^^^^^<never-to-any>
 }
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 4af7f9bdb73..6a50927333d 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -176,15 +176,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
 mod tests {
     // This module also contains tests for super::closure_ret
 
-    use expect_test::expect;
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
     use crate::{fixture, inlay_hints::InlayHintsConfig};
 
-    use crate::inlay_hints::tests::{
-        check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
-    };
+    use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
     use crate::ClosureReturnTypeHints;
 
     #[track_caller]
@@ -278,8 +275,7 @@ fn main() {
     #[test]
     fn iterator_hint_regression_issue_12674() {
         // Ensure we don't crash while solving the projection type of iterators.
-        check_expect(
-            InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+        let (analysis, file_id) = fixture::file(
             r#"
 //- minicore: iterators
 struct S<T>(T);
@@ -302,107 +298,18 @@ impl<'a, T> Iterator for SliceIter<'a, T> {
 
 fn main(a: SliceIter<'_, Container>) {
     a
-    .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
-    .map(|e| e);
+        .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
+        .map(|e| e);
 }
-            "#,
-            expect![[r#"
-                [
-                    InlayHint {
-                        range: 484..554,
-                        kind: Chaining,
-                        label: [
-                            "impl ",
-                            InlayHintLabelPart {
-                                text: "Iterator",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2611..2619,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Item",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2643..2647,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            " = impl ",
-                            InlayHintLabelPart {
-                                text: "Iterator",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2611..2619,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Item",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            1,
-                                        ),
-                                        range: 2643..2647,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            " = &&str>>",
-                        ],
-                    },
-                    InlayHint {
-                        range: 484..485,
-                        kind: Chaining,
-                        label: [
-                            "",
-                            InlayHintLabelPart {
-                                text: "SliceIter",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            0,
-                                        ),
-                                        range: 289..298,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            "<",
-                            InlayHintLabelPart {
-                                text: "Container",
-                                linked_location: Some(
-                                    FileRange {
-                                        file_id: FileId(
-                                            0,
-                                        ),
-                                        range: 238..247,
-                                    },
-                                ),
-                                tooltip: "",
-                            },
-                            ">",
-                        ],
-                    },
-                ]
-            "#]],
+"#,
         );
+        analysis
+            .inlay_hints(
+                &InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+                file_id,
+                None,
+            )
+            .unwrap();
     }
 
     #[test]
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 0c54f084c19..0a7513e465a 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -435,7 +435,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -448,7 +448,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
@@ -468,7 +468,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -481,7 +481,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
@@ -501,7 +501,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2611..2619,
+                                        range: 3386..3394,
                                     },
                                 ),
                                 tooltip: "",
@@ -514,7 +514,7 @@ fn main() {
                                         file_id: FileId(
                                             1,
                                         ),
-                                        range: 2643..2647,
+                                        range: 3418..3422,
                                     },
                                 ),
                                 tooltip: "",
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index 5dd51ad11f4..67eaa553ada 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -59,8 +59,14 @@ fn variant_hints(
         },
         kind: InlayKind::Discriminant,
         label: InlayHintLabel::simple(
-            match &d {
-                Ok(v) => format!("{}", v),
+            match d {
+                Ok(x) => {
+                    if x >= 10 {
+                        format!("{x} ({x:#X})")
+                    } else {
+                        format!("{x}")
+                    }
+                }
                 Err(_) => "?".into(),
             },
             Some(InlayTooltip::String(match &d {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f2b535bdc7e..078b66dd395 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -55,6 +55,7 @@ mod syntax_tree;
 mod typing;
 mod view_crate_graph;
 mod view_hir;
+mod view_mir;
 mod view_item_tree;
 mod shuffle_crate_graph;
 
@@ -308,6 +309,10 @@ impl Analysis {
         self.with_db(|db| view_hir::view_hir(db, position))
     }
 
+    pub fn view_mir(&self, position: FilePosition) -> Cancellable<String> {
+        self.with_db(|db| view_mir::view_mir(db, position))
+    }
+
     pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
         self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
     }
diff --git a/crates/ide/src/markup.rs b/crates/ide/src/markup.rs
index de9fef61a78..411eb695fbf 100644
--- a/crates/ide/src/markup.rs
+++ b/crates/ide/src/markup.rs
@@ -32,7 +32,7 @@ impl Markup {
     pub fn as_str(&self) -> &str {
         self.text.as_str()
     }
-    pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
+    pub fn fenced_block(contents: impl fmt::Display) -> Markup {
         format!("```rust\n{contents}\n```").into()
     }
 }
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index af5e96d2381..349e79ecfdd 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -208,6 +208,9 @@ pub(crate) fn def_to_moniker(
         Definition::Trait(trait_) => {
             MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
         }
+        Definition::TraitAlias(ta) => {
+            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
+        }
         Definition::TypeAlias(ta) => {
             MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
         }
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index ffc4bdd7da3..b955ea99f0c 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -73,6 +73,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
         SyntaxKind::MACRO_CALL,
         SyntaxKind::TYPE_ALIAS,
         SyntaxKind::TRAIT,
+        SyntaxKind::TRAIT_ALIAS,
         SyntaxKind::IMPL,
         SyntaxKind::MACRO_DEF,
         SyntaxKind::STRUCT,
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 3aa799d43a8..6aae82f9816 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -5,7 +5,7 @@ use std::fmt;
 use either::Either;
 use hir::{
     symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
-    InFile, ModuleSource, Semantics,
+    InFile, LocalSource, ModuleSource, Semantics,
 };
 use ide_db::{
     base_db::{FileId, FileRange},
@@ -192,6 +192,7 @@ impl TryToNav for Definition {
             Definition::Const(it) => it.try_to_nav(db),
             Definition::Static(it) => it.try_to_nav(db),
             Definition::Trait(it) => it.try_to_nav(db),
+            Definition::TraitAlias(it) => it.try_to_nav(db),
             Definition::TypeAlias(it) => it.try_to_nav(db),
             Definition::BuiltinType(_) => None,
             Definition::ToolModule(_) => None,
@@ -212,6 +213,7 @@ impl TryToNav for hir::ModuleDef {
             hir::ModuleDef::Const(it) => it.try_to_nav(db),
             hir::ModuleDef::Static(it) => it.try_to_nav(db),
             hir::ModuleDef::Trait(it) => it.try_to_nav(db),
+            hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db),
             hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
             hir::ModuleDef::Macro(it) => it.try_to_nav(db),
             hir::ModuleDef::BuiltinType(_) => None,
@@ -249,6 +251,9 @@ impl ToNavFromAst for hir::TypeAlias {
 impl ToNavFromAst for hir::Trait {
     const KIND: SymbolKind = SymbolKind::Trait;
 }
+impl ToNavFromAst for hir::TraitAlias {
+    const KIND: SymbolKind = SymbolKind::TraitAlias;
+}
 
 impl<D> TryToNav for D
 where
@@ -382,9 +387,11 @@ impl TryToNav for hir::GenericParam {
     }
 }
 
-impl ToNav for hir::Local {
+impl ToNav for LocalSource {
     fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
-        let InFile { file_id, value } = self.source(db);
+        let InFile { file_id, value } = &self.source;
+        let file_id = *file_id;
+        let local = self.local;
         let (node, name) = match &value {
             Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
             Either::Right(it) => (it.syntax(), it.name()),
@@ -393,10 +400,10 @@ impl ToNav for hir::Local {
         let FileRange { file_id, range: full_range } =
             InFile::new(file_id, node).original_file_range(db);
 
-        let name = self.name(db).to_smol_str();
-        let kind = if self.is_self(db) {
+        let name = local.name(db).to_smol_str();
+        let kind = if local.is_self(db) {
             SymbolKind::SelfParam
-        } else if self.is_param(db) {
+        } else if local.is_param(db) {
             SymbolKind::ValueParam
         } else {
             SymbolKind::Local
@@ -414,6 +421,12 @@ impl ToNav for hir::Local {
     }
 }
 
+impl ToNav for hir::Local {
+    fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
+        self.primary_source(db).to_nav(db)
+    }
+}
+
 impl ToNav for hir::Label {
     fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
         let InFile { file_id, value } = self.source(db);
@@ -544,6 +557,7 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
             ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
+            ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
             ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index cabbc287279..3684c1033f3 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1355,6 +1355,38 @@ impl Foo {
         );
     }
 
+    #[test]
+    fn test_trait_alias() {
+        check(
+            r#"
+trait Foo {}
+trait Bar$0 = Foo where Self: ;
+fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
+"#,
+            expect![[r#"
+                Bar TraitAlias FileId(0) 13..42 19..22
+
+                FileId(0) 53..56
+                FileId(0) 66..69
+                FileId(0) 79..82
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_trait_alias_self() {
+        check(
+            r#"
+trait Foo = where Self$0: ;
+"#,
+            expect![[r#"
+                Self TypeParam FileId(0) 6..9 6..9
+
+                FileId(0) 18..22
+            "#]],
+        );
+    }
+
     #[test]
     fn test_attr_differs_from_fn_with_same_name() {
         check(
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index c0237e1edd0..e10c4638102 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -353,6 +353,11 @@ mod tests {
     fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let (analysis, position) = fixture::position(ra_fixture_before);
+        if !ra_fixture_after.starts_with("error: ") {
+            if let Err(err) = analysis.prepare_rename(position).unwrap() {
+                panic!("Prepare rename to '{new_name}' was failed: {err}")
+            }
+        }
         let rename_result = analysis
             .rename(position, new_name)
             .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
@@ -1709,6 +1714,23 @@ fn foo(bar: i32) -> Foo {
         );
     }
 
+    #[test]
+    fn test_rename_local_simple() {
+        check(
+            "i",
+            r#"
+fn foo(bar$0: i32) -> i32 {
+    bar
+}
+"#,
+            r#"
+fn foo(i: i32) -> i32 {
+    i
+}
+"#,
+        );
+    }
+
     #[test]
     fn test_rename_local_put_init_shorthand() {
         cov_mark::check!(test_rename_local_put_init_shorthand);
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 5b35262aabe..8a8a9151c42 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,7 +2,7 @@ use std::fmt;
 
 use ast::HasName;
 use cfg::CfgExpr;
-use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
+use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
 use ide_assists::utils::test_related_attribute;
 use ide_db::{
     base_db::{FilePosition, FileRange},
@@ -195,14 +195,13 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
 //
 // Provides a sneak peek of all tests where the current item is used.
 //
-// The simplest way to use this feature is via the context menu:
-//  - Right-click on the selected item. The context menu opens.
-//  - Select **Peek related tests**
+// The simplest way to use this feature is via the context menu. Right-click on
+// the selected item. The context menu opens. Select **Peek Related Tests**.
 //
 // |===
 // | Editor  | Action Name
 //
-// | VS Code | **rust-analyzer: Peek related tests**
+// | VS Code | **rust-analyzer: Peek Related Tests**
 // |===
 pub(crate) fn related_tests(
     db: &RootDatabase,
@@ -371,9 +370,9 @@ pub(crate) fn runnable_impl(
     let nav = def.try_to_nav(sema.db)?;
     let ty = def.self_ty(sema.db);
     let adt_name = ty.as_adt()?.name(sema.db);
-    let mut ty_args = ty.type_arguments().peekable();
+    let mut ty_args = ty.generic_parameters(sema.db).peekable();
     let params = if ty_args.peek().is_some() {
-        format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
+        format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
     } else {
         String::new()
     };
@@ -417,6 +416,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
         Definition::Const(it) => it.attrs(db),
         Definition::Static(it) => it.attrs(db),
         Definition::Trait(it) => it.attrs(db),
+        Definition::TraitAlias(it) => it.attrs(db),
         Definition::TypeAlias(it) => it.attrs(db),
         Definition::Macro(it) => it.attrs(db),
         Definition::SelfType(it) => it.attrs(db),
@@ -437,14 +437,10 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
                 let ty = imp.self_ty(db);
                 if let Some(adt) = ty.as_adt() {
                     let name = adt.name(db);
-                    let mut ty_args = ty.type_arguments().peekable();
+                    let mut ty_args = ty.generic_parameters(db).peekable();
                     format_to!(path, "{}", name);
                     if ty_args.peek().is_some() {
-                        format_to!(
-                            path,
-                            "<{}>",
-                            ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
-                        );
+                        format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
                     }
                     format_to!(path, "::{}", def_name);
                     path.retain(|c| c != ' ');
@@ -1000,6 +996,221 @@ impl Data {
         );
     }
 
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a>;
+impl Data<'a> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 52..106,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, T, U>;
+impl<T, U> Data<'a, T, U> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 70..124,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,T,U>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_const() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<const N: usize>;
+impl<const N: usize> Data<N> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 79..133,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<N>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, T, const N: usize>;
+impl<'a, T, const N: usize> Data<'a, T, N> {
+    /// ```
+    /// let x = 5;
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 100..154,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,T,N>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
     #[test]
     fn test_runnables_module() {
         check(
@@ -2062,6 +2273,59 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {
+        check(
+            r#"
+//- /lib.rs
+$0
+fn main() {}
+
+struct Data<'a, A, const B: usize, C, const D: u32>;
+impl<A, C, const D: u32> Data<'a, A, 12, C, D> {
+    /// ```
+    /// ```
+    fn foo() {}
+}
+"#,
+            &[Bin, DocTest],
+            expect![[r#"
+                [
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 1..13,
+                            focus_range: 4..8,
+                            name: "main",
+                            kind: Function,
+                        },
+                        kind: Bin,
+                        cfg: None,
+                    },
+                    Runnable {
+                        use_name_in_title: false,
+                        nav: NavigationTarget {
+                            file_id: FileId(
+                                0,
+                            ),
+                            full_range: 121..156,
+                            name: "foo",
+                        },
+                        kind: DocTest {
+                            test_id: Path(
+                                "Data<'a,A,12,C,D>::foo",
+                            ),
+                        },
+                        cfg: None,
+                    },
+                ]
+            "#]],
+        );
+    }
+
     #[test]
     fn doc_test_type_params() {
         check(
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index f70ca55a508..2c08c457b33 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -172,7 +172,7 @@ fn signature_help_for_call(
 
     res.signature.push('(');
     {
-        if let Some(self_param) = callable.receiver_param(db) {
+        if let Some((self_param, _)) = callable.receiver_param(db) {
             format_to!(res.signature, "{}", self_param)
         }
         let mut buf = String::new();
@@ -252,6 +252,10 @@ fn signature_help_for_generics(
             res.doc = it.docs(db).map(|it| it.into());
             format_to!(res.signature, "trait {}", it.name(db));
         }
+        hir::GenericDef::TraitAlias(it) => {
+            res.doc = it.docs(db).map(|it| it.into());
+            format_to!(res.signature, "trait {}", it.name(db));
+        }
         hir::GenericDef::TypeAlias(it) => {
             res.doc = it.docs(db).map(|it| it.into());
             format_to!(res.signature, "type {}", it.name(db));
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 3f7f6885f61..c97691b14a5 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -139,6 +139,7 @@ impl StaticIndex<'_> {
             documentation: true,
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
+            interpret_tests: false,
         };
         let tokens = tokens.filter(|token| {
             matches!(
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 892e6a9bb0a..2111baad74d 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -217,7 +217,9 @@ fn highlight_name_ref(
         // to anything when used.
         // We can fix this for derive attributes since derive helpers are recorded, but not for
         // general attributes.
-        None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) => {
+        None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR)
+            && !sema.hir_file_for(name_ref.syntax()).is_derive_attr_pseudo_expansion(sema.db) =>
+        {
             return HlTag::Symbol(SymbolKind::Attribute).into();
         }
         None => return HlTag::UnresolvedReference.into(),
@@ -410,6 +412,7 @@ fn highlight_def(
             h
         }
         Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)),
+        Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)),
         Definition::TypeAlias(type_) => {
             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
 
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 9139528c7ed..3c4cfc78152 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -274,6 +274,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
         Definition::Const(_) => SymbolKind::Const,
         Definition::Static(_) => SymbolKind::Static,
         Definition::Trait(_) => SymbolKind::Trait,
+        Definition::TraitAlias(_) => SymbolKind::TraitAlias,
         Definition::TypeAlias(_) => SymbolKind::TypeAlias,
         Definition::BuiltinType(_) => return HlTag::BuiltinType,
         Definition::Macro(_) => SymbolKind::Macro,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 3949f1189bd..a81c4ee0cbd 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -150,6 +150,7 @@ impl HlTag {
                 SymbolKind::Struct => "struct",
                 SymbolKind::ToolModule => "tool_module",
                 SymbolKind::Trait => "trait",
+                SymbolKind::TraitAlias => "trait_alias",
                 SymbolKind::TypeAlias => "type_alias",
                 SymbolKind::TypeParam => "type_param",
                 SymbolKind::Union => "union",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index 1a439881476..567ab8ccc11 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -53,6 +53,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment">// This is another normal comment</span>
 <span class="comment documentation">/// This is another doc comment</span>
 <span class="comment">// This is another normal comment</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Copy</span><span class="comma attribute">,</span> <span class="unresolved_reference attribute">Unresolved</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
 <span class="comment">// The reason for these being here is to test AttrIds</span>
 <span class="keyword">struct</span> <span class="struct declaration">Foo</span><span class="semicolon">;</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index fc9b5d3ba4c..ac9bd8e39dc 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -34,7 +34,7 @@ fn attributes() {
 // This is another normal comment
 /// This is another doc comment
 // This is another normal comment
-#[derive(Copy)]
+#[derive(Copy, Unresolved)]
 // The reason for these being here is to test AttrIds
 struct Foo;
 "#,
diff --git a/crates/ide/src/view_mir.rs b/crates/ide/src/view_mir.rs
new file mode 100644
index 00000000000..a36aba58bc0
--- /dev/null
+++ b/crates/ide/src/view_mir.rs
@@ -0,0 +1,29 @@
+use hir::{DefWithBody, Semantics};
+use ide_db::base_db::FilePosition;
+use ide_db::RootDatabase;
+use syntax::{algo::find_node_at_offset, ast, AstNode};
+
+// Feature: View Mir
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: View Mir**
+// |===
+pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
+    body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_string())
+}
+
+fn body_mir(db: &RootDatabase, position: FilePosition) -> Option<String> {
+    let sema = Semantics::new(db);
+    let source_file = sema.parse(position.file_id);
+
+    let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
+    let def: DefWithBody = match item {
+        ast::Item::Fn(it) => sema.to_def(&it)?.into(),
+        ast::Item::Const(it) => sema.to_def(&it)?.into(),
+        ast::Item::Static(it) => sema.to_def(&it)?.into(),
+        _ => return None,
+    };
+    Some(def.debug_mir(db))
+}
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 15ec9e167e0..15435a26cea 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -198,6 +198,10 @@ impl BlockLike {
     fn is_block(self) -> bool {
         self == BlockLike::Block
     }
+
+    fn is_blocklike(kind: SyntaxKind) -> bool {
+        matches!(kind, BLOCK_EXPR | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR)
+    }
 }
 
 const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 4ecaa6e6a85..c13a1943792 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -43,7 +43,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
     match p.current() {
         T![=] => {
             p.bump(T![=]);
-            if !expressions::expr(p) {
+            if expressions::expr(p).is_none() {
                 p.error("expected expression");
             }
         }
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 4b080102a2c..a884d8b6ec8 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -16,9 +16,9 @@ pub(super) enum Semicolon {
 
 const EXPR_FIRST: TokenSet = LHS_FIRST;
 
-pub(super) fn expr(p: &mut Parser<'_>) -> bool {
+pub(super) fn expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
     let r = Restrictions { forbid_structs: false, prefer_stmt: false };
-    expr_bp(p, None, r, 1).is_some()
+    expr_bp(p, None, r, 1).map(|(m, _)| m)
 }
 
 pub(super) fn expr_stmt(
@@ -120,16 +120,27 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
             // fn f() { let x: i32; }
             types::ascription(p);
         }
+
+        let mut expr_after_eq: Option<CompletedMarker> = None;
         if p.eat(T![=]) {
             // test let_stmt_init
             // fn f() { let x = 92; }
-            expressions::expr(p);
+            expr_after_eq = expressions::expr(p);
         }
 
         if p.at(T![else]) {
+            // test_err let_else_right_curly_brace
+            // fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
+            if let Some(expr) = expr_after_eq {
+                if BlockLike::is_blocklike(expr.kind()) {
+                    p.error(
+                        "right curly brace `}` before `else` in a `let...else` statement not allowed",
+                    )
+                }
+            }
+
             // test let_else
             // fn f() { let Some(x) = opt else { return }; }
-
             let m = p.start();
             p.bump(T![else]);
             block_expr(p);
@@ -578,7 +589,14 @@ fn arg_list(p: &mut Parser<'_>) {
     // fn main() {
     //     foo(#[attr] 92)
     // }
-    delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr);
+    delimited(
+        p,
+        T!['('],
+        T![')'],
+        T![,],
+        EXPR_FIRST.union(ATTRIBUTE_FIRST),
+        |p: &mut Parser<'_>| expr(p).is_some(),
+    );
     m.complete(p, ARG_LIST);
 }
 
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index efc2603835e..d051dd2682f 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -163,10 +163,8 @@ pub(super) fn atom_expr(
             return None;
         }
     };
-    let blocklike = match done.kind() {
-        IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block,
-        _ => BlockLike::NotBlock,
-    };
+    let blocklike =
+        if BlockLike::is_blocklike(done.kind()) { BlockLike::Block } else { BlockLike::NotBlock };
     Some((done, blocklike))
 }
 
@@ -188,7 +186,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
 
         // test tuple_attrs
         // const A: (i64, i64) = (1, #[cfg(test)] 2);
-        if !expr(p) {
+        if expr(p).is_none() {
             break;
         }
 
@@ -221,7 +219,7 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker {
 
         // test array_attrs
         // const A: &[i64] = &[1, #[cfg(test)] 2];
-        if !expr(p) {
+        if expr(p).is_none() {
             break;
         }
 
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index c982e2d564c..a8a1ccb15e6 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -20,7 +20,7 @@ pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) {
         // trait Z<U> = where Self: T<U>;
         generic_params::opt_where_clause(p);
         p.expect(T![;]);
-        m.complete(p, TRAIT);
+        m.complete(p, TRAIT_ALIAS);
         return;
     }
 
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 1064ae9970c..26490aa97e0 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -77,6 +77,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
     // type X = <A as B>::Output;
     // fn foo() { <usize as Default>::default(); }
     if first && p.eat(T![<]) {
+        // test_err angled_path_without_qual
+        // type X = <()>;
+        // type Y = <A as B>;
         types::type_(p);
         if p.eat(T![as]) {
             if is_use_path_start(p) {
@@ -86,6 +89,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
             }
         }
         p.expect(T![>]);
+        if !p.at(T![::]) {
+            p.error("expected `::`");
+        }
     } else {
         let empty = if first {
             p.eat(T![::]);
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 52b3fc23d59..cd87b304a2f 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -135,6 +135,7 @@ pub enum SyntaxKind {
     STATIC,
     CONST,
     TRAIT,
+    TRAIT_ALIAS,
     IMPL,
     TYPE_ALIAS,
     MACRO_CALL,
diff --git a/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast b/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast
new file mode 100644
index 00000000000..026fecf4c9d
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          FOR_EXPR
+            FOR_KW "for"
+            WHITESPACE " "
+            WILDCARD_PAT
+              UNDERSCORE "_"
+            WHITESPACE " "
+            IN_KW "in"
+            WHITESPACE " "
+            RANGE_EXPR
+              LITERAL
+                INT_NUMBER "0"
+              DOT2 ".."
+              LITERAL
+                INT_NUMBER "10"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 43: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs b/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs
new file mode 100644
index 00000000000..d410274198d
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = for _ in 0..10 {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast b/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast
new file mode 100644
index 00000000000..10232195413
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast
@@ -0,0 +1,46 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          LOOP_EXPR
+            LOOP_KW "loop"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 33: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs b/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs
new file mode 100644
index 00000000000..28b892869a1
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = loop {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast b/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast
new file mode 100644
index 00000000000..6e1181246c0
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast
@@ -0,0 +1,85 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MATCH_EXPR
+            MATCH_KW "match"
+            WHITESPACE " "
+            CALL_EXPR
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "Some"
+              ARG_LIST
+                L_PAREN "("
+                LITERAL
+                  INT_NUMBER "1"
+                R_PAREN ")"
+            WHITESPACE " "
+            MATCH_ARM_LIST
+              L_CURLY "{"
+              WHITESPACE "\n        "
+              MATCH_ARM
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+                WHITESPACE " "
+                FAT_ARROW "=>"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "1"
+                COMMA ","
+              WHITESPACE "\n        "
+              MATCH_ARM
+                IDENT_PAT
+                  NAME
+                    IDENT "None"
+                WHITESPACE " "
+                FAT_ARROW "=>"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "2"
+                COMMA ","
+              WHITESPACE "\n    "
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 83: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs b/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs
new file mode 100644
index 00000000000..902d70daed5
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs
@@ -0,0 +1,8 @@
+fn f() {
+    let _ = match Some(1) {
+        Some(_) => 1,
+        None => 2,
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast b/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast
new file mode 100644
index 00000000000..298d47d5394
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          WHILE_EXPR
+            WHILE_KW "while"
+            WHITESPACE " "
+            LITERAL
+              TRUE_KW "true"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 39: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs b/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs
new file mode 100644
index 00000000000..a52343d8e6f
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs
@@ -0,0 +1,6 @@
+fn f() {
+    let _ = while true {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast b/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast
new file mode 100644
index 00000000000..c0a4b0400d8
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast
@@ -0,0 +1,57 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          IF_EXPR
+            IF_KW "if"
+            WHITESPACE " "
+            LITERAL
+              TRUE_KW "true"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+            WHITESPACE " "
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                RETURN_EXPR
+                  RETURN_KW "return"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 49: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs b/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs
new file mode 100644
index 00000000000..9a87aecbd41
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs
@@ -0,0 +1,7 @@
+fn f() {
+    let _ = if true {
+    } else {
+    } else {
+        return
+    };
+}
\ No newline at end of file
diff --git a/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast b/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast
new file mode 100644
index 00000000000..0529e9750e7
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast
@@ -0,0 +1,49 @@
+SOURCE_FILE
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "X"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          L_ANGLE "<"
+          TUPLE_TYPE
+            L_PAREN "("
+            R_PAREN ")"
+          R_ANGLE ">"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+  TYPE_ALIAS
+    TYPE_KW "type"
+    WHITESPACE " "
+    NAME
+      IDENT "Y"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PATH_TYPE
+      PATH
+        PATH_SEGMENT
+          L_ANGLE "<"
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "A"
+          WHITESPACE " "
+          AS_KW "as"
+          WHITESPACE " "
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "B"
+          R_ANGLE ">"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 13: expected `::`
+error 32: expected `::`
diff --git a/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs b/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs
new file mode 100644
index 00000000000..802d6cc14a4
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs
@@ -0,0 +1,2 @@
+type X = <()>;
+type Y = <A as B>;
diff --git a/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast
new file mode 100644
index 00000000000..6ec580212b4
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast
@@ -0,0 +1,69 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "func"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          TUPLE_STRUCT_PAT
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "Some"
+            L_PAREN "("
+            WILDCARD_PAT
+              UNDERSCORE "_"
+            R_PAREN ")"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              CALL_EXPR
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                ARG_LIST
+                  L_PAREN "("
+                  LITERAL
+                    INT_NUMBER "1"
+                  R_PAREN ")"
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                MACRO_EXPR
+                  MACRO_CALL
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "panic"
+                    BANG "!"
+                    TOKEN_TREE
+                      L_PAREN "("
+                      STRING "\"h\""
+                      R_PAREN ")"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 35: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs
new file mode 100644
index 00000000000..30d52fea3b2
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs
@@ -0,0 +1 @@
+fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
diff --git a/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast b/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
index 2ef66484ae4..c45f8708980 100644
--- a/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
+++ b/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast
@@ -1,5 +1,5 @@
 SOURCE_FILE
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
diff --git a/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast b/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
index 4443d9d1426..8f678247731 100644
--- a/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
+++ b/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast
@@ -1,5 +1,5 @@
 SOURCE_FILE
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
@@ -50,7 +50,7 @@ SOURCE_FILE
                     IDENT "Copy"
     SEMICOLON ";"
   WHITESPACE "\n"
-  TRAIT
+  TRAIT_ALIAS
     TRAIT_KW "trait"
     WHITESPACE " "
     NAME
diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs
index 6550cf27e99..6df1273edd6 100644
--- a/crates/project-model/src/build_scripts.rs
+++ b/crates/project-model/src/build_scripts.rs
@@ -15,13 +15,13 @@ use std::{
 
 use cargo_metadata::{camino::Utf8Path, Message};
 use la_arena::ArenaMap;
-use paths::AbsPathBuf;
+use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::FxHashMap;
 use semver::Version;
 use serde::Deserialize;
 
 use crate::{
-    cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
+    cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
     InvocationStrategy, Package,
 };
 
@@ -67,6 +67,7 @@ impl WorkspaceBuildScripts {
                 let mut cmd = Command::new(toolchain::cargo());
 
                 cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
+                cmd.args(&config.extra_args);
 
                 // --all-targets includes tests, benches and examples in addition to the
                 // default lib and bins. This is an independent concept from the --target
@@ -250,7 +251,7 @@ impl WorkspaceBuildScripts {
 
         if tracing::enabled!(tracing::Level::INFO) {
             for package in workspace.packages() {
-                let package_build_data = &mut outputs[package];
+                let package_build_data = &outputs[package];
                 if !package_build_data.is_unchanged() {
                     tracing::info!(
                         "{}: {:?}",
@@ -378,6 +379,83 @@ impl WorkspaceBuildScripts {
     pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
         self.outputs.get(idx)
     }
+
+    pub(crate) fn rustc_crates(
+        rustc: &CargoWorkspace,
+        current_dir: &AbsPath,
+        extra_env: &FxHashMap<String, String>,
+    ) -> Self {
+        let mut bs = WorkspaceBuildScripts::default();
+        for p in rustc.packages() {
+            bs.outputs.insert(p, BuildScriptOutput::default());
+        }
+        let res = (|| {
+            let target_libdir = (|| {
+                let mut cargo_config = Command::new(toolchain::cargo());
+                cargo_config.envs(extra_env);
+                cargo_config
+                    .current_dir(current_dir)
+                    .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"])
+                    .env("RUSTC_BOOTSTRAP", "1");
+                if let Ok(it) = utf8_stdout(cargo_config) {
+                    return Ok(it);
+                }
+                let mut cmd = Command::new(toolchain::rustc());
+                cmd.envs(extra_env);
+                cmd.args(["--print", "target-libdir"]);
+                utf8_stdout(cmd)
+            })()?;
+
+            let target_libdir = AbsPathBuf::try_from(PathBuf::from(target_libdir))
+                .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?;
+            tracing::info!("Loading rustc proc-macro paths from {}", target_libdir.display());
+
+            let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)?
+                .filter_map(|entry| {
+                    let dir_entry = entry.ok()?;
+                    if dir_entry.file_type().ok()?.is_file() {
+                        let path = dir_entry.path();
+                        tracing::info!("p{:?}", path);
+                        let extension = path.extension()?;
+                        if extension == std::env::consts::DLL_EXTENSION {
+                            let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned();
+                            let path = AbsPathBuf::try_from(path).ok()?;
+                            return Some((name, path));
+                        }
+                    }
+                    None
+                })
+                .collect();
+            for p in rustc.packages() {
+                let package = &rustc[p];
+                if package.targets.iter().any(|&it| rustc[it].is_proc_macro) {
+                    if let Some((_, path)) =
+                        proc_macro_dylibs.iter().find(|(name, _)| *name == package.name)
+                    {
+                        bs.outputs[p].proc_macro_dylib_path = Some(path.clone());
+                    }
+                }
+            }
+
+            if tracing::enabled!(tracing::Level::INFO) {
+                for package in rustc.packages() {
+                    let package_build_data = &bs.outputs[package];
+                    if !package_build_data.is_unchanged() {
+                        tracing::info!(
+                            "{}: {:?}",
+                            rustc[package].manifest.parent().display(),
+                            package_build_data,
+                        );
+                    }
+                }
+            }
+            Ok(())
+        })();
+        if let Err::<_, anyhow::Error>(e) = res {
+            bs.error = Some(e.to_string());
+        }
+        bs
+    }
 }
 
 // FIXME: Find a better way to know if it is a dylib.
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index fdc7859eb90..732adc50b50 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -105,6 +105,8 @@ pub struct CargoConfig {
     pub wrap_rustc_in_build_scripts: bool,
     /// The command to run instead of `cargo check` for building build scripts.
     pub run_build_script_command: Option<Vec<String>>,
+    /// Extra args to pass to the cargo command.
+    pub extra_args: Vec<String>,
     /// Extra env vars to set when invoking the cargo command
     pub extra_env: FxHashMap<String, String>,
     pub invocation_strategy: InvocationStrategy,
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index 328d2fbcf31..74e41eda763 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -88,23 +88,17 @@ impl Sysroot {
     }
 
     pub fn discover_with_src_override(
-        dir: &AbsPath,
+        current_dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
         src: AbsPathBuf,
     ) -> Result<Sysroot> {
-        tracing::debug!("discovering sysroot for {}", dir.display());
-        let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
+        tracing::debug!("discovering sysroot for {}", current_dir.display());
+        let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?;
         Ok(Sysroot::load(sysroot_dir, src))
     }
 
-    pub fn discover_rustc(
-        cargo_toml: &ManifestPath,
-        extra_env: &FxHashMap<String, String>,
-    ) -> Option<ManifestPath> {
-        tracing::debug!("discovering rustc source for {}", cargo_toml.display());
-        let current_dir = cargo_toml.parent();
-        let sysroot_dir = discover_sysroot_dir(current_dir, extra_env).ok()?;
-        get_rustc_src(&sysroot_dir)
+    pub fn discover_rustc(&self) -> Option<ManifestPath> {
+        get_rustc_src(&self.root)
     }
 
     pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
@@ -282,4 +276,7 @@ unwind
 std_detect
 test";
 
-const PROC_MACRO_DEPS: &str = "std";
+// core is required for our builtin derives to work in the proc_macro lib currently
+const PROC_MACRO_DEPS: &str = "
+std
+core";
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 9e9691d11e8..749eee531ee 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -1547,6 +1547,15 @@ fn rust_project_hello_world_project_model() {
                                 ),
                                 prelude: true,
                             },
+                            Dependency {
+                                crate_id: CrateId(
+                                    1,
+                                ),
+                                name: CrateName(
+                                    "core",
+                                ),
+                                prelude: true,
+                            },
                         ],
                         proc_macro: Err(
                             "no proc macro loaded for sysroot crate",
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 2a11f1e8eb8..faa6816fdc2 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -70,7 +70,7 @@ pub enum ProjectWorkspace {
         cargo: CargoWorkspace,
         build_scripts: WorkspaceBuildScripts,
         sysroot: Option<Sysroot>,
-        rustc: Option<CargoWorkspace>,
+        rustc: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
         ///
@@ -116,7 +116,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("sysroot", &sysroot.is_some())
                 .field(
                     "n_rustc_compiler_crates",
-                    &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
+                    &rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()),
                 )
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .field("n_cfg_overrides", &cfg_overrides.len())
@@ -237,37 +237,45 @@ impl ProjectWorkspace {
                 };
 
                 if let Some(sysroot) = &sysroot {
-                    tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
+                    tracing::info!(workspace = %cargo_toml.display(), src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
                 }
 
                 let rustc_dir = match &config.rustc_source {
                     Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
                     Some(RustcSource::Discover) => {
-                        Sysroot::discover_rustc(&cargo_toml, &config.extra_env)
+                        sysroot.as_ref().and_then(Sysroot::discover_rustc)
                     }
                     None => None,
                 };
-                if let Some(rustc_dir) = &rustc_dir {
-                    tracing::info!(rustc_dir = %rustc_dir.display(), "Using rustc source");
-                }
 
                 let rustc = match rustc_dir {
-                    Some(rustc_dir) => match CargoWorkspace::fetch_metadata(
-                        &rustc_dir,
-                        cargo_toml.parent(),
-                        config,
-                        progress,
-                    ) {
-                        Ok(meta) => Some(CargoWorkspace::new(meta)),
-                        Err(e) => {
-                            tracing::error!(
-                                %e,
-                                "Failed to read Cargo metadata from rustc source at {}",
-                                rustc_dir.display()
-                            );
-                            None
+                    Some(rustc_dir) => {
+                        tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source");
+                        match CargoWorkspace::fetch_metadata(
+                            &rustc_dir,
+                            cargo_toml.parent(),
+                            config,
+                            progress,
+                        ) {
+                            Ok(meta) => {
+                                let workspace = CargoWorkspace::new(meta);
+                                let buildscripts = WorkspaceBuildScripts::rustc_crates(
+                                    &workspace,
+                                    cargo_toml.parent(),
+                                    &config.extra_env,
+                                );
+                                Some((workspace, buildscripts))
+                            }
+                            Err(e) => {
+                                tracing::error!(
+                                    %e,
+                                    "Failed to read Cargo metadata from rustc source at {}",
+                                    rustc_dir.display()
+                                );
+                                None
+                            }
                         }
-                    },
+                    }
                     None => None,
                 };
 
@@ -531,7 +539,7 @@ impl ProjectWorkspace {
                         PackageRoot { is_local, include, exclude }
                     })
                     .chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root())))
-                    .chain(rustc.iter().flat_map(|rustc| {
+                    .chain(rustc.iter().flat_map(|(rustc, _)| {
                         rustc.packages().map(move |krate| PackageRoot {
                             is_local: false,
                             include: vec![rustc[krate].manifest.parent().to_path_buf()],
@@ -559,7 +567,7 @@ impl ProjectWorkspace {
                 sysroot_package_len + project.n_crates()
             }
             ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
-                let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len());
+                let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len());
                 let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len());
                 cargo.packages().len() + sysroot_package_len + rustc_package_len
             }
@@ -778,7 +786,7 @@ fn project_json_to_crate_graph(
 fn cargo_to_crate_graph(
     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    rustc: &Option<CargoWorkspace>,
+    rustc: &Option<(CargoWorkspace, WorkspaceBuildScripts)>,
     cargo: &CargoWorkspace,
     sysroot: Option<&Sysroot>,
     rustc_cfg: Vec<CfgFlag>,
@@ -924,7 +932,7 @@ fn cargo_to_crate_graph(
     if has_private {
         // If the user provided a path to rustc sources, we add all the rustc_private crates
         // and create dependencies on them for the crates which opt-in to that
-        if let Some(rustc_workspace) = rustc {
+        if let Some((rustc_workspace, build_scripts)) = rustc {
             handle_rustc_crates(
                 &mut crate_graph,
                 &mut pkg_to_lib_crate,
diff --git a/crates/rust-analyzer/default_12483297303756020505_0.profraw b/crates/rust-analyzer/default_12483297303756020505_0.profraw
deleted file mode 100644
index e49d7c14492..00000000000
Binary files a/crates/rust-analyzer/default_12483297303756020505_0.profraw and /dev/null differ
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 93297faa664..e8c10927d62 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::{
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
-    expr::ExprId,
+    expr::{ExprId, PatId},
     FunctionId,
 };
 use hir_ty::{Interner, TyExt, TypeFlags};
@@ -222,7 +222,11 @@ impl flags::AnalysisStats {
         let mut num_exprs = 0;
         let mut num_exprs_unknown = 0;
         let mut num_exprs_partially_unknown = 0;
-        let mut num_type_mismatches = 0;
+        let mut num_expr_type_mismatches = 0;
+        let mut num_pats = 0;
+        let mut num_pats_unknown = 0;
+        let mut num_pats_partially_unknown = 0;
+        let mut num_pat_type_mismatches = 0;
         let analysis = host.analysis();
         for f in funcs.iter().copied() {
             let name = f.name(db);
@@ -255,6 +259,8 @@ impl flags::AnalysisStats {
             let f_id = FunctionId::from(f);
             let (body, sm) = db.body_with_source_map(f_id.into());
             let inference_result = db.infer(f_id.into());
+
+            // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
                 (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
             for (expr_id, _) in body.exprs.iter() {
@@ -307,12 +313,12 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv(db, &analysis, vfs, &sm, expr_id),
+                        location_csv_expr(db, &analysis, vfs, &sm, expr_id),
                         ty.display(db)
                     );
                 }
                 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
-                    num_type_mismatches += 1;
+                    num_expr_type_mismatches += 1;
                     if verbosity.is_verbose() {
                         if let Some((path, start, end)) =
                             expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
@@ -339,7 +345,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv(db, &analysis, vfs, &sm, expr_id),
+                            location_csv_expr(db, &analysis, vfs, &sm, expr_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -355,6 +361,109 @@ impl flags::AnalysisStats {
                     num_exprs_partially_unknown - previous_partially_unknown
                 ));
             }
+            // endregion:expressions
+
+            // region:patterns
+            let (previous_pats, previous_unknown, previous_partially_unknown) =
+                (num_pats, num_pats_unknown, num_pats_partially_unknown);
+            for (pat_id, _) in body.pats.iter() {
+                let ty = &inference_result[pat_id];
+                num_pats += 1;
+                let unknown_or_partial = if ty.is_unknown() {
+                    num_pats_unknown += 1;
+                    if verbosity.is_spammy() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Unknown type",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                            ));
+                        } else {
+                            bar.println(format!("{name}: Unknown type",));
+                        }
+                    }
+                    true
+                } else {
+                    let is_partially_unknown =
+                        ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR);
+                    if is_partially_unknown {
+                        num_pats_partially_unknown += 1;
+                    }
+                    is_partially_unknown
+                };
+                if self.only.is_some() && verbosity.is_spammy() {
+                    // in super-verbose mode for just one function, we print every single pattern
+                    if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                    {
+                        bar.println(format!(
+                            "{}:{}-{}:{}: {}",
+                            start.line + 1,
+                            start.col,
+                            end.line + 1,
+                            end.col,
+                            ty.display(db)
+                        ));
+                    } else {
+                        bar.println(format!("unknown location: {}", ty.display(db)));
+                    }
+                }
+                if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
+                    println!(
+                        r#"{},type,"{}""#,
+                        location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                        ty.display(db)
+                    );
+                }
+                if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
+                    num_pat_type_mismatches += 1;
+                    if verbosity.is_verbose() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Expected {}, got {}",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        } else {
+                            bar.println(format!(
+                                "{}: Expected {}, got {}",
+                                name,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        }
+                    }
+                    if self.output == Some(OutputFormat::Csv) {
+                        println!(
+                            r#"{},mismatch,"{}","{}""#,
+                            location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                            mismatch.expected.display(db),
+                            mismatch.actual.display(db)
+                        );
+                    }
+                }
+            }
+            if verbosity.is_spammy() {
+                bar.println(format!(
+                    "In {}: {} pats, {} unknown, {} partial",
+                    full_name,
+                    num_pats - previous_pats,
+                    num_pats_unknown - previous_unknown,
+                    num_pats_partially_unknown - previous_partially_unknown
+                ));
+            }
+            // endregion:patterns
             bar.inc(1);
         }
 
@@ -366,10 +475,21 @@ impl flags::AnalysisStats {
             percentage(num_exprs_unknown, num_exprs),
             num_exprs_partially_unknown,
             percentage(num_exprs_partially_unknown, num_exprs),
-            num_type_mismatches
+            num_expr_type_mismatches
+        );
+        eprintln!(
+            "  pats: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
+            num_pats,
+            num_pats_unknown,
+            percentage(num_pats_unknown, num_pats),
+            num_pats_partially_unknown,
+            percentage(num_pats_partially_unknown, num_pats),
+            num_pat_type_mismatches
         );
         report_metric("unknown type", num_exprs_unknown, "#");
-        report_metric("type mismatches", num_type_mismatches, "#");
+        report_metric("type mismatches", num_expr_type_mismatches, "#");
+        report_metric("pattern unknown type", num_pats_unknown, "#");
+        report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
 
         eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
     }
@@ -379,7 +499,7 @@ impl flags::AnalysisStats {
     }
 }
 
-fn location_csv(
+fn location_csv_expr(
     db: &RootDatabase,
     analysis: &Analysis,
     vfs: &Vfs,
@@ -401,6 +521,30 @@ fn location_csv(
     format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
 }
 
+fn location_csv_pat(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> String {
+    let src = match sm.pat_syntax(pat_id) {
+        Ok(s) => s,
+        Err(SyntheticSyntax) => return "synthetic,,".to_string(),
+    };
+    let root = db.parse_or_expand(src.file_id).unwrap();
+    let node = src.map(|e| {
+        e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone())
+    });
+    let original_range = node.as_ref().original_file_range(db);
+    let path = vfs.file_path(original_range.file_id);
+    let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+    let text_range = original_range.range;
+    let (start, end) =
+        (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
+    format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
+}
+
 fn expr_syntax_range(
     db: &RootDatabase,
     analysis: &Analysis,
@@ -423,6 +567,33 @@ fn expr_syntax_range(
         None
     }
 }
+fn pat_syntax_range(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> Option<(VfsPath, LineCol, LineCol)> {
+    let src = sm.pat_syntax(pat_id);
+    if let Ok(src) = src {
+        let root = db.parse_or_expand(src.file_id).unwrap();
+        let node = src.map(|e| {
+            e.either(
+                |it| it.to_node(&root).syntax().clone(),
+                |it| it.to_node(&root).syntax().clone(),
+            )
+        });
+        let original_range = node.as_ref().original_file_range(db);
+        let path = vfs.file_path(original_range.file_id);
+        let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+        let text_range = original_range.range;
+        let (start, end) =
+            (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
+        Some((path, start, end))
+    } else {
+        None
+    }
+}
 
 fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
     for i in 0..slice.len() {
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index ff821be53d8..0721d486ef1 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -1,6 +1,7 @@
 //! Analyze all modules in a project for diagnostics. Exits with a non-zero
 //! status code if any errors are found.
 
+use project_model::{CargoConfig, RustcSource};
 use rustc_hash::FxHashSet;
 
 use hir::{db::HirDatabase, Crate, Module};
@@ -14,7 +15,8 @@ use crate::cli::{
 
 impl flags::Diagnostics {
     pub fn run(self) -> anyhow::Result<()> {
-        let cargo_config = Default::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs
index 3fc1aa4eaeb..9b5451496c6 100644
--- a/crates/rust-analyzer/src/cli/lsif.rs
+++ b/crates/rust-analyzer/src/cli/lsif.rs
@@ -13,7 +13,7 @@ use ide_db::LineIndexDatabase;
 use ide_db::base_db::salsa::{self, ParallelDatabase};
 use ide_db::line_index::WideEncoding;
 use lsp_types::{self, lsif};
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
 use vfs::{AbsPathBuf, Vfs};
 
 use crate::cli::load_cargo::ProcMacroServerChoice;
@@ -289,7 +289,8 @@ impl flags::Lsif {
     pub fn run(self) -> Result<()> {
         eprintln!("Generating LSIF started...");
         let now = Instant::now();
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let no_progress = &|_| ();
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index 9a04fbea774..df5c26cf77a 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -15,7 +15,7 @@ use ide::{
     TokenStaticData,
 };
 use ide_db::LineIndexDatabase;
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
 use scip::types as scip_types;
 use std::env;
 
@@ -29,7 +29,8 @@ impl flags::Scip {
     pub fn run(self) -> Result<()> {
         eprintln!("Generating SCIP start...");
         let now = Instant::now();
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
 
         let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
         let load_cargo_config = LoadCargoConfig {
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index 3552f840a1b..35a874f8920 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -1,7 +1,7 @@
 //! Applies structured search replace rules from the command line.
 
 use ide_ssr::MatchFinder;
-use project_model::CargoConfig;
+use project_model::{CargoConfig, RustcSource};
 
 use crate::cli::{
     flags,
@@ -12,7 +12,8 @@ use crate::cli::{
 impl flags::Ssr {
     pub fn run(self) -> Result<()> {
         use ide_db::base_db::SourceDatabaseExt;
-        let cargo_config = CargoConfig::default();
+        let mut cargo_config = CargoConfig::default();
+        cargo_config.sysroot = Some(RustcSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index f609a50a05f..75233dbb2ab 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -101,6 +101,8 @@ config_data! {
         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = "true",
+        /// Extra arguments that are passed to every cargo invocation.
+        cargo_extraArgs: Vec<String> = "[]",
         /// Extra environment variables that will be set when running cargo, rustc
         /// or other commands within the workspace. Useful for setting RUSTFLAGS.
         cargo_extraEnv: FxHashMap<String, String> = "{}",
@@ -366,6 +368,8 @@ config_data! {
         inlayHints_typeHints_hideClosureInitialization: bool       = "false",
         /// Whether to hide inlay type hints for constructors.
         inlayHints_typeHints_hideNamedConstructor: bool            = "false",
+        /// Enables the experimental support for interpreting tests.
+        interpret_tests: bool                                      = "false",
 
         /// Join lines merges consecutive declaration and initialization of an assignment.
         joinLines_joinAssignments: bool = "true",
@@ -456,7 +460,10 @@ config_data! {
         /// Additional arguments to `rustfmt`.
         rustfmt_extraArgs: Vec<String>               = "[]",
         /// Advanced option, fully override the command rust-analyzer uses for
-        /// formatting.
+        /// formatting. This should be the equivalent of `rustfmt` here, and
+        /// not that of `cargo fmt`. The file contents will be passed on the
+        /// standard input and the formatted result will be read from the
+        /// standard output.
         rustfmt_overrideCommand: Option<Vec<String>> = "null",
         /// Enables the use of rustfmt's unstable range formatting command for the
         /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
@@ -849,27 +856,27 @@ impl Config {
     }
     pub fn linked_projects(&self) -> Vec<LinkedProject> {
         match self.data.linkedProjects.as_slice() {
-            [] => match self.discovered_projects.as_ref() {
-                Some(discovered_projects) => {
-                    let exclude_dirs: Vec<_> = self
-                        .data
-                        .files_excludeDirs
+            [] => {
+                match self.discovered_projects.as_ref() {
+                    Some(discovered_projects) => {
+                        let exclude_dirs: Vec<_> = self
+                            .data
+                            .files_excludeDirs
+                            .iter()
+                            .map(|p| self.root_path.join(p))
+                            .collect();
+                        discovered_projects
                         .iter()
-                        .map(|p| self.root_path.join(p))
-                        .collect();
-                    discovered_projects
-                        .iter()
-                        .filter(|p| {
-                            let (ProjectManifest::ProjectJson(path)
-                            | ProjectManifest::CargoToml(path)) = p;
+                        .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| {
                             !exclude_dirs.iter().any(|p| path.starts_with(p))
                         })
                         .cloned()
                         .map(LinkedProject::from)
                         .collect()
+                    }
+                    None => Vec::new(),
                 }
-                None => Vec::new(),
-            },
+            }
             linked_projects => linked_projects
                 .iter()
                 .filter_map(|linked_project| match linked_project {
@@ -1050,10 +1057,20 @@ impl Config {
         }
     }
 
+    pub fn extra_args(&self) -> &Vec<String> {
+        &self.data.cargo_extraArgs
+    }
+
     pub fn extra_env(&self) -> &FxHashMap<String, String> {
         &self.data.cargo_extraEnv
     }
 
+    pub fn check_extra_args(&self) -> Vec<String> {
+        let mut extra_args = self.extra_args().clone();
+        extra_args.extend_from_slice(&self.data.check_extraArgs);
+        extra_args
+    }
+
     pub fn check_extra_env(&self) -> FxHashMap<String, String> {
         let mut extra_env = self.data.cargo_extraEnv.clone();
         extra_env.extend(self.data.check_extraEnv.clone());
@@ -1152,6 +1169,7 @@ impl Config {
                 InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
             },
             run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
+            extra_args: self.data.cargo_extraArgs.clone(),
             extra_env: self.data.cargo_extraEnv.clone(),
         }
     }
@@ -1222,7 +1240,7 @@ impl Config {
                     CargoFeaturesDef::All => vec![],
                     CargoFeaturesDef::Selected(it) => it,
                 },
-                extra_args: self.data.check_extraArgs.clone(),
+                extra_args: self.check_extra_args(),
                 extra_env: self.check_extra_env(),
                 ansi_color_output: self.color_diagnostic_output(),
             },
@@ -1441,6 +1459,7 @@ impl Config {
                 }
             },
             keywords: self.data.hover_documentation_keywords_enable,
+            interpret_tests: self.data.interpret_tests,
         }
     }
 
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4e08bd0a724..32ac9a42dec 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -134,6 +134,16 @@ pub(crate) fn handle_view_hir(
     Ok(res)
 }
 
+pub(crate) fn handle_view_mir(
+    snap: GlobalStateSnapshot,
+    params: lsp_types::TextDocumentPositionParams,
+) -> Result<String> {
+    let _p = profile::span("handle_view_mir");
+    let position = from_proto::file_position(&snap, params)?;
+    let res = snap.analysis.view_mir(position)?;
+    Ok(res)
+}
+
 pub(crate) fn handle_view_file_text(
     snap: GlobalStateSnapshot,
     params: lsp_types::TextDocumentIdentifier,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index e33589cc536..c7b513db981 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -74,6 +74,14 @@ impl Request for ViewHir {
     const METHOD: &'static str = "rust-analyzer/viewHir";
 }
 
+pub enum ViewMir {}
+
+impl Request for ViewMir {
+    type Params = lsp_types::TextDocumentPositionParams;
+    type Result = String;
+    const METHOD: &'static str = "rust-analyzer/viewMir";
+}
+
 pub enum ViewFileText {}
 
 impl Request for ViewFileText {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index d1e38b33c7d..dd0804b4398 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -111,12 +111,7 @@ impl fmt::Debug for Event {
 
 impl GlobalState {
     fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
-        if self.config.linked_projects().is_empty()
-            && self.config.detached_files().is_empty()
-            && self.config.notifications().cargo_toml_not_found
-        {
-            self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None);
-        };
+        self.update_status_or_notify();
 
         if self.config.did_save_text_document_dynamic_registration() {
             let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
@@ -323,17 +318,6 @@ impl GlobalState {
 
         if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
             for file_id in diagnostic_changes {
-                let db = self.analysis_host.raw_database();
-                let source_root = db.file_source_root(file_id);
-                if db.source_root(source_root).is_library {
-                    // Only publish diagnostics for files in the workspace, not from crates.io deps
-                    // or the sysroot.
-                    // While theoretically these should never have errors, we have quite a few false
-                    // positives particularly in the stdlib, and those diagnostics would stay around
-                    // forever if we emitted them here.
-                    continue;
-                }
-
                 let uri = file_id_to_url(&self.vfs.read().0, file_id);
                 let mut diagnostics =
                     self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
@@ -405,18 +389,7 @@ impl GlobalState {
             });
         }
 
-        let status = self.current_status();
-        if self.last_reported_status.as_ref() != Some(&status) {
-            self.last_reported_status = Some(status.clone());
-
-            if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) {
-                self.show_message(lsp_types::MessageType::ERROR, message.clone());
-            }
-
-            if self.config.server_status_notification() {
-                self.send_notification::<lsp_ext::ServerStatusNotification>(status);
-            }
-        }
+        self.update_status_or_notify();
 
         let loop_duration = loop_start.elapsed();
         if loop_duration > Duration::from_millis(100) && was_quiescent {
@@ -426,6 +399,20 @@ impl GlobalState {
         Ok(())
     }
 
+    fn update_status_or_notify(&mut self) {
+        let status = self.current_status();
+        if self.last_reported_status.as_ref() != Some(&status) {
+            self.last_reported_status = Some(status.clone());
+
+            if self.config.server_status_notification() {
+                self.send_notification::<lsp_ext::ServerStatusNotification>(status);
+            } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message)
+            {
+                self.show_and_log_error(message.clone(), None);
+            }
+        }
+    }
+
     fn handle_task(&mut self, prime_caches_progress: &mut Vec<PrimeCachesProgress>, task: Task) {
         match task {
             Task::Response(response) => self.respond(response),
@@ -456,6 +443,9 @@ impl GlobalState {
                     ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
                     ProjectWorkspaceProgress::End(workspaces) => {
                         self.fetch_workspaces_queue.op_completed(Some(workspaces));
+                        if let Err(e) = self.fetch_workspace_error() {
+                            tracing::error!("FetchWorkspaceError:\n{e}");
+                        }
 
                         let old = Arc::clone(&self.workspaces);
                         self.switch_workspaces("fetched workspace".to_string());
@@ -477,6 +467,9 @@ impl GlobalState {
                     BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
                     BuildDataProgress::End(build_data_result) => {
                         self.fetch_build_data_queue.op_completed(build_data_result);
+                        if let Err(e) = self.fetch_build_data_error() {
+                            tracing::error!("FetchBuildDataError:\n{e}");
+                        }
 
                         self.switch_workspaces("fetched build data".to_string());
 
@@ -509,6 +502,7 @@ impl GlobalState {
                 self.vfs_progress_n_total = n_total;
                 self.vfs_progress_n_done = n_done;
 
+                // if n_total != 0 {
                 let state = if n_done == 0 {
                     Progress::Begin
                 } else if n_done < n_total {
@@ -523,7 +517,8 @@ impl GlobalState {
                     Some(format!("{n_done}/{n_total}")),
                     Some(Progress::fraction(n_done, n_total)),
                     None,
-                )
+                );
+                // }
             }
         }
     }
@@ -565,7 +560,10 @@ impl GlobalState {
                     flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
                     flycheck::Progress::DidCancel => (Progress::End, None),
                     flycheck::Progress::DidFailToRestart(err) => {
-                        self.show_and_log_error("cargo check failed".to_string(), Some(err));
+                        self.show_and_log_error(
+                            "cargo check failed to start".to_string(),
+                            Some(err),
+                        );
                         return;
                     }
                     flycheck::Progress::DidFinish(result) => {
@@ -634,6 +632,7 @@ impl GlobalState {
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
             .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
             .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
+            .on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
             .on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
             .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
             .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
@@ -971,10 +970,20 @@ impl GlobalState {
     }
 
     fn update_diagnostics(&mut self) {
+        let db = self.analysis_host.raw_database();
         let subscriptions = self
             .mem_docs
             .iter()
             .map(|path| self.vfs.read().0.file_id(path).unwrap())
+            .filter(|&file_id| {
+                let source_root = db.file_source_root(file_id);
+                // Only publish diagnostics for files in the workspace, not from crates.io deps
+                // or the sysroot.
+                // While theoretically these should never have errors, we have quite a few false
+                // positives particularly in the stdlib, and those diagnostics would stay around
+                // forever if we emitted them here.
+                !db.source_root(source_root).is_library
+            })
             .collect::<Vec<_>>();
 
         tracing::trace!("updating notifications for {:?}", subscriptions);
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index abce0d73782..28d37f5685a 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -12,17 +12,21 @@
 //! correct. Instead, we try to provide a best-effort service. Even if the
 //! project is currently loading and we don't have a full project model, we
 //! still want to respond to various  requests.
-use std::{mem, sync::Arc};
+use std::{collections::hash_map::Entry, mem, sync::Arc};
 
 use flycheck::{FlycheckConfig, FlycheckHandle};
 use hir::db::DefDatabase;
 use ide::Change;
-use ide_db::base_db::{
-    CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
-    ProcMacroLoadResult, SourceRoot, VfsPath,
+use ide_db::{
+    base_db::{
+        CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
+        ProcMacroLoadResult, SourceRoot, VfsPath,
+    },
+    FxHashMap,
 };
+use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
-use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
+use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
 use syntax::SmolStr;
 use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
 
@@ -52,7 +56,8 @@ pub(crate) enum BuildDataProgress {
 
 impl GlobalState {
     pub(crate) fn is_quiescent(&self) -> bool {
-        !(self.fetch_workspaces_queue.op_in_progress()
+        !(self.last_reported_status.is_none()
+            || 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)
@@ -104,9 +109,9 @@ impl GlobalState {
             status.message = Some("Workspace reload required".to_string())
         }
 
-        if let Err(error) = self.fetch_workspace_error() {
+        if let Err(_) = self.fetch_workspace_error() {
             status.health = lsp_ext::Health::Error;
-            status.message = Some(error)
+            status.message = Some("Failed to load workspaces".to_string())
         }
 
         if self.config.linked_projects().is_empty()
@@ -114,8 +119,9 @@ impl GlobalState {
             && self.config.notifications().cargo_toml_not_found
         {
             status.health = lsp_ext::Health::Warning;
-            status.message = Some("Workspace reload required".to_string())
+            status.message = Some("Failed to discover workspace".to_string())
         }
+
         status
     }
 
@@ -197,8 +203,7 @@ impl GlobalState {
         let _p = profile::span("GlobalState::switch_workspaces");
         tracing::info!(%cause, "will switch workspaces");
 
-        if let Err(error_message) = self.fetch_workspace_error() {
-            self.show_and_log_error(error_message, None);
+        if let Err(_) = self.fetch_workspace_error() {
             if !self.workspaces.is_empty() {
                 // It only makes sense to switch to a partially broken workspace
                 // if we don't have any workspace at all yet.
@@ -206,10 +211,6 @@ impl GlobalState {
             }
         }
 
-        if let Err(error) = self.fetch_build_data_error() {
-            self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
-        }
-
         let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
         let workspaces =
             workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
@@ -388,7 +389,7 @@ impl GlobalState {
         tracing::info!("did switch workspaces");
     }
 
-    fn fetch_workspace_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
@@ -409,7 +410,7 @@ impl GlobalState {
         Err(buf)
     }
 
-    fn fetch_build_data_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_build_data_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         for ws in &self.fetch_build_data_queue.last_op_result().1 {
@@ -494,7 +495,69 @@ impl ProjectFolders {
         let mut fsc = FileSetConfig::builder();
         let mut local_filesets = vec![];
 
-        for root in workspaces.iter().flat_map(|ws| ws.to_roots()) {
+        // Dedup source roots
+        // Depending on the project setup, we can have duplicated source roots, or for example in
+        // the case of the rustc workspace, we can end up with two source roots that are almost the
+        // same but not quite, like:
+        // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] }
+        // PackageRoot {
+        //     is_local: true,
+        //     include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")],
+        //     exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")]
+        // }
+        //
+        // The first one comes from the explicit rustc workspace which points to the rustc workspace itself
+        // The second comes from the rustc workspace that we load as the actual project workspace
+        // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries.
+        // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work,
+        // so we need to also coalesce the includes if they overlap.
+
+        let mut roots: Vec<_> = workspaces
+            .iter()
+            .flat_map(|ws| ws.to_roots())
+            .update(|root| root.include.sort())
+            .sorted_by(|a, b| a.include.cmp(&b.include))
+            .collect();
+
+        // map that tracks indices of overlapping roots
+        let mut overlap_map = FxHashMap::<_, Vec<_>>::default();
+        let mut done = false;
+
+        while !mem::replace(&mut done, true) {
+            // maps include paths to indices of the corresponding root
+            let mut include_to_idx = FxHashMap::default();
+            // Find and note down the indices of overlapping roots
+            for (idx, root) in roots.iter().enumerate().filter(|(_, it)| !it.include.is_empty()) {
+                for include in &root.include {
+                    match include_to_idx.entry(include) {
+                        Entry::Occupied(e) => {
+                            overlap_map.entry(*e.get()).or_default().push(idx);
+                        }
+                        Entry::Vacant(e) => {
+                            e.insert(idx);
+                        }
+                    }
+                }
+            }
+            for (k, v) in overlap_map.drain() {
+                done = false;
+                for v in v {
+                    let r = mem::replace(
+                        &mut roots[v],
+                        PackageRoot { is_local: false, include: vec![], exclude: vec![] },
+                    );
+                    roots[k].is_local |= r.is_local;
+                    roots[k].include.extend(r.include);
+                    roots[k].exclude.extend(r.exclude);
+                }
+                roots[k].include.sort();
+                roots[k].exclude.sort();
+                roots[k].include.dedup();
+                roots[k].exclude.dedup();
+            }
+        }
+
+        for root in roots.into_iter().filter(|it| !it.include.is_empty()) {
             let file_set_roots: Vec<VfsPath> =
                 root.include.iter().cloned().map(VfsPath::from).collect();
 
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 92029dc1de7..7d97b69f8ea 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -50,7 +50,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         SymbolKind::Struct => lsp_types::SymbolKind::STRUCT,
         SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
         SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
-        SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
+        SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE,
         SymbolKind::Macro
         | SymbolKind::BuiltinAttr
         | SymbolKind::Attribute
@@ -135,6 +135,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Static => lsp_types::CompletionItemKind::VALUE,
             SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT,
             SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE,
+            SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE,
             SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT,
             SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
             SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
@@ -656,6 +657,7 @@ fn semantic_token_type_and_modifiers(
             SymbolKind::Union => semantic_tokens::UNION,
             SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
             SymbolKind::Trait => semantic_tokens::INTERFACE,
+            SymbolKind::TraitAlias => semantic_tokens::INTERFACE,
             SymbolKind::Macro => semantic_tokens::MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 8fc493a23f5..305cf2d394b 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
 
 [dependencies]
 cov-mark = "2.0.0-pre.1"
+either = "1.7.0"
 itertools = "0.10.5"
 rowan = "0.15.10"
 rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 36ad5fddfd0..548b5ba8b8b 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -97,6 +97,7 @@ Item =
 | Static
 | Struct
 | Trait
+| TraitAlias
 | TypeAlias
 | Union
 | Use
@@ -240,10 +241,11 @@ Trait =
   Attr* Visibility?
   'unsafe'? 'auto'?
   'trait' Name GenericParamList?
-  (
-    (':' TypeBoundList?)? WhereClause? AssocItemList
-    | '=' TypeBoundList? WhereClause? ';'
-  )
+  (':' TypeBoundList?)? WhereClause? AssocItemList
+
+TraitAlias =
+  Attr* Visibility?
+  'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';'
 
 AssocItemList =
   '{' Attr* AssocItem* '}'
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 385a4e0a3ce..1e691befff6 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -13,7 +13,7 @@ pub mod prec;
 
 use std::marker::PhantomData;
 
-use itertools::Either;
+use either::Either;
 
 use crate::{
     syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
@@ -25,7 +25,8 @@ pub use self::{
     generated::{nodes::*, tokens::*},
     node_ext::{
         AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
-        SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind,
+        SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam,
+        VisibilityKind,
     },
     operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
     token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
@@ -128,6 +129,13 @@ where
     }
 }
 
+impl<L, R> HasAttrs for Either<L, R>
+where
+    L: HasAttrs,
+    R: HasAttrs,
+{
+}
+
 mod support {
     use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
 
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 642a3bfc35d..fe324845360 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -407,7 +407,21 @@ impl Trait {
     pub fn auto_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![auto]) }
     pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) }
     pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TraitAlias {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for TraitAlias {}
+impl ast::HasName for TraitAlias {}
+impl ast::HasVisibility for TraitAlias {}
+impl ast::HasGenericParams for TraitAlias {}
+impl ast::HasDocComments for TraitAlias {}
+impl TraitAlias {
+    pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) }
     pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+    pub fn type_bound_list(&self) -> Option<TypeBoundList> { support::child(&self.syntax) }
     pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
 }
 
@@ -1573,6 +1587,7 @@ pub enum Item {
     Static(Static),
     Struct(Struct),
     Trait(Trait),
+    TraitAlias(TraitAlias),
     TypeAlias(TypeAlias),
     Union(Union),
     Use(Use),
@@ -2058,6 +2073,17 @@ impl AstNode for Trait {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for TraitAlias {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for TypeAlias {
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3570,6 +3596,9 @@ impl From<Struct> for Item {
 impl From<Trait> for Item {
     fn from(node: Trait) -> Item { Item::Trait(node) }
 }
+impl From<TraitAlias> for Item {
+    fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) }
+}
 impl From<TypeAlias> for Item {
     fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) }
 }
@@ -3596,6 +3625,7 @@ impl AstNode for Item {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -3616,6 +3646,7 @@ impl AstNode for Item {
             STATIC => Item::Static(Static { syntax }),
             STRUCT => Item::Struct(Struct { syntax }),
             TRAIT => Item::Trait(Trait { syntax }),
+            TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }),
             TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }),
             UNION => Item::Union(Union { syntax }),
             USE => Item::Use(Use { syntax }),
@@ -3638,6 +3669,7 @@ impl AstNode for Item {
             Item::Static(it) => &it.syntax,
             Item::Struct(it) => &it.syntax,
             Item::Trait(it) => &it.syntax,
+            Item::TraitAlias(it) => &it.syntax,
             Item::TypeAlias(it) => &it.syntax,
             Item::Union(it) => &it.syntax,
             Item::Use(it) => &it.syntax,
@@ -3950,6 +3982,7 @@ impl AstNode for AnyHasAttrs {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4035,6 +4068,7 @@ impl AstNode for AnyHasDocComments {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4056,7 +4090,7 @@ impl AnyHasGenericParams {
 }
 impl AstNode for AnyHasGenericParams {
     fn can_cast(kind: SyntaxKind) -> bool {
-        matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION)
+        matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION)
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
         Self::can_cast(syntax.kind()).then_some(AnyHasGenericParams { syntax })
@@ -4108,6 +4142,7 @@ impl AstNode for AnyHasName {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | RENAME
@@ -4163,6 +4198,7 @@ impl AstNode for AnyHasVisibility {
                 | STATIC
                 | STRUCT
                 | TRAIT
+                | TRAIT_ALIAS
                 | TYPE_ALIAS
                 | UNION
                 | USE
@@ -4391,6 +4427,11 @@ impl std::fmt::Display for Trait {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for TraitAlias {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for TypeAlias {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index fe82aa90722..15bd5ab3c72 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -680,6 +680,81 @@ impl TypeOrConstParam {
     }
 }
 
+impl AstNode for TypeOrConstParam {
+    fn can_cast(kind: SyntaxKind) -> bool
+    where
+        Self: Sized,
+    {
+        matches!(kind, SyntaxKind::TYPE_PARAM | SyntaxKind::CONST_PARAM)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let res = match syntax.kind() {
+            SyntaxKind::TYPE_PARAM => TypeOrConstParam::Type(ast::TypeParam { syntax }),
+            SyntaxKind::CONST_PARAM => TypeOrConstParam::Const(ast::ConstParam { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            TypeOrConstParam::Type(it) => it.syntax(),
+            TypeOrConstParam::Const(it) => it.syntax(),
+        }
+    }
+}
+
+impl HasAttrs for TypeOrConstParam {}
+
+#[derive(Debug, Clone)]
+pub enum TraitOrAlias {
+    Trait(ast::Trait),
+    TraitAlias(ast::TraitAlias),
+}
+
+impl TraitOrAlias {
+    pub fn name(&self) -> Option<ast::Name> {
+        match self {
+            TraitOrAlias::Trait(x) => x.name(),
+            TraitOrAlias::TraitAlias(x) => x.name(),
+        }
+    }
+}
+
+impl AstNode for TraitOrAlias {
+    fn can_cast(kind: SyntaxKind) -> bool
+    where
+        Self: Sized,
+    {
+        matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let res = match syntax.kind() {
+            SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }),
+            SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            TraitOrAlias::Trait(it) => it.syntax(),
+            TraitOrAlias::TraitAlias(it) => it.syntax(),
+        }
+    }
+}
+
+impl HasAttrs for TraitOrAlias {}
+
 pub enum VisibilityKind {
     In(ast::Path),
     PubCrate,
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
index aa2b7ed5c8b..3e43df2d0d5 100644
--- a/crates/syntax/src/ast/traits.rs
+++ b/crates/syntax/src/ast/traits.rs
@@ -1,7 +1,7 @@
 //! Various traits that are implemented by ast nodes.
 //!
 //! The implementations are usually trivial, and live in generated.rs
-use itertools::Either;
+use either::Either;
 
 use crate::{
     ast::{self, support, AstChildren, AstNode, AstToken},
@@ -134,3 +134,5 @@ impl Iterator for AttrDocCommentIter {
         })
     }
 }
+
+impl<A: HasName, B: HasName> HasName for Either<A, B> {}
diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs
index 3ff6e03006b..ccce71966ff 100644
--- a/crates/syntax/src/tests/ast_src.rs
+++ b/crates/syntax/src/tests/ast_src.rs
@@ -86,6 +86,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
         "STATIC",
         "CONST",
         "TRAIT",
+        "TRAIT_ALIAS",
         "IMPL",
         "TYPE_ALIAS",
         "MACRO_CALL",
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index 03aa2c451e8..e954b58251f 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -783,6 +783,7 @@ fn extract_struct_traits(ast: &mut AstSrc) {
         "Enum",
         "Variant",
         "Trait",
+        "TraitAlias",
         "Module",
         "Static",
         "Const",
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index d1afd0039aa..cd1235fa6dc 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -180,7 +180,9 @@ impl Fixture {
         let mut cfg_key_values = Vec::new();
         let mut env = FxHashMap::default();
         let mut introduce_new_source_root = None;
-        let mut target_data_layout = None;
+        let mut target_data_layout = Some(
+            "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_string(),
+        );
         for component in components[1..].iter() {
             let (key, value) =
                 component.split_once(':').unwrap_or_else(|| panic!("invalid meta line: {meta:?}"));
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 3b033e1aae5..93ff76a040c 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -510,6 +510,7 @@ pub mod fmt {
 pub mod slice {
     #[lang = "slice"]
     impl<T> [T] {
+        #[lang = "slice_len_fn"]
         pub fn len(&self) -> usize {
             loop {}
         }
@@ -533,6 +534,40 @@ pub mod option {
                 None => panic!("called `Option::unwrap()` on a `None` value"),
             }
         }
+
+        pub fn and<U>(self, optb: Option<U>) -> Option<U> {
+            loop {}
+        }
+        pub fn unwrap_or(self, default: T) -> T {
+            loop {}
+        }
+        // region:fn
+        pub fn and_then<U, F>(self, f: F) -> Option<U>
+        where
+            F: FnOnce(T) -> Option<U>,
+        {
+            loop {}
+        }
+        pub fn unwrap_or_else<F>(self, f: F) -> T
+        where
+            F: FnOnce() -> T,
+        {
+            loop {}
+        }
+        pub fn map_or<U, F>(self, default: U, f: F) -> U
+        where
+            F: FnOnce(T) -> U,
+        {
+            loop {}
+        }
+        pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
+        where
+            D: FnOnce() -> U,
+            F: FnOnce(T) -> U,
+        {
+            loop {}
+        }
+        // endregion:fn
     }
 }
 // endregion:option
@@ -727,6 +762,20 @@ pub mod iter {
                     self
                 }
             }
+            pub struct IntoIter<T, const N: usize>([T; N]);
+            impl<T, const N: usize> IntoIterator for [T; N] {
+                type Item = T;
+                type IntoIter = IntoIter<T, N>;
+                fn into_iter(self) -> I {
+                    IntoIter(self)
+                }
+            }
+            impl<T, const N: usize> Iterator for IntoIter<T, N> {
+                type Item = T;
+                fn next(&mut self) -> Option<T> {
+                    loop {}
+                }
+            }
         }
         pub use self::collect::IntoIterator;
     }
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs
index 67bdad2aadd..729f84a8150 100644
--- a/crates/toolchain/src/lib.rs
+++ b/crates/toolchain/src/lib.rs
@@ -31,8 +31,9 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
     //      example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc
     // 2) `<executable_name>`
     //      example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH
-    // 3) `~/.cargo/bin/<executable_name>`
-    //      example: for cargo, this tries ~/.cargo/bin/cargo
+    // 3) `$CARGO_HOME/bin/<executable_name>`
+    //      where $CARGO_HOME defaults to ~/.cargo (see https://doc.rust-lang.org/cargo/guide/cargo-home.html)
+    //      example: for cargo, this tries $CARGO_HOME/bin/cargo, or ~/.cargo/bin/cargo if $CARGO_HOME is unset.
     //      It seems that this is a reasonable place to try for cargo, rustc, and rustup
     let env_var = executable_name.to_ascii_uppercase();
     if let Some(path) = env::var_os(env_var) {
@@ -43,8 +44,7 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
         return executable_name.into();
     }
 
-    if let Some(mut path) = home::home_dir() {
-        path.push(".cargo");
+    if let Some(mut path) = get_cargo_home() {
         path.push("bin");
         path.push(executable_name);
         if let Some(path) = probe(path) {
@@ -60,6 +60,19 @@ fn lookup_in_path(exec: &str) -> bool {
     env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some()
 }
 
+fn get_cargo_home() -> Option<PathBuf> {
+    if let Some(path) = env::var_os("CARGO_HOME") {
+        return Some(path.into());
+    }
+
+    if let Some(mut path) = home::home_dir() {
+        path.push(".cargo");
+        return Some(path);
+    }
+
+    None
+}
+
 fn probe(path: PathBuf) -> Option<PathBuf> {
     let with_extension = match env::consts::EXE_EXTENSION {
         "" => None,
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index c3623a5cc46..de142203208 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: d87477896dfe41d4
+lsp_ext.rs hash: 37f31ae648632897
 
 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:
@@ -527,6 +527,17 @@ Primarily for debugging, but very useful for all people working on rust-analyzer
 Returns a textual representation of the HIR of the function containing the cursor.
 For debugging or when working on rust-analyzer itself.
 
+## View Mir
+
+**Method:** `rust-analyzer/viewMir`
+
+**Request:** `TextDocumentPositionParams`
+
+**Response:** `string`
+
+Returns a textual representation of the MIR of the function containing the cursor.
+For debugging or when working on rust-analyzer itself.
+
 ## View File Text
 
 **Method:** `rust-analyzer/viewFileText`
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 50e3670a7a8..6937a7ed9a2 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -71,6 +71,11 @@ cargo check --quiet --workspace --message-format=json --all-targets
 Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 avoid checking unnecessary things.
 --
+[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`)::
++
+--
+Extra arguments that are passed to every cargo invocation.
+--
 [[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
 +
 --
@@ -537,6 +542,11 @@ Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closure
 --
 Whether to hide inlay type hints for constructors.
 --
+[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`)::
++
+--
+Enables the experimental support for interpreting tests.
+--
 [[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`)::
 +
 --
@@ -699,7 +709,10 @@ Additional arguments to `rustfmt`.
 +
 --
 Advanced option, fully override the command rust-analyzer uses for
-formatting.
+formatting. This should be the equivalent of `rustfmt` here, and
+not that of `cargo fmt`. The file contents will be passed on the
+standard input and the formatted result will be read from the
+standard output.
 --
 [[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`)::
 +
diff --git a/editors/code/package.json b/editors/code/package.json
index 3610e993f82..a3b1a3107d0 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -114,6 +114,11 @@
                 "title": "View Hir",
                 "category": "rust-analyzer (debug command)"
             },
+            {
+                "command": "rust-analyzer.viewMir",
+                "title": "View Mir",
+                "category": "rust-analyzer (debug command)"
+            },
             {
                 "command": "rust-analyzer.viewFileText",
                 "title": "View File Text (as seen by the server)",
@@ -226,7 +231,7 @@
             },
             {
                 "command": "rust-analyzer.openDocs",
-                "title": "Open docs under cursor",
+                "title": "Open Docs",
                 "category": "rust-analyzer"
             },
             {
@@ -236,7 +241,7 @@
             },
             {
                 "command": "rust-analyzer.peekTests",
-                "title": "Peek related tests",
+                "title": "Peek Related Tests",
                 "category": "rust-analyzer"
             },
             {
@@ -506,6 +511,14 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.cargo.extraArgs": {
+                    "markdownDescription": "Extra arguments that are passed to every cargo invocation.",
+                    "default": [],
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "rust-analyzer.cargo.extraEnv": {
                     "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.",
                     "default": {},
@@ -1110,6 +1123,11 @@
                     "default": false,
                     "type": "boolean"
                 },
+                "rust-analyzer.interpret.tests": {
+                    "markdownDescription": "Enables the experimental support for interpreting tests.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.joinLines.joinAssignments": {
                     "markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.",
                     "default": true,
@@ -1283,7 +1301,7 @@
                     }
                 },
                 "rust-analyzer.rustfmt.overrideCommand": {
-                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.",
+                    "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.",
                     "default": null,
                     "type": [
                         "null",
@@ -1869,8 +1887,13 @@
             "editor/context": [
                 {
                     "command": "rust-analyzer.peekTests",
-                    "when": "inRustProject",
+                    "when": "inRustProject && editorTextFocus && editorLangId == rust",
                     "group": "navigation@1000"
+                },
+                {
+                    "command": "rust-analyzer.openDocs",
+                    "when": "inRustProject && editorTextFocus && editorLangId == rust",
+                    "group": "navigation@1001"
                 }
             ]
         },
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 49a8ca4edba..f4a4579a92c 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -93,6 +93,14 @@ export function triggerParameterHints(_: CtxInit): Cmd {
     };
 }
 
+export function openLogs(ctx: CtxInit): Cmd {
+    return async () => {
+        if (ctx.client.outputChannel) {
+            ctx.client.outputChannel.show();
+        }
+    };
+}
+
 export function matchingBrace(ctx: CtxInit): Cmd {
     return async () => {
         const editor = ctx.activeRustEditor;
@@ -405,12 +413,11 @@ export function syntaxTree(ctx: CtxInit): Cmd {
     };
 }
 
-// Opens the virtual file that will show the HIR of the function containing the cursor position
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function viewHir(ctx: CtxInit): Cmd {
+function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
+    const viewXir = xir === "hir" ? "viewHir" : "viewMir";
+    const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
-        readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.rs");
+        readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`);
         readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
         constructor() {
             vscode.workspace.onDidChangeTextDocument(
@@ -452,7 +459,7 @@ export function viewHir(ctx: CtxInit): Cmd {
                 ),
                 position: client.code2ProtocolConverter.asPosition(rustEditor.selection.active),
             };
-            return client.sendRequest(ra.viewHir, params, ct);
+            return client.sendRequest(requestType, params, ct);
         }
 
         get onDidChange(): vscode.Event<vscode.Uri> {
@@ -461,7 +468,7 @@ export function viewHir(ctx: CtxInit): Cmd {
     })();
 
     ctx.pushExtCleanup(
-        vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-hir", tdcp)
+        vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
     );
 
     return async () => {
@@ -474,6 +481,20 @@ export function viewHir(ctx: CtxInit): Cmd {
     };
 }
 
+// Opens the virtual file that will show the HIR of the function containing the cursor position
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function viewHir(ctx: CtxInit): Cmd {
+    return viewHirOrMir(ctx, "hir");
+}
+
+// Opens the virtual file that will show the MIR of the function containing the cursor position
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function viewMir(ctx: CtxInit): Cmd {
+    return viewHirOrMir(ctx, "mir");
+}
+
 export function viewFileText(ctx: CtxInit): Cmd {
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
         readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index e2a30e0cc45..1708d47cee7 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -282,18 +282,18 @@ export class Ctx {
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
         let icon = "";
         const statusBar = this.statusBar;
+        statusBar.tooltip = new vscode.MarkdownString("", true);
+        statusBar.tooltip.isTrusted = true;
         switch (status.health) {
             case "ok":
-                statusBar.tooltip = (status.message ?? "Ready") + "\nClick to stop server.";
-                statusBar.command = "rust-analyzer.stopServer";
+                statusBar.tooltip.appendText(status.message ?? "Ready");
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 break;
             case "warning":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor(
                     "statusBarItem.warningBackground"
@@ -301,22 +301,32 @@ export class Ctx {
                 icon = "$(warning) ";
                 break;
             case "error":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
                 icon = "$(error) ";
                 break;
             case "stopped":
-                statusBar.tooltip = "Server is stopped.\nClick to start.";
-                statusBar.command = "rust-analyzer.startServer";
+                statusBar.tooltip.appendText("Server is stopped");
+                statusBar.tooltip.appendMarkdown(
+                    "\n\n[Start server](command:rust-analyzer.startServer)"
+                );
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 statusBar.text = `$(stop-circle) rust-analyzer`;
                 return;
         }
+        if (statusBar.tooltip.value) {
+            statusBar.tooltip.appendText("\n\n");
+        }
+        statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)");
+        statusBar.tooltip.appendMarkdown(
+            "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)"
+        );
+        statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)");
+        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
         if (!status.quiescent) icon = "$(sync~spin) ";
         statusBar.text = `${icon}rust-analyzer`;
     }
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index f6f5124dc41..400cd207d41 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -59,6 +59,9 @@ export const viewFileText = new lc.RequestType<lc.TextDocumentIdentifier, string
 export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
     "rust-analyzer/viewHir"
 );
+export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
+    "rust-analyzer/viewMir"
+);
 export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
     "rust-analyzer/viewItemTree"
 );
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 5987368e6e0..8a2412af849 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -158,6 +158,7 @@ function createCommands(): Record<string, CommandFactory> {
         parentModule: { enabled: commands.parentModule },
         syntaxTree: { enabled: commands.syntaxTree },
         viewHir: { enabled: commands.viewHir },
+        viewMir: { enabled: commands.viewMir },
         viewFileText: { enabled: commands.viewFileText },
         viewItemTree: { enabled: commands.viewItemTree },
         viewCrateGraph: { enabled: commands.viewCrateGraph },
@@ -187,5 +188,6 @@ function createCommands(): Record<string, CommandFactory> {
         runSingle: { enabled: commands.runSingle },
         showReferences: { enabled: commands.showReferences },
         triggerParameterHints: { enabled: commands.triggerParameterHints },
+        openLogs: { enabled: commands.openLogs },
     };
 }
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index e1ca4954280..eb70b88871e 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -156,19 +156,10 @@ export const getPathForExecutable = memoizeAsync(
 
         if (await lookupInPath(executableName)) return executableName;
 
-        try {
-            // hmm, `os.homedir()` seems to be infallible
-            // it is not mentioned in docs and cannot be inferred by the type signature...
-            const standardPath = vscode.Uri.joinPath(
-                vscode.Uri.file(os.homedir()),
-                ".cargo",
-                "bin",
-                executableName
-            );
-
+        const cargoHome = getCargoHome();
+        if (cargoHome) {
+            const standardPath = vscode.Uri.joinPath(cargoHome, "bin", executableName);
             if (await isFileAtUri(standardPath)) return standardPath.fsPath;
-        } catch (err) {
-            log.error("Failed to read the fs info", err);
         }
         return executableName;
     }
@@ -190,6 +181,21 @@ async function lookupInPath(exec: string): Promise<boolean> {
     return false;
 }
 
+function getCargoHome(): vscode.Uri | null {
+    const envVar = process.env["CARGO_HOME"];
+    if (envVar) return vscode.Uri.file(envVar);
+
+    try {
+        // hmm, `os.homedir()` seems to be infallible
+        // it is not mentioned in docs and cannot be inferred by the type signature...
+        return vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo");
+    } catch (err) {
+        log.error("Failed to read the fs info", err);
+    }
+
+    return null;
+}
+
 async function isFileAtPath(path: string): Promise<boolean> {
     return isFileAtUri(vscode.Uri.file(path));
 }
diff --git a/lib/la-arena/src/map.rs b/lib/la-arena/src/map.rs
index b9d491da3c0..7fff2b09c97 100644
--- a/lib/la-arena/src/map.rs
+++ b/lib/la-arena/src/map.rs
@@ -94,6 +94,12 @@ impl<T, V> ArenaMap<Idx<T>, V> {
             .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?)))
     }
 
+    /// Returns an iterator over the arena indexes and values in the map.
+    // FIXME: Implement `IntoIterator` trait.
+    pub fn into_iter(self) -> impl Iterator<Item = (Idx<T>, V)> {
+        self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?)))
+    }
+
     /// Gets the given key's corresponding entry in the map for in-place manipulation.
     pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
         let idx = Self::to_idx(idx);