Merge ref '6159a44067eb' from rust-lang/rust

Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 6159a44067ebce42b38f062cc7df267a1348e092
Filtered ref: 6ab1e12842abe4da734757701b2f17970e11f6b9
Upstream diff: https://github.com/rust-lang/rust/compare/c5dabe8cf798123087d094f06417f5a767ca73e8...6159a44067ebce42b38f062cc7df267a1348e092

This merge was created using https://github.com/rust-lang/josh-sync.
diff --git a/Cargo.lock b/Cargo.lock
index 5bc4d9f..c1dbe6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -187,69 +187,21 @@
 
 [[package]]
 name = "cargo-platform"
-version = "0.2.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4"
+checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
 dependencies = [
  "serde",
 ]
 
 [[package]]
-name = "cargo-util-schemas"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca"
-dependencies = [
- "semver",
- "serde",
- "serde-untagged",
- "serde-value",
- "thiserror 1.0.69",
- "toml",
- "unicode-xid",
- "url",
-]
-
-[[package]]
-name = "cargo-util-schemas"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
-dependencies = [
- "semver",
- "serde",
- "serde-untagged",
- "serde-value",
- "thiserror 2.0.16",
- "toml",
- "unicode-xid",
- "url",
-]
-
-[[package]]
 name = "cargo_metadata"
-version = "0.20.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502"
+checksum = "981a6f317983eec002839b90fae7411a85621410ae591a9cab2ecf5cb5744873"
 dependencies = [
  "camino",
  "cargo-platform",
- "cargo-util-schemas 0.2.0",
- "semver",
- "serde",
- "serde_json",
- "thiserror 2.0.16",
-]
-
-[[package]]
-name = "cargo_metadata"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
-dependencies = [
- "camino",
- "cargo-platform",
- "cargo-util-schemas 0.8.2",
  "semver",
  "serde",
  "serde_json",
@@ -624,17 +576,6 @@
 checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
 [[package]]
-name = "erased-serde"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
-dependencies = [
- "serde",
- "serde_core",
- "typeid",
-]
-
-[[package]]
 name = "errno"
 version = "0.3.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1717,15 +1658,6 @@
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
 [[package]]
-name = "ordered-float"
-version = "2.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
 name = "parking_lot"
 version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1937,7 +1869,7 @@
 name = "proc-macro-test"
 version = "0.0.0"
 dependencies = [
- "cargo_metadata 0.20.0",
+ "cargo_metadata",
 ]
 
 [[package]]
@@ -1978,7 +1910,7 @@
 dependencies = [
  "anyhow",
  "base-db",
- "cargo_metadata 0.21.0",
+ "cargo_metadata",
  "cfg",
  "expect-test",
  "intern",
@@ -1993,6 +1925,7 @@
  "span",
  "stdx",
  "temp-dir",
+ "toml",
  "toolchain",
  "tracing",
  "triomphe",
@@ -2343,7 +2276,7 @@
 dependencies = [
  "anyhow",
  "base64",
- "cargo_metadata 0.21.0",
+ "cargo_metadata",
  "cfg",
  "crossbeam-channel",
  "dhat",
@@ -2591,28 +2524,6 @@
 ]
 
 [[package]]
-name = "serde-untagged"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058"
-dependencies = [
- "erased-serde",
- "serde",
- "serde_core",
- "typeid",
-]
-
-[[package]]
-name = "serde-value"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
-dependencies = [
- "ordered-float",
- "serde",
-]
-
-[[package]]
 name = "serde_core"
 version = "1.0.226"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2659,11 +2570,11 @@
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.9"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
 dependencies = [
- "serde",
+ "serde_core",
 ]
 
 [[package]]
@@ -3042,44 +2953,42 @@
 
 [[package]]
 name = "toml"
-version = "0.8.23"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-name = "toml_datetime"
-version = "0.6.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.22.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
 dependencies = [
  "indexmap",
- "serde",
+ "serde_core",
  "serde_spanned",
  "toml_datetime",
- "toml_write",
+ "toml_parser",
+ "toml_writer",
  "winnow",
 ]
 
 [[package]]
-name = "toml_write"
-version = "0.1.2"
+name = "toml_datetime"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
 
 [[package]]
 name = "toolchain"
@@ -3181,12 +3090,6 @@
 checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
 
 [[package]]
-name = "typeid"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
-
-[[package]]
 name = "unarray"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3654,9 +3557,6 @@
 version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
-dependencies = [
- "memchr",
-]
 
 [[package]]
 name = "wit-bindgen"
diff --git a/Cargo.toml b/Cargo.toml
index 6f5ea44..35f2fe4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -106,7 +106,7 @@
 anyhow = "1.0.98"
 arrayvec = "0.7.6"
 bitflags = "2.9.1"
-cargo_metadata = "0.21.0"
+cargo_metadata = "0.23.0"
 camino = "1.1.10"
 crossbeam-channel = "0.5.15"
 dissimilar = "1.0.10"
@@ -134,10 +134,11 @@
 rowan = "=0.15.15"
 # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
 # on impls without it
-salsa = { version = "0.24.0", default-features = true, features = [
+salsa = { version = "0.24.0", default-features = false, features = [
     "rayon",
     "salsa_unstable",
     "macros",
+    "inventory",
 ] }
 salsa-macros = "0.24.0"
 semver = "1.0.26"
@@ -154,6 +155,7 @@
 smol_str = "0.3.2"
 temp-dir = "0.1.16"
 text-size = "1.1.1"
+toml = "0.9.8"
 tracing = "0.1.41"
 tracing-tree = "0.4.0"
 tracing-subscriber = { version = "0.3.20", default-features = false, features = [
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index cac7477..ffd82d5 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -867,6 +867,10 @@
     pub fn insert(&mut self, k: impl Into<String>, v: impl Into<String>) -> Option<String> {
         self.entries.insert(k.into(), v.into())
     }
+
+    pub fn contains_key(&self, arg: &str) -> bool {
+        self.entries.contains_key(arg)
+    }
 }
 
 impl From<Env> for Vec<(String, String)> {
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 0e411bc..90e0aa9 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -273,7 +273,7 @@
     fn transitive_rev_deps(&self, of: Crate) -> FxHashSet<Crate>;
 }
 
-pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
+fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Crate> {
     // There is a bit of duplication here and in `CrateGraphBuilder` in the same method, but it's not terrible
     // and removing that is a bit difficult.
     let mut worklist = vec![crate_id];
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 3794cb1..c13a7e9 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2409,6 +2409,7 @@
                 };
                 let start = range_part_lower(p.start());
                 let end = range_part_lower(p.end());
+                // FIXME: Exclusive ended pattern range is stabilised
                 Pat::Range { start, end }
             }
         };
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 5b9da3c..3b31883 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -510,7 +510,22 @@
     }
 
     fn print_expr(&mut self, expr: ExprId) {
+        self.print_expr_in(None, expr);
+    }
+
+    fn print_expr_in(&mut self, prec: Option<ast::prec::ExprPrecedence>, expr: ExprId) {
         let expr = &self.store[expr];
+        let needs_parens = match (prec, expr.precedence()) {
+            (Some(ast::prec::ExprPrecedence::LOr), ast::prec::ExprPrecedence::LOr) => false,
+            (Some(ast::prec::ExprPrecedence::LAnd), ast::prec::ExprPrecedence::LAnd) => false,
+            (Some(parent), prec) => prec.needs_parentheses_in(parent),
+            (None, _) => false,
+        };
+        let prec = Some(expr.precedence());
+
+        if needs_parens {
+            w!(self, "(");
+        }
 
         match expr {
             Expr::Missing => w!(self, "�"),
@@ -544,7 +559,7 @@
                 w!(self, "let ");
                 self.print_pat(*pat);
                 w!(self, " = ");
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
@@ -554,7 +569,7 @@
                 self.print_expr(*body);
             }
             Expr::Call { callee, args } => {
-                self.print_expr(*callee);
+                self.print_expr_in(prec, *callee);
                 w!(self, "(");
                 if !args.is_empty() {
                     self.indented(|p| {
@@ -567,7 +582,7 @@
                 w!(self, ")");
             }
             Expr::MethodCall { receiver, method_name, args, generic_args } => {
-                self.print_expr(*receiver);
+                self.print_expr_in(prec, *receiver);
                 w!(self, ".{}", method_name.display(self.db, self.edition));
                 if let Some(args) = generic_args {
                     w!(self, "::<");
@@ -616,26 +631,26 @@
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
-                    self.print_expr(*expr);
+                    self.print_expr_in(prec, *expr);
                 }
             }
             Expr::Return { expr } => {
                 w!(self, "return");
                 if let Some(expr) = expr {
                     self.whitespace();
-                    self.print_expr(*expr);
+                    self.print_expr_in(prec, *expr);
                 }
             }
             Expr::Become { expr } => {
                 w!(self, "become");
                 self.whitespace();
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
             Expr::Yield { expr } => {
                 w!(self, "yield");
                 if let Some(expr) = expr {
                     self.whitespace();
-                    self.print_expr(*expr);
+                    self.print_expr_in(prec, *expr);
                 }
             }
             Expr::Yeet { expr } => {
@@ -644,7 +659,7 @@
                 w!(self, "yeet");
                 if let Some(expr) = expr {
                     self.whitespace();
-                    self.print_expr(*expr);
+                    self.print_expr_in(prec, *expr);
                 }
             }
             Expr::RecordLit { path, fields, spread } => {
@@ -670,15 +685,15 @@
                 w!(self, "}}");
             }
             Expr::Field { expr, name } => {
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
                 w!(self, ".{}", name.display(self.db, self.edition));
             }
             Expr::Await { expr } => {
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
                 w!(self, ".await");
             }
             Expr::Cast { expr, type_ref } => {
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
                 w!(self, " as ");
                 self.print_type_ref(*type_ref);
             }
@@ -690,11 +705,11 @@
                 if mutability.is_mut() {
                     w!(self, "mut ");
                 }
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
             Expr::Box { expr } => {
                 w!(self, "box ");
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
             Expr::UnaryOp { expr, op } => {
                 let op = match op {
@@ -703,43 +718,32 @@
                     ast::UnaryOp::Neg => "-",
                 };
                 w!(self, "{}", op);
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
             Expr::BinaryOp { lhs, rhs, op } => {
-                let (bra, ket) = match op {
-                    None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""),
-                    _ => ("(", ")"),
-                };
-                w!(self, "{}", bra);
-                self.print_expr(*lhs);
-                w!(self, "{} ", ket);
+                self.print_expr_in(prec, *lhs);
+                self.whitespace();
                 match op {
                     Some(op) => w!(self, "{}", op),
                     None => w!(self, "�"), // :)
                 }
-                w!(self, " {}", bra);
-                self.print_expr(*rhs);
-                w!(self, "{}", ket);
+                self.whitespace();
+                self.print_expr_in(prec, *rhs);
             }
             Expr::Range { lhs, rhs, range_type } => {
                 if let Some(lhs) = lhs {
-                    w!(self, "(");
-                    self.print_expr(*lhs);
-                    w!(self, ") ");
+                    self.print_expr_in(prec, *lhs);
                 }
-                let range = match range_type {
-                    ast::RangeOp::Exclusive => "..",
-                    ast::RangeOp::Inclusive => "..=",
+                match range_type {
+                    ast::RangeOp::Exclusive => w!(self, ".."),
+                    ast::RangeOp::Inclusive => w!(self, "..="),
                 };
-                w!(self, "{}", range);
                 if let Some(rhs) = rhs {
-                    w!(self, "(");
-                    self.print_expr(*rhs);
-                    w!(self, ") ");
+                    self.print_expr_in(prec, *rhs);
                 }
             }
             Expr::Index { base, index } => {
-                self.print_expr(*base);
+                self.print_expr_in(prec, *base);
                 w!(self, "[");
                 self.print_expr(*index);
                 w!(self, "]");
@@ -826,9 +830,13 @@
             &Expr::Assignment { target, value } => {
                 self.print_pat(target);
                 w!(self, " = ");
-                self.print_expr(value);
+                self.print_expr_in(prec, value);
             }
         }
+
+        if needs_parens {
+            w!(self, ")");
+        }
     }
 
     fn print_block(
@@ -857,6 +865,7 @@
     }
 
     fn print_pat(&mut self, pat: PatId) {
+        let prec = Some(ast::prec::ExprPrecedence::Shift);
         let pat = &self.store[pat];
 
         match pat {
@@ -930,11 +939,11 @@
             }
             Pat::Range { start, end } => {
                 if let Some(start) = start {
-                    self.print_expr(*start);
+                    self.print_expr_in(prec, *start);
                 }
                 w!(self, "..=");
                 if let Some(end) = end {
-                    self.print_expr(*end);
+                    self.print_expr_in(prec, *end);
                 }
             }
             Pat::Slice { prefix, slice, suffix } => {
@@ -954,7 +963,7 @@
                 w!(self, "]");
             }
             Pat::Path(path) => self.print_path(path),
-            Pat::Lit(expr) => self.print_expr(*expr),
+            Pat::Lit(expr) => self.print_expr_in(prec, *expr),
             Pat::Bind { id, subpat } => {
                 self.print_binding(*id);
                 if let Some(pat) = subpat {
@@ -996,7 +1005,7 @@
                 self.print_expr(*c);
             }
             Pat::Expr(expr) => {
-                self.print_expr(*expr);
+                self.print_expr_in(prec, *expr);
             }
         }
     }
@@ -1181,7 +1190,9 @@
     pub(crate) fn print_generic_arg(&mut self, arg: &GenericArg) {
         match arg {
             GenericArg::Type(ty) => self.print_type_ref(*ty),
-            GenericArg::Const(ConstRef { expr }) => self.print_expr(*expr),
+            GenericArg::Const(ConstRef { expr }) => {
+                self.print_expr_in(Some(ast::prec::ExprPrecedence::Unambiguous), *expr)
+            }
             GenericArg::Lifetime(lt) => self.print_lifetime_ref(*lt),
         }
     }
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index c31428b..0a982b9 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -159,7 +159,7 @@
     expect![[r#"
         fn main() {
             match builtin#lang(into_iter)(
-                (0) ..(10) ,
+                0..10,
             ) {
                 mut <ra@gennew>11 => loop {
                     match builtin#lang(next)(
@@ -590,3 +590,30 @@
         _ => {}
     }
 }
+
+#[test]
+fn print_hir_precedences() {
+    let (db, body, def) = lower(
+        r#"
+fn main() {
+    _ = &(1 - (2 - 3) + 4 * 5 * (6 + 7));
+    _ = 1 + 2 < 3 && true && 4 < 5 && (a || b || c) || d && e;
+    if let _ = 2 && true && let _ = 3 {}
+    break a && b || (return) || (return 2);
+    let r = &2;
+    let _ = &mut (*r as i32)
+}
+"#,
+    );
+
+    expect![[r#"
+        fn main() {
+            _ = &((1 - (2 - 3)) + (4 * 5) * (6 + 7));
+            _ = 1 + 2 < 3 && true && 4 < 5 && (a || b || c) || d && e;
+            if let _ = 2 && true && let _ = 3 {}
+            break a && b || (return) || (return 2);
+            let r = &2;
+            let _ = &mut (*r as i32);
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index e70cd2c..8ca8308 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -322,6 +322,72 @@
     InlineAsm(InlineAsm),
 }
 
+impl Expr {
+    pub fn precedence(&self) -> ast::prec::ExprPrecedence {
+        use ast::prec::ExprPrecedence;
+
+        match self {
+            Expr::Array(_)
+            | Expr::InlineAsm(_)
+            | Expr::Block { .. }
+            | Expr::Unsafe { .. }
+            | Expr::Const(_)
+            | Expr::Async { .. }
+            | Expr::If { .. }
+            | Expr::Literal(_)
+            | Expr::Loop { .. }
+            | Expr::Match { .. }
+            | Expr::Missing
+            | Expr::Path(_)
+            | Expr::RecordLit { .. }
+            | Expr::Tuple { .. }
+            | Expr::OffsetOf(_)
+            | Expr::Underscore => ExprPrecedence::Unambiguous,
+
+            Expr::Await { .. }
+            | Expr::Call { .. }
+            | Expr::Field { .. }
+            | Expr::Index { .. }
+            | Expr::MethodCall { .. } => ExprPrecedence::Postfix,
+
+            Expr::Box { .. } | Expr::Let { .. } | Expr::UnaryOp { .. } | Expr::Ref { .. } => {
+                ExprPrecedence::Prefix
+            }
+
+            Expr::Cast { .. } => ExprPrecedence::Cast,
+
+            Expr::BinaryOp { op, .. } => match op {
+                None => ExprPrecedence::Unambiguous,
+                Some(BinaryOp::LogicOp(LogicOp::Or)) => ExprPrecedence::LOr,
+                Some(BinaryOp::LogicOp(LogicOp::And)) => ExprPrecedence::LAnd,
+                Some(BinaryOp::CmpOp(_)) => ExprPrecedence::Compare,
+                Some(BinaryOp::Assignment { .. }) => ExprPrecedence::Assign,
+                Some(BinaryOp::ArithOp(arith_op)) => match arith_op {
+                    ArithOp::Add | ArithOp::Sub => ExprPrecedence::Sum,
+                    ArithOp::Mul | ArithOp::Div | ArithOp::Rem => ExprPrecedence::Product,
+                    ArithOp::Shl | ArithOp::Shr => ExprPrecedence::Shift,
+                    ArithOp::BitXor => ExprPrecedence::BitXor,
+                    ArithOp::BitOr => ExprPrecedence::BitOr,
+                    ArithOp::BitAnd => ExprPrecedence::BitAnd,
+                },
+            },
+
+            Expr::Assignment { .. } => ExprPrecedence::Assign,
+
+            Expr::Become { .. }
+            | Expr::Break { .. }
+            | Expr::Closure { .. }
+            | Expr::Return { .. }
+            | Expr::Yeet { .. }
+            | Expr::Yield { .. } => ExprPrecedence::Jump,
+
+            Expr::Continue { .. } => ExprPrecedence::Unambiguous,
+
+            Expr::Range { .. } => ExprPrecedence::Range,
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct OffsetOf {
     pub container: TypeRefId,
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index abcf0a3..b5afbf3 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -708,6 +708,20 @@
         self.item_scope_().0
     }
 
+    #[inline]
+    pub fn top_level_def_map(&self) -> &'db DefMap {
+        self.module_scope.def_map
+    }
+
+    #[inline]
+    pub fn is_visible(&self, db: &dyn DefDatabase, visibility: Visibility) -> bool {
+        visibility.is_visible_from_def_map(
+            db,
+            self.module_scope.def_map,
+            self.module_scope.module_id,
+        )
+    }
+
     pub fn generic_def(&self) -> Option<GenericDefId> {
         self.scopes().find_map(|scope| match scope {
             Scope::GenericParams { def, .. } => Some(*def),
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 392b0b0..d21108f 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -13,11 +13,11 @@
 use crate::{
     TraitEnvironment,
     db::HirDatabase,
-    infer::unify::InferenceTable,
+    infer::InferenceContext,
     next_solver::{
-        Canonical, TraitRef, Ty, TyKind,
+        Canonical, DbInterner, ParamEnv, TraitRef, Ty, TyKind, TypingMode,
         infer::{
-            InferOk,
+            DbInternerInferExt, InferCtxt,
             traits::{Obligation, ObligationCause, PredicateObligations},
         },
         obligation_ctxt::ObligationCtxt,
@@ -38,14 +38,15 @@
     env: Arc<TraitEnvironment<'db>>,
     ty: Canonical<'db, Ty<'db>>,
 ) -> impl Iterator<Item = Ty<'db>> + use<'db> {
-    let mut table = InferenceTable::new(db, env, None);
-    let ty = table.instantiate_canonical(ty);
-    let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
+    let interner = DbInterner::new_with(db, Some(env.krate), env.block);
+    let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+    let (ty, _) = infcx.instantiate_canonical(&ty);
+    let autoderef = Autoderef::new(&infcx, &env, ty);
     let mut v = Vec::new();
-    while let Some((ty, _steps)) = autoderef.next() {
+    for (ty, _steps) in autoderef {
         // `ty` may contain unresolved inference variables. Since there's no chance they would be
         // resolved, just replace with fallback type.
-        let resolved = autoderef.table.resolve_completely(ty);
+        let resolved = infcx.resolve_vars_if_possible(ty).replace_infer_with_error(interner);
 
         // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
         // would revisit some already visited types. Stop here to avoid duplication.
@@ -105,13 +106,48 @@
     trait_target: TypeAliasId,
 }
 
+// We use a trait here and a generic implementation unfortunately, because sometimes (specifically
+// in place_op.rs), you need to have mutable access to the `InferenceContext` while the `Autoderef`
+// borrows it.
+pub(crate) trait AutoderefCtx<'db> {
+    fn infcx(&self) -> &InferCtxt<'db>;
+    fn env(&self) -> &TraitEnvironment<'db>;
+}
+
+pub(crate) struct DefaultAutoderefCtx<'a, 'db> {
+    infcx: &'a InferCtxt<'db>,
+    env: &'a TraitEnvironment<'db>,
+}
+impl<'db> AutoderefCtx<'db> for DefaultAutoderefCtx<'_, 'db> {
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        self.infcx
+    }
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        self.env
+    }
+}
+
+pub(crate) struct InferenceContextAutoderefCtx<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>);
+impl<'db> AutoderefCtx<'db> for InferenceContextAutoderefCtx<'_, '_, 'db> {
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        &self.0.table.infer_ctxt
+    }
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        &self.0.table.trait_env
+    }
+}
+
 /// Recursively dereference a type, considering both built-in
 /// dereferences (`*`) and the `Deref` trait.
 /// Although called `Autoderef` it can be configured to use the
 /// `Receiver` trait instead of the `Deref` trait.
-pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
+pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind)>> {
     // Meta infos:
-    pub(crate) table: &'a mut InferenceTable<'db>,
+    ctx: Ctx,
     traits: Option<AutoderefTraits>,
 
     // Current state:
@@ -122,7 +158,16 @@
     use_receiver_trait: bool,
 }
 
-impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> {
+pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
+    GeneralAutoderef<'db, DefaultAutoderefCtx<'a, 'db>, Steps>;
+pub(crate) type InferenceContextAutoderef<'a, 'b, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
+    GeneralAutoderef<'db, InferenceContextAutoderefCtx<'a, 'b, 'db>, Steps>;
+
+impl<'db, Ctx, Steps> Iterator for GeneralAutoderef<'db, Ctx, Steps>
+where
+    Ctx: AutoderefCtx<'db>,
+    Steps: TrackAutoderefSteps<'db>,
+{
     type Item = (Ty<'db>, usize);
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -148,26 +193,26 @@
         // be better to skip this clause and use the Overloaded case only, since &T
         // and &mut T implement Receiver. But built-in derefs apply equally to Receiver
         // and Deref, and this has benefits for const and the emitted MIR.
-        let (kind, new_ty) = if let Some(ty) =
-            self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers)
-        {
-            debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty));
-            // NOTE: we may still need to normalize the built-in deref in case
-            // we have some type like `&<Ty as Trait>::Assoc`, since users of
-            // autoderef expect this type to have been structurally normalized.
-            if let TyKind::Alias(..) = ty.kind() {
-                let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?;
-                self.state.obligations.extend(obligations);
-                (AutoderefKind::Builtin, normalized_ty)
+        let (kind, new_ty) =
+            if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) {
+                debug_assert_eq!(ty, self.infcx().resolve_vars_if_possible(ty));
+                // NOTE: we may still need to normalize the built-in deref in case
+                // we have some type like `&<Ty as Trait>::Assoc`, since users of
+                // autoderef expect this type to have been structurally normalized.
+                if let TyKind::Alias(..) = ty.kind() {
+                    let (normalized_ty, obligations) =
+                        structurally_normalize_ty(self.infcx(), self.env().env, ty)?;
+                    self.state.obligations.extend(obligations);
+                    (AutoderefKind::Builtin, normalized_ty)
+                } else {
+                    (AutoderefKind::Builtin, ty)
+                }
+            } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
+                // The overloaded deref check already normalizes the pointee type.
+                (AutoderefKind::Overloaded, ty)
             } else {
-                (AutoderefKind::Builtin, ty)
-            }
-        } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
-            // The overloaded deref check already normalizes the pointee type.
-            (AutoderefKind::Overloaded, ty)
-        } else {
-            return None;
-        };
+                return None;
+            };
 
         self.state.steps.push(self.state.cur_ty, kind);
         debug!(
@@ -183,34 +228,84 @@
 }
 
 impl<'a, 'db> Autoderef<'a, 'db> {
-    pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
-        Self::new_impl(table, base_ty)
+    #[inline]
+    pub(crate) fn new_with_tracking(
+        infcx: &'a InferCtxt<'db>,
+        env: &'a TraitEnvironment<'db>,
+        base_ty: Ty<'db>,
+    ) -> Self {
+        Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
+    }
+}
+
+impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> {
+    #[inline]
+    pub(crate) fn new_from_inference_context(
+        ctx: &'a mut InferenceContext<'b, 'db>,
+        base_ty: Ty<'db>,
+    ) -> Self {
+        Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty)
+    }
+
+    #[inline]
+    pub(crate) fn ctx(&mut self) -> &mut InferenceContext<'b, 'db> {
+        self.ctx.0
     }
 }
 
 impl<'a, 'db> Autoderef<'a, 'db, usize> {
-    pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
-        Self::new_impl(table, base_ty)
+    #[inline]
+    pub(crate) fn new(
+        infcx: &'a InferCtxt<'db>,
+        env: &'a TraitEnvironment<'db>,
+        base_ty: Ty<'db>,
+    ) -> Self {
+        Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty)
     }
 }
 
-impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> {
-    fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self {
-        Autoderef {
+impl<'db, Ctx, Steps> GeneralAutoderef<'db, Ctx, Steps>
+where
+    Ctx: AutoderefCtx<'db>,
+    Steps: TrackAutoderefSteps<'db>,
+{
+    #[inline]
+    fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self {
+        GeneralAutoderef {
             state: AutoderefSnapshot {
                 steps: Steps::default(),
-                cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty),
+                cur_ty: ctx.infcx().resolve_vars_if_possible(base_ty),
                 obligations: PredicateObligations::new(),
                 at_start: true,
                 reached_recursion_limit: false,
             },
-            table,
+            ctx,
             traits: None,
             include_raw_pointers: false,
             use_receiver_trait: false,
         }
     }
 
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        self.ctx.infcx()
+    }
+
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        self.ctx.env()
+    }
+
+    #[inline]
+    fn interner(&self) -> DbInterner<'db> {
+        self.infcx().interner
+    }
+
+    #[inline]
+    fn db(&self) -> &'db dyn HirDatabase {
+        self.interner().db
+    }
+
     fn autoderef_traits(&mut self) -> Option<AutoderefTraits> {
         match &mut self.traits {
             Some(it) => Some(*it),
@@ -219,25 +314,23 @@
                     (|| {
                         Some(AutoderefTraits {
                             trait_: LangItem::Receiver
-                                .resolve_trait(self.table.db, self.table.trait_env.krate)?,
+                                .resolve_trait(self.db(), self.env().krate)?,
                             trait_target: LangItem::ReceiverTarget
-                                .resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
+                                .resolve_type_alias(self.db(), self.env().krate)?,
                         })
                     })()
                     .or_else(|| {
                         Some(AutoderefTraits {
-                            trait_: LangItem::Deref
-                                .resolve_trait(self.table.db, self.table.trait_env.krate)?,
+                            trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
                             trait_target: LangItem::DerefTarget
-                                .resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
+                                .resolve_type_alias(self.db(), self.env().krate)?,
                         })
                     })?
                 } else {
                     AutoderefTraits {
-                        trait_: LangItem::Deref
-                            .resolve_trait(self.table.db, self.table.trait_env.krate)?,
+                        trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?,
                         trait_target: LangItem::DerefTarget
-                            .resolve_type_alias(self.table.db, self.table.trait_env.krate)?,
+                            .resolve_type_alias(self.db(), self.env().krate)?,
                     }
                 };
                 Some(*self.traits.insert(traits))
@@ -247,31 +340,32 @@
 
     fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option<Ty<'db>> {
         debug!("overloaded_deref_ty({:?})", ty);
-        let interner = self.table.interner();
+        let interner = self.interner();
 
         // <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk.
         let AutoderefTraits { trait_, trait_target } = self.autoderef_traits()?;
 
         let trait_ref = TraitRef::new(interner, trait_.into(), [ty]);
         let obligation =
-            Obligation::new(interner, ObligationCause::new(), self.table.trait_env.env, trait_ref);
+            Obligation::new(interner, ObligationCause::new(), self.env().env, trait_ref);
         // We detect whether the self type implements `Deref` before trying to
         // structurally normalize. We use `predicate_may_hold_opaque_types_jank`
         // to support not-yet-defined opaque types. It will succeed for `impl Deref`
         // but fail for `impl OtherTrait`.
-        if !self.table.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation) {
+        if !self.infcx().predicate_may_hold_opaque_types_jank(&obligation) {
             debug!("overloaded_deref_ty: cannot match obligation");
             return None;
         }
 
         let (normalized_ty, obligations) = structurally_normalize_ty(
-            self.table,
+            self.infcx(),
+            self.env().env,
             Ty::new_projection(interner, trait_target.into(), [ty]),
         )?;
         debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
         self.state.obligations.extend(obligations);
 
-        Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty))
+        Some(self.infcx().resolve_vars_if_possible(normalized_ty))
     }
 
     /// Returns the final type we ended up with, which may be an unresolved
@@ -292,7 +386,6 @@
         &self.state.steps
     }
 
-    #[expect(dead_code)]
     pub(crate) fn reached_recursion_limit(&self) -> bool {
         self.state.reached_recursion_limit
     }
@@ -316,12 +409,12 @@
 }
 
 fn structurally_normalize_ty<'db>(
-    table: &InferenceTable<'db>,
+    infcx: &InferCtxt<'db>,
+    param_env: ParamEnv<'db>,
     ty: Ty<'db>,
 ) -> Option<(Ty<'db>, PredicateObligations<'db>)> {
-    let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
-    let Ok(normalized_ty) =
-        ocx.structurally_normalize_ty(&ObligationCause::misc(), table.trait_env.env, ty)
+    let mut ocx = ObligationCtxt::new(infcx);
+    let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty)
     else {
         // We shouldn't have errors here in the old solver, except for
         // evaluate/fulfill mismatches, but that's not a reason for an ICE.
@@ -334,17 +427,3 @@
 
     Some((normalized_ty, ocx.into_pending_obligations()))
 }
-
-pub(crate) fn overloaded_deref_ty<'db>(
-    table: &InferenceTable<'db>,
-    ty: Ty<'db>,
-) -> Option<InferOk<'db, Ty<'db>>> {
-    let interner = table.interner();
-
-    let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?;
-
-    let (normalized_ty, obligations) =
-        structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?;
-
-    Some(InferOk { value: normalized_ty, obligations })
-}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index f1aa06d..70185bb 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -851,6 +851,7 @@
 fn loops() {
     check_number(
         r#"
+    //- minicore: add, builtin_impls
     const GOAL: u8 = {
         let mut x = 0;
         loop {
@@ -871,6 +872,7 @@
     );
     check_number(
         r#"
+    //- minicore: add, builtin_impls
     const GOAL: u8 = {
         let mut x = 0;
         loop {
@@ -885,6 +887,7 @@
     );
     check_number(
         r#"
+    //- minicore: add, builtin_impls
     const GOAL: u8 = {
         'a: loop {
             let x = 'b: loop {
@@ -907,7 +910,7 @@
     );
     check_number(
         r#"
-    //- minicore: add
+    //- minicore: add, builtin_impls
     const GOAL: u8 = {
         let mut x = 0;
         'a: loop {
@@ -1277,7 +1280,7 @@
 fn destructing_assignment() {
     check_number(
         r#"
-    //- minicore: add
+    //- minicore: add, builtin_impls
     const fn f(i: &mut u8) -> &mut u8 {
         *i += 1;
         i
@@ -1469,11 +1472,11 @@
 fn options() {
     check_number(
         r#"
-    //- minicore: option
+    //- minicore: option, add, builtin_impls
     const GOAL: u8 = {
         let x = Some(2);
         match x {
-            Some(y) => 2 * y,
+            Some(y) => 2 + y,
             _ => 10,
         }
     };
@@ -1482,7 +1485,7 @@
     );
     check_number(
         r#"
-    //- minicore: option
+    //- minicore: option, add, builtin_impls
     fn f(x: Option<Option<i32>>) -> i32 {
         if let Some(y) = x && let Some(z) = y {
             z
@@ -1498,11 +1501,11 @@
     );
     check_number(
         r#"
-    //- minicore: option
+    //- minicore: option, add, builtin_impls
     const GOAL: u8 = {
         let x = None;
         match x {
-            Some(y) => 2 * y,
+            Some(y) => 2 + y,
             _ => 10,
         }
     };
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 9b58abb..98f4209 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -3,15 +3,12 @@
 
 use base_db::{Crate, target::TargetLoadError};
 use hir_def::{
-    AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
-    GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
-    TypeAliasId, TypeOrConstParamId, VariantId, db::DefDatabase, hir::ExprId,
-    layout::TargetDataLayout,
+    AdtId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId,
+    GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId,
+    TypeOrConstParamId, VariantId, db::DefDatabase, hir::ExprId, layout::TargetDataLayout,
 };
-use hir_expand::name::Name;
 use la_arena::ArenaMap;
 use salsa::plumbing::AsId;
-use smallvec::SmallVec;
 use triomphe::Arc;
 
 use crate::{
@@ -19,8 +16,7 @@
     consteval::ConstEvalError,
     dyn_compatibility::DynCompatibilityViolation,
     layout::{Layout, LayoutError},
-    lower::{Diagnostics, GenericDefaults, GenericPredicates, ImplTraits},
-    method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
+    lower::{Diagnostics, GenericDefaults},
     mir::{BorrowckResult, MirBody, MirLowerError},
     next_solver::{Const, EarlyBinder, GenericArgs, PolyFnSig, TraitRef, Ty, VariancesOf},
 };
@@ -190,43 +186,6 @@
         def: CallableDefId,
     ) -> EarlyBinder<'db, PolyFnSig<'db>>;
 
-    #[salsa::invoke(crate::lower::return_type_impl_traits)]
-    fn return_type_impl_traits<'db>(
-        &'db self,
-        def: FunctionId,
-    ) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>>;
-
-    #[salsa::invoke(crate::lower::type_alias_impl_traits)]
-    fn type_alias_impl_traits<'db>(
-        &'db self,
-        def: TypeAliasId,
-    ) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>>;
-
-    #[salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)]
-    fn generic_predicates_without_parent_with_diagnostics<'db>(
-        &'db self,
-        def: GenericDefId,
-    ) -> (GenericPredicates<'db>, Diagnostics);
-
-    #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
-    #[salsa::transparent]
-    fn generic_predicates_without_parent<'db>(
-        &'db self,
-        def: GenericDefId,
-    ) -> GenericPredicates<'db>;
-
-    #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
-    #[salsa::cycle(cycle_result = crate::lower::generic_predicates_for_param_cycle_result)]
-    fn generic_predicates_for_param<'db>(
-        &'db self,
-        def: GenericDefId,
-        param_id: TypeOrConstParamId,
-        assoc_name: Option<Name>,
-    ) -> GenericPredicates<'db>;
-
-    #[salsa::invoke(crate::lower::generic_predicates_query)]
-    fn generic_predicates<'db>(&'db self, def: GenericDefId) -> GenericPredicates<'db>;
-
     #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
     #[salsa::transparent]
     fn trait_environment_for_body<'db>(&'db self, def: DefWithBodyId)
@@ -249,32 +208,6 @@
     #[salsa::transparent]
     fn generic_defaults<'db>(&'db self, def: GenericDefId) -> GenericDefaults<'db>;
 
-    #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
-    fn inherent_impls_in_crate(&self, krate: Crate) -> Arc<InherentImpls>;
-
-    #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
-    fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
-
-    /// Collects all crates in the dependency graph that have impls for the
-    /// given fingerprint. This is only used for primitive types and types
-    /// annotated with `rustc_has_incoherent_inherent_impls`; for other types
-    /// we just look at the crate where the type is defined.
-    #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)]
-    fn incoherent_inherent_impl_crates(
-        &self,
-        krate: Crate,
-        fp: TyFingerprint,
-    ) -> SmallVec<[Crate; 2]>;
-
-    #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
-    fn trait_impls_in_crate(&self, krate: Crate) -> Arc<TraitImpls>;
-
-    #[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
-    fn trait_impls_in_block(&self, block: BlockId) -> Option<Arc<TraitImpls>>;
-
-    #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
-    fn trait_impls_in_deps(&self, krate: Crate) -> Arc<[Arc<TraitImpls>]>;
-
     // Interned IDs for solver integration
     #[salsa::interned]
     fn intern_impl_trait_id(&self, id: ImplTraitId<'_>) -> InternedOpaqueTyId;
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 0815e62..f6992df 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -563,6 +563,10 @@
             cov_mark::hit!(extern_static_incorrect_case_ignored);
             return;
         }
+        if self.db.attrs(static_id.into()).by_key(sym::no_mangle).exists() {
+            cov_mark::hit!(no_mangle_static_incorrect_case_ignored);
+            return;
+        }
 
         self.create_incorrect_case_diagnostic_for_item_name(
             static_id,
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 0a37966..3b37dc8 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -52,6 +52,7 @@
     db::{HirDatabase, InternedClosure, InternedCoroutine},
     generics::generics,
     layout::Layout,
+    lower::GenericPredicates,
     mir::pad16,
     next_solver::{
         AliasTy, Clause, ClauseKind, Const, ConstKind, DbInterner, EarlyBinder,
@@ -625,23 +626,20 @@
     {
         // FIXME: We shouldn't use `param.id`, it should be removed. We should know the
         // `GenericDefId` from the formatted type (store it inside the `HirFormatter`).
-        let bounds =
-            f.db.generic_predicates(param.id.parent())
-                .instantiate_identity()
-                .into_iter()
-                .flatten()
-                .filter(|wc| {
-                    let ty = match wc.kind().skip_binder() {
-                        ClauseKind::Trait(tr) => tr.self_ty(),
-                        ClauseKind::TypeOutlives(t) => t.0,
-                        _ => return false,
-                    };
-                    let TyKind::Alias(AliasTyKind::Projection, a) = ty.kind() else {
-                        return false;
-                    };
-                    a == *alias
-                })
-                .collect::<Vec<_>>();
+        let bounds = GenericPredicates::query_all(f.db, param.id.parent())
+            .iter_identity_copied()
+            .filter(|wc| {
+                let ty = match wc.kind().skip_binder() {
+                    ClauseKind::Trait(tr) => tr.self_ty(),
+                    ClauseKind::TypeOutlives(t) => t.0,
+                    _ => return false,
+                };
+                let TyKind::Alias(AliasTyKind::Projection, a) = ty.kind() else {
+                    return false;
+                };
+                a == *alias
+            })
+            .collect::<Vec<_>>();
         if !bounds.is_empty() {
             return f.format_bounds_with(*alias, |f| {
                 write_bounds_like_dyn_trait_with_prefix(
@@ -1122,13 +1120,8 @@
                             _ => unreachable!(),
                         };
                         let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id);
-                        if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id {
-                            let datas = db
-                                .return_type_impl_traits(func)
-                                .expect("impl trait id without data");
-                            let data = (*datas)
-                                .as_ref()
-                                .map_bound(|rpit| &rpit.impl_traits[idx].predicates);
+                        if let ImplTraitId::ReturnTypeImplTrait(func, _) = impl_trait_id {
+                            let data = impl_trait_id.predicates(db);
                             let bounds =
                                 || data.iter_instantiated_copied(f.interner, ty.args.as_slice());
                             let mut len = bounds().count();
@@ -1354,43 +1347,24 @@
                     ));
                 }
                 let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id);
-                match impl_trait_id {
-                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                        let datas =
-                            db.return_type_impl_traits(func).expect("impl trait id without data");
-                        let data =
-                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
-                        let bounds = data
-                            .iter_instantiated_copied(interner, alias_ty.args.as_slice())
-                            .collect::<Vec<_>>();
-                        let krate = func.krate(db);
-                        write_bounds_like_dyn_trait_with_prefix(
-                            f,
-                            "impl",
-                            Either::Left(*self),
-                            &bounds,
-                            SizedByDefault::Sized { anchor: krate },
-                        )?;
+                let data = impl_trait_id.predicates(db);
+                let bounds = data
+                    .iter_instantiated_copied(interner, alias_ty.args.as_slice())
+                    .collect::<Vec<_>>();
+                let krate = match impl_trait_id {
+                    ImplTraitId::ReturnTypeImplTrait(func, _) => {
+                        func.krate(db)
                         // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
                     }
-                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
-                        let datas =
-                            db.type_alias_impl_traits(alias).expect("impl trait id without data");
-                        let data =
-                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
-                        let bounds = data
-                            .iter_instantiated_copied(interner, alias_ty.args.as_slice())
-                            .collect::<Vec<_>>();
-                        let krate = alias.krate(db);
-                        write_bounds_like_dyn_trait_with_prefix(
-                            f,
-                            "impl",
-                            Either::Left(*self),
-                            &bounds,
-                            SizedByDefault::Sized { anchor: krate },
-                        )?;
-                    }
-                }
+                    ImplTraitId::TypeAliasImplTrait(alias, _) => alias.krate(db),
+                };
+                write_bounds_like_dyn_trait_with_prefix(
+                    f,
+                    "impl",
+                    Either::Left(*self),
+                    &bounds,
+                    SizedByDefault::Sized { anchor: krate },
+                )?;
             }
             TyKind::Closure(id, substs) => {
                 let id = id.0;
@@ -1541,11 +1515,8 @@
                             )?
                         }
                         TypeParamProvenance::ArgumentImplTrait => {
-                            let bounds = db
-                                .generic_predicates(param.id.parent())
-                                .instantiate_identity()
-                                .into_iter()
-                                .flatten()
+                            let bounds = GenericPredicates::query_all(f.db, param.id.parent())
+                                .iter_identity_copied()
                                 .filter(|wc| match wc.kind().skip_binder() {
                                     ClauseKind::Trait(tr) => tr.self_ty() == *self,
                                     ClauseKind::Projection(proj) => proj.self_ty() == *self,
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index b09d1fb..522d12d 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -9,9 +9,9 @@
 use crate::{
     TraitEnvironment, consteval,
     db::HirDatabase,
-    method_resolution::TyFingerprint,
+    method_resolution::TraitImpls,
     next_solver::{
-        Ty, TyKind,
+        SimplifiedType, Ty, TyKind,
         infer::{InferCtxt, traits::ObligationCause},
         obligation_ctxt::ObligationCtxt,
     },
@@ -27,13 +27,13 @@
         return false;
     };
     let impls = match module.containing_block() {
-        Some(block) => match db.trait_impls_in_block(block) {
+        Some(block) => match TraitImpls::for_block(db, block) {
             Some(it) => it,
             None => return false,
         },
-        None => db.trait_impls_in_crate(module.krate()),
+        None => &**TraitImpls::for_crate(db, module.krate()),
     };
-    impls.for_trait_and_self_ty(drop_trait, TyFingerprint::Adt(adt)).next().is_some()
+    !impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).is_empty()
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index 437141e..1bd5981 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -18,7 +18,7 @@
 use crate::{
     ImplTraitId,
     db::{HirDatabase, InternedOpaqueTyId},
-    lower::associated_ty_item_bounds,
+    lower::{GenericPredicates, associated_ty_item_bounds},
     next_solver::{
         Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, ParamTy,
         SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, mk_param,
@@ -136,11 +136,11 @@
     };
 
     let interner = DbInterner::new_with(db, Some(krate), None);
-    let predicates = db.generic_predicates(def);
+    let predicates = GenericPredicates::query_explicit(db, def);
     // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to
     // rust-analyzer yet
     // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490
-    elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| {
+    elaborate::elaborate(interner, predicates.iter_identity_copied()).any(|pred| {
         match pred.kind().skip_binder() {
             ClauseKind::Trait(trait_pred) => {
                 if sized == trait_pred.def_id().0
@@ -162,8 +162,8 @@
 // but we don't have good way to render such locations.
 // So, just return single boolean value for existence of such `Self` reference
 fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool {
-    db.generic_predicates(trait_.into())
-        .iter()
+    GenericPredicates::query_explicit(db, trait_.into())
+        .iter_identity_copied()
         .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No))
 }
 
@@ -199,7 +199,7 @@
 fn predicate_references_self<'db>(
     db: &'db dyn HirDatabase,
     trait_: TraitId,
-    predicate: &Clause<'db>,
+    predicate: Clause<'db>,
     allow_self_projection: AllowSelfProjection,
 ) -> bool {
     match predicate.kind().skip_binder() {
@@ -363,8 +363,8 @@
         cb(MethodViolationCode::UndispatchableReceiver)?;
     }
 
-    let predicates = &*db.generic_predicates_without_parent(func.into());
-    for pred in predicates {
+    let predicates = GenericPredicates::query_own(db, func.into());
+    for pred in predicates.iter_identity_copied() {
         let pred = pred.kind().skip_binder();
 
         if matches!(pred, ClauseKind::TypeOutlives(_)) {
@@ -440,7 +440,7 @@
     let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty);
 
     let param_env = {
-        let generic_predicates = &*db.generic_predicates(func.into());
+        let generic_predicates = GenericPredicates::query_all(db, func.into());
 
         // Self: Unsize<U>
         let unsize_predicate =
@@ -458,7 +458,7 @@
         ParamEnv {
             clauses: Clauses::new_from_iter(
                 interner,
-                generic_predicates.iter().copied().chain([
+                generic_predicates.iter_identity_copied().chain([
                     unsize_predicate.upcast(interner),
                     trait_predicate.upcast(interner),
                     meta_sized_predicate.upcast(interner),
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index 26e03aa..5f02614 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -60,11 +60,6 @@
         self.params.where_predicates().iter()
     }
 
-    pub(crate) fn has_no_predicates(&self) -> bool {
-        self.params.has_no_predicates()
-            && self.parent_generics.as_ref().is_none_or(|g| g.params.has_no_predicates())
-    }
-
     pub(crate) fn is_empty(&self) -> bool {
         self.params.is_empty() && self.parent_generics.as_ref().is_none_or(|g| g.params.is_empty())
     }
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 016edb2..37060bd 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -21,9 +21,11 @@
 mod expr;
 mod fallback;
 mod mutability;
+mod op;
 mod opaques;
 mod pat;
 mod path;
+mod place_op;
 pub(crate) mod unify;
 
 use std::{cell::OnceCell, convert::identity, iter, ops::Index};
@@ -45,12 +47,14 @@
 use indexmap::IndexSet;
 use intern::sym;
 use la_arena::ArenaMap;
+use macros::{TypeFoldable, TypeVisitable};
 use rustc_ast_ir::Mutability;
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_type_ir::{
     AliasTyKind, TypeFoldable,
     inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
 };
+use span::Edition;
 use stdx::never;
 use triomphe::Arc;
 
@@ -65,10 +69,13 @@
     lower::{
         ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
     },
+    method_resolution::{CandidateId, MethodResolutionUnstableFeatures},
     mir::MirSpan,
     next_solver::{
         AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind,
-        Tys, abi::Safety, infer::traits::ObligationCause,
+        Tys,
+        abi::Safety,
+        infer::{InferCtxt, traits::ObligationCause},
     },
     traits::FnTrait,
     utils::TargetFeatureIsSafeInTarget,
@@ -330,16 +337,21 @@
 ///    At some point, of course, `Box` should move out of the compiler, in which
 ///    case this is analogous to transforming a struct. E.g., Box<[i32; 4]> ->
 ///    Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
 pub struct Adjustment<'db> {
-    pub kind: Adjust<'db>,
+    #[type_visitable(ignore)]
+    #[type_foldable(identity)]
+    pub kind: Adjust,
     pub target: Ty<'db>,
 }
 
 impl<'db> Adjustment<'db> {
     pub fn borrow(interner: DbInterner<'db>, m: Mutability, ty: Ty<'db>, lt: Region<'db>) -> Self {
         let ty = Ty::new_ref(interner, lt, ty, m);
-        Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty }
+        Adjustment {
+            kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::new(m, AllowTwoPhase::No))),
+            target: ty,
+        }
     }
 }
 
@@ -357,20 +369,20 @@
 /// capable mutable borrows.
 /// See #49434 for tracking.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub(crate) enum AllowTwoPhase {
+pub enum AllowTwoPhase {
     // FIXME: We should use this when appropriate.
     Yes,
     No,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Adjust<'db> {
+pub enum Adjust {
     /// Go from ! to any type.
     NeverToAny,
     /// Dereference once, producing a place.
     Deref(Option<OverloadedDeref>),
     /// Take the address and produce either a `&` or `*` pointer.
-    Borrow(AutoBorrow<'db>),
+    Borrow(AutoBorrow),
     Pointer(PointerCast),
 }
 
@@ -381,18 +393,47 @@
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub struct OverloadedDeref(pub Option<Mutability>);
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum AutoBorrow<'db> {
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum AutoBorrowMutability {
+    Mut { allow_two_phase_borrow: AllowTwoPhase },
+    Not,
+}
+
+impl AutoBorrowMutability {
+    /// Creates an `AutoBorrowMutability` from a mutability and allowance of two phase borrows.
+    ///
+    /// Note that when `mutbl.is_not()`, `allow_two_phase_borrow` is ignored
+    pub fn new(mutbl: Mutability, allow_two_phase_borrow: AllowTwoPhase) -> Self {
+        match mutbl {
+            Mutability::Not => Self::Not,
+            Mutability::Mut => Self::Mut { allow_two_phase_borrow },
+        }
+    }
+}
+
+impl From<AutoBorrowMutability> for Mutability {
+    fn from(m: AutoBorrowMutability) -> Self {
+        match m {
+            AutoBorrowMutability::Mut { .. } => Mutability::Mut,
+            AutoBorrowMutability::Not => Mutability::Not,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum AutoBorrow {
     /// Converts from T to &T.
-    Ref(Region<'db>, Mutability),
+    Ref(AutoBorrowMutability),
     /// Converts from T to *T.
     RawPtr(Mutability),
 }
 
-impl<'db> AutoBorrow<'db> {
-    fn mutability(&self) -> Mutability {
-        let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self;
-        *m
+impl AutoBorrow {
+    fn mutability(self) -> Mutability {
+        match self {
+            AutoBorrow::Ref(mutbl) => mutbl.into(),
+            AutoBorrow::RawPtr(mutbl) => mutbl,
+        }
     }
 }
 
@@ -442,7 +483,7 @@
     /// For each struct literal or pattern, records the variant it resolves to.
     variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
     /// For each associated item record what it resolves to
-    assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, GenericArgs<'db>)>,
+    assoc_resolutions: FxHashMap<ExprOrPatId, (CandidateId, GenericArgs<'db>)>,
     /// Whenever a tuple field expression access a tuple field, we allocate a tuple id in
     /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
     /// that which allows us to resolve a [`TupleFieldId`]s type.
@@ -457,7 +498,7 @@
     pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>,
     pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>,
     pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
-    type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
+    pub(crate) type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
     /// Whether there are any type-mismatching errors in the result.
     // FIXME: This isn't as useful as initially thought due to us falling back placeholders to
     // `TyKind::Error`.
@@ -535,16 +576,16 @@
     pub fn assoc_resolutions_for_expr(
         &self,
         id: ExprId,
-    ) -> Option<(AssocItemId, GenericArgs<'db>)> {
+    ) -> Option<(CandidateId, GenericArgs<'db>)> {
         self.assoc_resolutions.get(&id.into()).copied()
     }
-    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, GenericArgs<'db>)> {
+    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(CandidateId, GenericArgs<'db>)> {
         self.assoc_resolutions.get(&id.into()).copied()
     }
     pub fn assoc_resolutions_for_expr_or_pat(
         &self,
         id: ExprOrPatId,
-    ) -> Option<(AssocItemId, GenericArgs<'db>)> {
+    ) -> Option<(CandidateId, GenericArgs<'db>)> {
         match id {
             ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id),
             ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
@@ -769,8 +810,10 @@
     /// and resolve the path via its methods. This will ensure proper error reporting.
     pub(crate) resolver: Resolver<'db>,
     target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
+    pub(crate) unstable_features: MethodResolutionUnstableFeatures,
+    pub(crate) edition: Edition,
     pub(crate) generic_def: GenericDefId,
-    table: unify::InferenceTable<'db>,
+    pub(crate) table: unify::InferenceTable<'db>,
     /// The traits in scope, disregarding block modules. This is used for caching purposes.
     traits_in_scope: FxHashSet<TraitId>,
     pub(crate) result: InferenceResult<'db>,
@@ -873,6 +916,10 @@
             return_ty: types.error, // set in collect_* calls
             types,
             target_features: OnceCell::new(),
+            unstable_features: MethodResolutionUnstableFeatures::from_def_map(
+                resolver.top_level_def_map(),
+            ),
+            edition: resolver.krate().data(db).edition,
             table,
             tuple_field_accesses_rev: Default::default(),
             resume_yield_tys: None,
@@ -906,18 +953,15 @@
         self.resolver.krate()
     }
 
-    fn target_features<'a>(
-        db: &dyn HirDatabase,
-        target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
-        owner: DefWithBodyId,
-        krate: Crate,
-    ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) {
-        let (target_features, target_feature_is_safe) = target_features.get_or_init(|| {
-            let target_features = match owner {
-                DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())),
+    fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+        let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| {
+            let target_features = match self.owner {
+                DefWithBodyId::FunctionId(id) => {
+                    TargetFeatures::from_attrs(&self.db.attrs(id.into()))
+                }
                 _ => TargetFeatures::default(),
             };
-            let target_feature_is_safe = match &krate.workspace_data(db).target {
+            let target_feature_is_safe = match &self.krate().workspace_data(self.db).target {
                 Ok(target) => crate::utils::target_feature_is_safe_in_target(target),
                 Err(_) => TargetFeatureIsSafeInTarget::No,
             };
@@ -927,7 +971,7 @@
     }
 
     #[inline]
-    pub(crate) fn set_tainted_by_errors(&mut self) {
+    fn set_tainted_by_errors(&mut self) {
         self.result.has_errors = true;
     }
 
@@ -1162,6 +1206,11 @@
         self.table.interner()
     }
 
+    #[inline]
+    pub(crate) fn infcx(&self) -> &InferCtxt<'db> {
+        &self.table.infer_ctxt
+    }
+
     fn infer_body(&mut self) {
         match self.return_coercion {
             Some(_) => self.infer_return(self.body.body_expr),
@@ -1179,7 +1228,7 @@
         self.result.type_of_expr.insert(expr, ty);
     }
 
-    fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment<'db>]>) {
+    pub(crate) fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment<'db>]>) {
         if adjustments.is_empty() {
             return;
         }
@@ -1212,7 +1261,12 @@
         self.result.pat_adjustments.entry(pat).or_default().extend(adjustments);
     }
 
-    fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: GenericArgs<'db>) {
+    pub(crate) fn write_method_resolution(
+        &mut self,
+        expr: ExprId,
+        func: FunctionId,
+        subst: GenericArgs<'db>,
+    ) {
         self.result.method_resolutions.insert(expr, (func, subst));
     }
 
@@ -1223,7 +1277,7 @@
     fn write_assoc_resolution(
         &mut self,
         id: ExprOrPatId,
-        item: AssocItemId,
+        item: CandidateId,
         subs: GenericArgs<'db>,
     ) {
         self.result.assoc_resolutions.insert(id, (item, subs));
@@ -1237,7 +1291,7 @@
         self.result.type_of_binding.insert(id, ty);
     }
 
-    fn push_diagnostic(&self, diagnostic: InferenceDiagnostic<'db>) {
+    pub(crate) fn push_diagnostic(&self, diagnostic: InferenceDiagnostic<'db>) {
         self.diagnostics.push(diagnostic);
     }
 
@@ -1284,7 +1338,7 @@
         self.process_user_written_ty(ty)
     }
 
-    fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
+    pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
         self.make_ty(
             type_ref,
             self.body,
@@ -1293,7 +1347,7 @@
         )
     }
 
-    fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> {
+    pub(crate) fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> {
         let const_ = self.with_ty_lowering(
             self.body,
             InferenceTyDiagnosticSource::Body,
@@ -1303,7 +1357,7 @@
         self.insert_type_vars(const_)
     }
 
-    fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
+    pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
         let const_ = self.with_ty_lowering(
             self.body,
             InferenceTyDiagnosticSource::Body,
@@ -1317,7 +1371,7 @@
         self.types.error
     }
 
-    fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> {
+    pub(crate) fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> {
         let lt = self.with_ty_lowering(
             self.body,
             InferenceTyDiagnosticSource::Body,
@@ -1399,19 +1453,13 @@
     }
 
     /// Whenever you lower a user-written type, you should call this.
-    fn process_user_written_ty<T>(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable<DbInterner<'db>>,
-    {
+    fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
         self.table.process_user_written_ty(ty)
     }
 
     /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
     /// while `process_user_written_ty()` should (but doesn't currently).
-    fn process_remote_user_written_ty<T>(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable<DbInterner<'db>>,
-    {
+    fn process_remote_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
         self.table.process_remote_user_written_ty(ty)
     }
 
@@ -1430,8 +1478,7 @@
     fn demand_eqtype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
         let result = self
             .table
-            .infer_ctxt
-            .at(&ObligationCause::new(), self.table.trait_env.env)
+            .at(&ObligationCause::new())
             .eq(expected, actual)
             .map(|infer_ok| self.table.register_infer_ok(infer_ok));
         if let Err(_err) = result {
@@ -1439,6 +1486,46 @@
         }
     }
 
+    fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
+        let result = self
+            .table
+            .at(&ObligationCause::new())
+            .sup(expected, actual)
+            .map(|infer_ok| self.table.register_infer_ok(infer_ok));
+        if let Err(_err) = result {
+            // FIXME: Emit diagnostic.
+        }
+    }
+
+    fn demand_coerce(
+        &mut self,
+        expr: ExprId,
+        checked_ty: Ty<'db>,
+        expected: Ty<'db>,
+        allow_two_phase: AllowTwoPhase,
+        expr_is_read: ExprIsRead,
+    ) -> Ty<'db> {
+        let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read);
+        if let Err(_err) = result {
+            // FIXME: Emit diagnostic.
+        }
+        result.unwrap_or(self.types.error)
+    }
+
+    fn expr_ty(&self, expr: ExprId) -> Ty<'db> {
+        self.result[expr]
+    }
+
+    fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {
+        let mut ty = None;
+        if let Some(it) = self.result.expr_adjustments.get(&e)
+            && let Some(it) = it.last()
+        {
+            ty = Some(it.target);
+        }
+        ty.unwrap_or_else(|| self.expr_ty(e))
+    }
+
     fn resolve_associated_type_with_params(
         &mut self,
         inner_ty: Ty<'db>,
@@ -1596,9 +1683,7 @@
                     (ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
                     tried_resolving_once = true;
 
-                    ty = self.table.insert_type_vars(ty);
-                    ty = self.table.normalize_associated_types_in(ty);
-                    ty = self.table.structurally_resolve_type(ty);
+                    ty = self.table.process_user_written_ty(ty);
                     if ty.is_ty_error() {
                         return (self.err_ty(), None);
                     }
@@ -1709,18 +1794,6 @@
         trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output))
     }
 
-    fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> {
-        self.resolve_lang_item(lang)?.as_trait()
-    }
-
-    fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
-        self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
-    }
-
-    fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
-        self.resolve_output_on(self.resolve_lang_trait(LangItem::Not)?)
-    }
-
     fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
         let ItemContainerId::TraitId(trait_) = self
             .resolve_lang_item(LangItem::IntoFutureIntoFuture)?
@@ -1768,24 +1841,17 @@
         Some(struct_.into())
     }
 
-    fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
-        self.resolve_output_on(self.resolve_lang_trait(LangItem::Index)?)
-    }
-
     fn resolve_va_list(&self) -> Option<AdtId> {
         let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
         Some(struct_.into())
     }
 
-    fn get_traits_in_scope<'a>(
-        resolver: &Resolver<'db>,
-        traits_in_scope: &'a FxHashSet<TraitId>,
-    ) -> Either<FxHashSet<TraitId>, &'a FxHashSet<TraitId>> {
-        let mut b_traits = resolver.traits_in_scope_from_block_scopes().peekable();
+    pub(crate) 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(traits_in_scope.iter().copied().chain(b_traits).collect())
+            Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
         } else {
-            Either::Right(traits_in_scope)
+            Either::Right(&self.traits_in_scope)
         }
     }
 }
diff --git a/crates/hir-ty/src/infer/autoderef.rs b/crates/hir-ty/src/infer/autoderef.rs
index ba133aa..1af102a 100644
--- a/crates/hir-ty/src/infer/autoderef.rs
+++ b/crates/hir-ty/src/infer/autoderef.rs
@@ -6,7 +6,7 @@
 
 use crate::{
     Adjust, Adjustment, OverloadedDeref,
-    autoderef::{Autoderef, AutoderefKind},
+    autoderef::{Autoderef, AutoderefCtx, AutoderefKind, GeneralAutoderef},
     infer::unify::InferenceTable,
     next_solver::{
         Ty,
@@ -15,18 +15,16 @@
 };
 
 impl<'db> InferenceTable<'db> {
-    pub(crate) fn autoderef(&mut self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> {
-        Autoderef::new(self, base_ty)
+    pub(crate) fn autoderef(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db, usize> {
+        Autoderef::new(&self.infer_ctxt, &self.trait_env, base_ty)
+    }
+
+    pub(crate) fn autoderef_with_tracking(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> {
+        Autoderef::new_with_tracking(&self.infer_ctxt, &self.trait_env, base_ty)
     }
 }
 
-impl<'db> Autoderef<'_, 'db> {
-    /// Returns the adjustment steps.
-    pub(crate) fn adjust_steps(mut self) -> Vec<Adjustment<'db>> {
-        let infer_ok = self.adjust_steps_as_infer_ok();
-        self.table.register_infer_ok(infer_ok)
-    }
-
+impl<'db, Ctx: AutoderefCtx<'db>> GeneralAutoderef<'db, Ctx> {
     pub(crate) fn adjust_steps_as_infer_ok(&mut self) -> InferOk<'db, Vec<Adjustment<'db>>> {
         let steps = self.steps();
         if steps.is_empty() {
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs
index c128977..8b657dc 100644
--- a/crates/hir-ty/src/infer/cast.rs
+++ b/crates/hir-ty/src/infer/cast.rs
@@ -11,7 +11,7 @@
 use crate::{
     InferenceDiagnostic,
     db::HirDatabase,
-    infer::{AllowTwoPhase, InferenceContext, coerce::CoerceNever},
+    infer::{AllowTwoPhase, InferenceContext, expr::ExprIsRead},
     next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind},
 };
 
@@ -120,7 +120,7 @@
                 self.expr_ty,
                 self.cast_ty,
                 AllowTwoPhase::No,
-                CoerceNever::Yes,
+                ExprIsRead::Yes,
             )
             .is_ok()
         {
@@ -167,7 +167,7 @@
                                 self.expr_ty,
                                 fn_ptr,
                                 AllowTwoPhase::No,
-                                CoerceNever::Yes,
+                                ExprIsRead::Yes,
                             )
                             .is_ok()
                         {
@@ -248,7 +248,7 @@
                     self.expr_ty,
                     array_ptr_type,
                     AllowTwoPhase::No,
-                    CoerceNever::Yes,
+                    ExprIsRead::Yes,
                 )
                 .is_ok()
             {
@@ -263,7 +263,7 @@
             // This is a less strict condition than rustc's `demand_eqtype`,
             // but false negative is better than false positive
             if ctx
-                .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, CoerceNever::Yes)
+                .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, ExprIsRead::Yes)
                 .is_ok()
             {
                 return Ok(());
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 06f8307..54a06eb 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -31,7 +31,6 @@
             BoundRegionConversionTime, InferOk, InferResult,
             traits::{ObligationCause, PredicateObligations},
         },
-        util::explicit_item_bounds,
     },
     traits::FnTrait,
 };
@@ -255,8 +254,10 @@
                 .deduce_closure_signature_from_predicates(
                     expected_ty,
                     closure_kind,
-                    explicit_item_bounds(self.interner(), def_id)
-                        .iter_instantiated(self.interner(), args)
+                    def_id
+                        .expect_opaque_ty()
+                        .predicates(self.db)
+                        .iter_instantiated_copied(self.interner(), args.as_slice())
                         .map(|clause| clause.as_predicate()),
                 ),
             TyKind::Dynamic(object_type, ..) => {
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index 763b145..944b359 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -11,11 +11,8 @@
         Statement, UnaryOp,
     },
     item_tree::FieldsShape,
-    lang_item::LangItem,
     resolver::ValueNs,
 };
-use hir_expand::name::Name;
-use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _};
@@ -351,10 +348,12 @@
                 return Some(place);
             }
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-                if matches!(
-                    self.expr_ty_after_adjustments(*expr).kind(),
-                    TyKind::Ref(..) | TyKind::RawPtr(..)
-                ) {
+                let is_builtin_deref = match self.expr_ty(*expr).kind() {
+                    TyKind::Ref(..) | TyKind::RawPtr(..) => true,
+                    TyKind::Adt(adt_def, _) if adt_def.is_box() => true,
+                    _ => false,
+                };
+                if is_builtin_deref {
                     let mut place = self.place_of_expr(*expr)?;
                     self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
                     place.projections.push(ProjectionElem::Deref);
@@ -609,28 +608,19 @@
             }
             Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-                if matches!(
-                    self.expr_ty_after_adjustments(*expr).kind(),
-                    TyKind::Ref(..) | TyKind::RawPtr(..)
-                ) {
-                    self.select_from_expr(*expr);
-                } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
-                    let mutability = 'b: {
-                        if let Some(deref_trait) =
-                            self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait())
-                            && let Some(deref_fn) = deref_trait
-                                .trait_items(self.db)
-                                .method_by_name(&Name::new_symbol_root(sym::deref_mut))
-                        {
-                            break 'b deref_fn == f;
+                if self.result.method_resolution(tgt_expr).is_some() {
+                    // Overloaded deref.
+                    match self.expr_ty_after_adjustments(*expr).kind() {
+                        TyKind::Ref(_, _, mutability) => {
+                            let place = self.place_of_expr(*expr);
+                            match mutability {
+                                Mutability::Mut => self.mutate_expr(*expr, place),
+                                Mutability::Not => self.ref_expr(*expr, place),
+                            }
                         }
-                        false
-                    };
-                    let place = self.place_of_expr(*expr);
-                    if mutability {
-                        self.mutate_expr(*expr, place);
-                    } else {
-                        self.ref_expr(*expr, place);
+                        // FIXME: Is this correct wrt. raw pointer derefs?
+                        TyKind::RawPtr(..) => self.select_from_expr(*expr),
+                        _ => never!("deref adjustments should include taking a mutable reference"),
                     }
                 } else {
                     self.select_from_expr(*expr);
@@ -806,20 +796,6 @@
         self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
     }
 
-    fn expr_ty(&self, expr: ExprId) -> Ty<'db> {
-        self.result[expr]
-    }
-
-    fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {
-        let mut ty = None;
-        if let Some(it) = self.result.expr_adjustments.get(&e)
-            && let Some(it) = it.last()
-        {
-            ty = Some(it.target);
-        }
-        ty.unwrap_or_else(|| self.expr_ty(e))
-    }
-
     fn is_upvar(&self, place: &HirPlace<'db>) -> bool {
         if let Some(c) = self.current_closure {
             let InternedClosure(_, root) = self.db.lookup_intern_closure(c);
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 40de923..4acf964 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -44,7 +44,8 @@
 use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::{
-    BoundVar, TypeAndMut,
+    BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt,
     error::TypeError,
     inherent::{Const as _, GenericArg as _, IntoKind, Safety, SliceLike, Ty as _},
 };
@@ -56,13 +57,16 @@
     Adjust, Adjustment, AutoBorrow, PointerCast, TargetFeatures, TraitEnvironment,
     autoderef::Autoderef,
     db::{HirDatabase, InternedClosureId},
-    infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable},
+    infer::{
+        AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
+    },
     next_solver::{
         Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
         Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
         GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
+        TypingMode,
         infer::{
-            InferCtxt, InferOk, InferResult,
+            DbInternerInferExt, InferCtxt, InferOk, InferResult,
             relate::RelateResult,
             select::{ImplSource, SelectionError},
             traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
@@ -72,10 +76,20 @@
     utils::TargetFeatureIsSafeInTarget,
 };
 
-struct Coerce<'a, 'b, 'db> {
-    table: &'a mut InferenceTable<'db>,
-    has_errors: &'a mut bool,
-    target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget),
+trait CoerceDelegate<'db> {
+    fn infcx(&self) -> &InferCtxt<'db>;
+    fn env(&self) -> &TraitEnvironment<'db>;
+    fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget);
+
+    fn set_diverging(&mut self, diverging_ty: Ty<'db>);
+
+    fn set_tainted_by_errors(&mut self);
+
+    fn type_var_is_sized(&mut self, var: TyVid) -> bool;
+}
+
+struct Coerce<D> {
+    delegate: D,
     use_lub: bool,
     /// Determines whether or not allow_two_phase_borrow is set on any
     /// autoref adjustments we create while coercing. We don't want to
@@ -109,43 +123,56 @@
     Ok(InferOk { value: (adj, target), obligations })
 }
 
-impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
+impl<'db, D> Coerce<D>
+where
+    D: CoerceDelegate<'db>,
+{
     #[inline]
     fn set_tainted_by_errors(&mut self) {
-        *self.has_errors = true;
+        self.delegate.set_tainted_by_errors();
+    }
+
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        self.delegate.infcx()
+    }
+
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        self.delegate.env()
     }
 
     #[inline]
     fn interner(&self) -> DbInterner<'db> {
-        self.table.interner()
+        self.infcx().interner
     }
 
     #[inline]
-    fn infer_ctxt(&self) -> &InferCtxt<'db> {
-        &self.table.infer_ctxt
+    fn db(&self) -> &'db dyn HirDatabase {
+        self.interner().db
     }
 
     pub(crate) fn commit_if_ok<T, E>(
         &mut self,
         f: impl FnOnce(&mut Self) -> Result<T, E>,
     ) -> Result<T, E> {
-        let snapshot = self.table.snapshot();
+        let snapshot = self.infcx().start_snapshot();
         let result = f(self);
         match result {
             Ok(_) => {}
             Err(_) => {
-                self.table.rollback_to(snapshot);
+                self.infcx().rollback_to(snapshot);
             }
         }
         result
     }
 
-    fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> {
+    fn unify_raw(&self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> {
         debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
-        self.commit_if_ok(|this| {
-            let at = this.infer_ctxt().at(&this.cause, this.table.trait_env.env);
+        self.infcx().commit_if_ok(|_| {
+            let at = self.infcx().at(&self.cause, self.env().env);
 
-            let res = if this.use_lub {
+            let res = if self.use_lub {
                 at.lub(b, a)
             } else {
                 at.sup(b, a)
@@ -157,7 +184,7 @@
             // Filter these cases out to make sure our coercion is more accurate.
             match res {
                 Ok(InferOk { value, obligations }) => {
-                    let mut ocx = ObligationCtxt::new(this.infer_ctxt());
+                    let mut ocx = ObligationCtxt::new(self.infcx());
                     ocx.register_obligations(obligations);
                     if ocx.try_evaluate_obligations().is_empty() {
                         Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
@@ -182,7 +209,7 @@
         a: Ty<'db>,
         b: Ty<'db>,
         adjustments: impl IntoIterator<Item = Adjustment<'db>>,
-        final_adjustment: Adjust<'db>,
+        final_adjustment: Adjust,
     ) -> CoerceResult<'db> {
         self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| {
             success(
@@ -199,15 +226,15 @@
     #[instrument(skip(self))]
     fn coerce(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
         // First, remove any resolved type variables (at the top level, at least):
-        let a = self.table.shallow_resolve(a);
-        let b = self.table.shallow_resolve(b);
+        let a = self.infcx().shallow_resolve(a);
+        let b = self.infcx().shallow_resolve(b);
         debug!("Coerce.tys({:?} => {:?})", a, b);
 
         // Coercing from `!` to any type is allowed:
         if a.is_never() {
             // If we're coercing into an inference var, mark it as possibly diverging.
             if b.is_infer() {
-                self.table.set_diverging(b);
+                self.delegate.set_diverging(b);
             }
 
             if self.coerce_never {
@@ -290,12 +317,12 @@
     /// fall back to subtyping (`unify_and`).
     fn coerce_from_inference_variable(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
         debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b);
-        debug_assert!(a.is_infer() && self.table.shallow_resolve(a) == a);
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(a.is_infer() && self.infcx().shallow_resolve(a) == a);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         if b.is_infer() {
             // Two unresolved type variables: create a `Coerce` predicate.
-            let target_ty = if self.use_lub { self.table.next_ty_var() } else { b };
+            let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b };
 
             let mut obligations = PredicateObligations::with_capacity(2);
             for &source_ty in &[a, b] {
@@ -303,7 +330,7 @@
                     obligations.push(Obligation::new(
                         self.interner(),
                         self.cause.clone(),
-                        self.table.trait_env.env,
+                        self.env().env,
                         Binder::dummy(PredicateKind::Coerce(CoercePredicate {
                             a: source_ty,
                             b: target_ty,
@@ -335,8 +362,8 @@
         mutbl_b: Mutability,
     ) -> CoerceResult<'db> {
         debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
-        debug_assert!(self.table.shallow_resolve(a) == a);
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(a) == a);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         // If we have a parameter of type `&M T_a` and the value
         // provided is `expr`, we will be adding an implicit borrow,
@@ -355,10 +382,10 @@
 
         let mut first_error = None;
         let mut r_borrow_var = None;
-        let mut autoderef = Autoderef::new(self.table, a);
+        let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.env(), a);
         let mut found = None;
 
-        while let Some((referent_ty, autoderefs)) = autoderef.next() {
+        for (referent_ty, autoderefs) in autoderef.by_ref() {
             if autoderefs == 0 {
                 // Don't let this pass, otherwise it would cause
                 // &T to autoref to &&T.
@@ -442,28 +469,18 @@
             } else {
                 if r_borrow_var.is_none() {
                     // create var lazily, at most once
-                    let r = autoderef.table.next_region_var();
+                    let r = self.infcx().next_region_var();
                     r_borrow_var = Some(r); // [4] above
                 }
                 r_borrow_var.unwrap()
             };
             let derefd_ty_a = Ty::new_ref(
-                autoderef.table.interner(),
+                self.interner(),
                 r,
                 referent_ty,
                 mutbl_b, // [1] above
             );
-            // We need to construct a new `Coerce` because of lifetimes.
-            let mut coerce = Coerce {
-                table: autoderef.table,
-                has_errors: self.has_errors,
-                target_features: self.target_features,
-                use_lub: self.use_lub,
-                allow_two_phase: self.allow_two_phase,
-                coerce_never: self.coerce_never,
-                cause: self.cause.clone(),
-            };
-            match coerce.unify_raw(derefd_ty_a, b) {
+            match self.unify_raw(derefd_ty_a, b) {
                 Ok(ok) => {
                     found = Some(ok);
                     break;
@@ -515,15 +532,9 @@
             autoderef.adjust_steps_as_infer_ok();
         obligations.extend(o);
 
-        // Now apply the autoref. We have to extract the region out of
-        // the final ref type we got.
-        let TyKind::Ref(region, _, _) = ty.kind() else {
-            panic!("expected a ref type, got {:?}", ty);
-        };
-        adjustments.push(Adjustment {
-            kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl_b)),
-            target: ty,
-        });
+        // Now apply the autoref.
+        let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
+        adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
 
         debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
 
@@ -538,8 +549,8 @@
     #[instrument(skip(self), level = "debug")]
     fn coerce_unsized(&mut self, source: Ty<'db>, target: Ty<'db>) -> CoerceResult<'db> {
         debug!(?source, ?target);
-        debug_assert!(self.table.shallow_resolve(source) == source);
-        debug_assert!(self.table.shallow_resolve(target) == target);
+        debug_assert!(self.infcx().shallow_resolve(source) == source);
+        debug_assert!(self.infcx().shallow_resolve(target) == target);
 
         // We don't apply any coercions incase either the source or target
         // aren't sufficiently well known but tend to instead just equate
@@ -602,8 +613,8 @@
         }
 
         let traits = (
-            LangItem::Unsize.resolve_trait(self.table.db, self.table.trait_env.krate),
-            LangItem::CoerceUnsized.resolve_trait(self.table.db, self.table.trait_env.krate),
+            LangItem::Unsize.resolve_trait(self.db(), self.env().krate),
+            LangItem::CoerceUnsized.resolve_trait(self.db(), self.env().krate),
         );
         let (Some(unsize_did), Some(coerce_unsized_did)) = traits else {
             debug!("missing Unsize or CoerceUnsized traits");
@@ -620,18 +631,17 @@
             (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => {
                 coerce_mutbls(mutbl_a, mutbl_b)?;
 
-                let r_borrow = self.table.next_region_var();
+                let r_borrow = self.infcx().next_region_var();
 
                 // We don't allow two-phase borrows here, at least for initial
                 // implementation. If it happens that this coercion is a function argument,
                 // the reborrow in coerce_borrowed_ptr will pick it up.
-                // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
-                let mutbl = mutbl_b;
+                let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
 
                 Some((
                     Adjustment { kind: Adjust::Deref(None), target: ty_a },
                     Adjustment {
-                        kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
+                        kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
                         target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b),
                     },
                 ))
@@ -655,7 +665,7 @@
         // the `CoerceUnsized` target type and the expected type.
         // We only have the latter, so we use an inference variable
         // for the former and let type inference do the rest.
-        let coerce_target = self.table.next_ty_var();
+        let coerce_target = self.infcx().next_ty_var();
 
         let mut coercion = self.unify_and(
             coerce_target,
@@ -677,7 +687,7 @@
         let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new(
             self.interner(),
             cause,
-            self.table.trait_env.env,
+            self.env().env,
             TraitRef::new(
                 self.interner(),
                 coerce_unsized_did.into(),
@@ -694,14 +704,14 @@
                 Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred)))
                     if traits.contains(&trait_pred.def_id().0) =>
                 {
-                    self.infer_ctxt().resolve_vars_if_possible(trait_pred)
+                    self.infcx().resolve_vars_if_possible(trait_pred)
                 }
                 // Eagerly process alias-relate obligations in new trait solver,
                 // since these can be emitted in the process of solving trait goals,
                 // but we need to constrain vars before processing goals mentioning
                 // them.
                 Some(PredicateKind::AliasRelate(..)) => {
-                    let mut ocx = ObligationCtxt::new(self.infer_ctxt());
+                    let mut ocx = ObligationCtxt::new(self.infcx());
                     ocx.register_obligation(obligation);
                     if !ocx.try_evaluate_obligations().is_empty() {
                         return Err(TypeError::Mismatch);
@@ -715,7 +725,7 @@
                 }
             };
             debug!("coerce_unsized resolve step: {:?}", trait_pred);
-            match self.infer_ctxt().select(&obligation.with(self.interner(), trait_pred)) {
+            match self.infcx().select(&obligation.with(self.interner(), trait_pred)) {
                 // Uncertain or unimplemented.
                 Ok(None) => {
                     if trait_pred.def_id().0 == unsize_did {
@@ -724,7 +734,7 @@
                         debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
                         match (self_ty.kind(), unsize_ty.kind()) {
                             (TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..))
-                                if self.table.type_var_is_sized(v) =>
+                                if self.delegate.type_var_is_sized(v) =>
                             {
                                 debug!("coerce_unsized: have sized infer {:?}", v);
                                 coercion.obligations.push(obligation);
@@ -794,9 +804,9 @@
         &mut self,
         fn_ty_a: PolyFnSig<'db>,
         b: Ty<'db>,
-        adjustment: Option<Adjust<'db>>,
+        adjustment: Option<Adjust>,
     ) -> CoerceResult<'db> {
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         self.commit_if_ok(|this| {
             if let TyKind::FnPtr(_, hdr_b) = b.kind()
@@ -825,15 +835,15 @@
 
     fn coerce_from_fn_pointer(&mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>) -> CoerceResult<'db> {
         debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer");
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         self.coerce_from_safe_fn(fn_ty_a, b, None)
     }
 
     fn coerce_from_fn_item(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> {
         debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b);
-        debug_assert!(self.table.shallow_resolve(a) == a);
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(a) == a);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         match b.kind() {
             TyKind::FnPtr(_, b_hdr) => {
@@ -841,11 +851,11 @@
                 if let TyKind::FnDef(def_id, _) = a.kind() {
                     // Intrinsics are not coercible to function pointers
                     if let CallableDefId::FunctionId(def_id) = def_id.0 {
-                        if FunctionSignature::is_intrinsic(self.table.db, def_id) {
+                        if FunctionSignature::is_intrinsic(self.db(), def_id) {
                             return Err(TypeError::IntrinsicCast);
                         }
 
-                        let attrs = self.table.db.attrs(def_id.into());
+                        let attrs = self.db().attrs(def_id.into());
                         if attrs.by_key(sym::rustc_force_inline).exists() {
                             return Err(TypeError::ForceInlineCast);
                         }
@@ -856,7 +866,7 @@
                             // Allow the coercion if the current function has all the features that would be
                             // needed to call the coercee safely.
                             let (target_features, target_feature_is_safe) =
-                                (self.target_features)();
+                                self.delegate.target_features();
                             if target_feature_is_safe == TargetFeatureIsSafeInTarget::No
                                 && !target_features.enabled.is_superset(&fn_target_features.enabled)
                             {
@@ -887,8 +897,8 @@
         args_a: GenericArgs<'db>,
         b: Ty<'db>,
     ) -> CoerceResult<'db> {
-        debug_assert!(self.table.shallow_resolve(a) == a);
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(a) == a);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         match b.kind() {
             // FIXME: We need to have an `upvars_mentioned()` query:
@@ -930,8 +940,8 @@
 
     fn coerce_raw_ptr(&mut self, a: Ty<'db>, b: Ty<'db>, mutbl_b: Mutability) -> CoerceResult<'db> {
         debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
-        debug_assert!(self.table.shallow_resolve(a) == a);
-        debug_assert!(self.table.shallow_resolve(b) == b);
+        debug_assert!(self.infcx().shallow_resolve(a) == a);
+        debug_assert!(self.infcx().shallow_resolve(b) == b);
 
         let (is_ref, mt_a) = match a.kind() {
             TyKind::Ref(_, ty, mutbl) => (true, TypeAndMut::<DbInterner<'db>> { ty, mutbl }),
@@ -960,10 +970,36 @@
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) enum CoerceNever {
-    No,
-    Yes,
+struct InferenceCoercionDelegate<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>);
+
+impl<'db> CoerceDelegate<'db> for InferenceCoercionDelegate<'_, '_, 'db> {
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        &self.0.table.infer_ctxt
+    }
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        &self.0.table.trait_env
+    }
+    #[inline]
+    fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+        self.0.target_features()
+    }
+
+    #[inline]
+    fn set_diverging(&mut self, diverging_ty: Ty<'db>) {
+        self.0.table.set_diverging(diverging_ty);
+    }
+
+    #[inline]
+    fn set_tainted_by_errors(&mut self) {
+        self.0.set_tainted_by_errors();
+    }
+
+    #[inline]
+    fn type_var_is_sized(&mut self, var: TyVid) -> bool {
+        self.0.table.type_var_is_sized(var)
+    }
 }
 
 impl<'db> InferenceContext<'_, 'db> {
@@ -977,24 +1013,26 @@
         expr_ty: Ty<'db>,
         mut target: Ty<'db>,
         allow_two_phase: AllowTwoPhase,
-        coerce_never: CoerceNever,
+        expr_is_read: ExprIsRead,
     ) -> RelateResult<'db, Ty<'db>> {
         let source = self.table.try_structurally_resolve_type(expr_ty);
         target = self.table.try_structurally_resolve_type(target);
         debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
 
         let cause = ObligationCause::new();
-        let krate = self.krate();
+        let coerce_never = match expr {
+            ExprOrPatId::ExprId(idx) => {
+                self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read)
+            }
+            // `PatId` is passed for `PatKind::Path`.
+            ExprOrPatId::PatId(_) => false,
+        };
         let mut coerce = Coerce {
-            table: &mut self.table,
-            has_errors: &mut self.result.has_errors,
+            delegate: InferenceCoercionDelegate(self),
             cause,
             allow_two_phase,
-            coerce_never: matches!(coerce_never, CoerceNever::Yes),
+            coerce_never,
             use_lub: false,
-            target_features: &mut || {
-                Self::target_features(self.db, &self.target_features, self.owner, krate)
-            },
         };
         let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?;
 
@@ -1157,23 +1195,18 @@
         //
         // NOTE: we set `coerce_never` to `true` here because coercion LUBs only
         // operate on values and not places, so a never coercion is valid.
-        let krate = self.krate();
         let mut coerce = Coerce {
-            table: &mut self.table,
-            has_errors: &mut self.result.has_errors,
+            delegate: InferenceCoercionDelegate(self),
             cause: ObligationCause::new(),
             allow_two_phase: AllowTwoPhase::No,
             coerce_never: true,
             use_lub: true,
-            target_features: &mut || {
-                Self::target_features(self.db, &self.target_features, self.owner, krate)
-            },
         };
 
         // First try to coerce the new expression to the type of the previous ones,
         // but only if the new expression has no coercion already applied to it.
         let mut first_error = None;
-        if !self.result.expr_adjustments.contains_key(&new) {
+        if !coerce.delegate.0.result.expr_adjustments.contains_key(&new) {
             let result = coerce.commit_if_ok(|coerce| coerce.coerce(new_ty, prev_ty));
             match result {
                 Ok(ok) => {
@@ -1335,8 +1368,9 @@
         cause: &ObligationCause,
         expression: ExprId,
         expression_ty: Ty<'db>,
+        expr_is_read: ExprIsRead,
     ) {
-        self.coerce_inner(icx, cause, expression, expression_ty, false, false)
+        self.coerce_inner(icx, cause, expression, expression_ty, false, false, expr_is_read)
     }
 
     /// Indicates that one of the inputs is a "forced unit". This
@@ -1357,8 +1391,17 @@
         expr: ExprId,
         cause: &ObligationCause,
         label_unit_as_expected: bool,
+        expr_is_read: ExprIsRead,
     ) {
-        self.coerce_inner(icx, cause, expr, icx.types.unit, true, label_unit_as_expected)
+        self.coerce_inner(
+            icx,
+            cause,
+            expr,
+            icx.types.unit,
+            true,
+            label_unit_as_expected,
+            expr_is_read,
+        )
     }
 
     /// The inner coercion "engine". If `expression` is `None`, this
@@ -1372,6 +1415,7 @@
         mut expression_ty: Ty<'db>,
         force_unit: bool,
         label_expression_as_expected: bool,
+        expr_is_read: ExprIsRead,
     ) {
         // Incorporate whatever type inference information we have
         // until now; in principle we might also want to process
@@ -1408,7 +1452,7 @@
                     expression_ty,
                     self.expected_ty,
                     AllowTwoPhase::No,
-                    CoerceNever::Yes,
+                    expr_is_read,
                 )
             } else {
                 match self.expressions {
@@ -1504,88 +1548,170 @@
     coerce(db, env, tys).is_ok()
 }
 
+struct HirCoercionDelegate<'a, 'db> {
+    infcx: &'a InferCtxt<'db>,
+    env: &'a TraitEnvironment<'db>,
+    target_features: &'a TargetFeatures,
+}
+
+impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> {
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        self.infcx
+    }
+    #[inline]
+    fn env(&self) -> &TraitEnvironment<'db> {
+        self.env
+    }
+    fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+        (self.target_features, TargetFeatureIsSafeInTarget::No)
+    }
+    fn set_diverging(&mut self, _diverging_ty: Ty<'db>) {}
+    fn set_tainted_by_errors(&mut self) {}
+    fn type_var_is_sized(&mut self, _var: TyVid) -> bool {
+        false
+    }
+}
+
 fn coerce<'db>(
     db: &'db dyn HirDatabase,
     env: Arc<TraitEnvironment<'db>>,
     tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
 ) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> {
-    let mut table = InferenceTable::new(db, env, None);
-    let interner = table.interner();
-    let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(tys);
+    let interner = DbInterner::new_with(db, Some(env.krate), env.block);
+    let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+    let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys);
 
     let cause = ObligationCause::new();
     // FIXME: Target features.
     let target_features = TargetFeatures::default();
     let mut coerce = Coerce {
-        table: &mut table,
-        has_errors: &mut false,
+        delegate: HirCoercionDelegate {
+            infcx: &infcx,
+            env: &env,
+            target_features: &target_features,
+        },
         cause,
         allow_two_phase: AllowTwoPhase::No,
         coerce_never: true,
         use_lub: false,
-        target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No),
     };
-    let InferOk { value: (adjustments, ty), obligations } =
-        coerce.coerce(ty1_with_vars, ty2_with_vars)?;
-    table.register_predicates(obligations);
+    let infer_ok = coerce.coerce(ty1_with_vars, ty2_with_vars)?;
+    let mut ocx = ObligationCtxt::new(&infcx);
+    let (adjustments, ty) = ocx.register_infer_ok_obligations(infer_ok);
+    _ = ocx.try_evaluate_obligations();
+    let (adjustments, ty) = infcx.resolve_vars_if_possible((adjustments, ty));
 
     // default any type vars that weren't unified back to their original bound vars
     // (kind of hacky)
-    let mut fallback_ty = |debruijn, infer| {
-        let var = vars.var_values.iter().position(|arg| {
-            arg.as_type().is_some_and(|ty| match ty.kind() {
-                TyKind::Infer(it) => infer == it,
-                _ => false,
-            })
-        });
-        var.map_or_else(
-            || Ty::new_error(interner, ErrorGuaranteed),
-            |i| {
-                Ty::new_bound(
-                    interner,
-                    debruijn,
-                    BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) },
+
+    struct Resolver<'db> {
+        interner: DbInterner<'db>,
+        debruijn: DebruijnIndex,
+        var_values: GenericArgs<'db>,
+    }
+
+    impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'db> {
+        fn cx(&self) -> DbInterner<'db> {
+            self.interner
+        }
+
+        fn fold_binder<T>(&mut self, t: Binder<'db, T>) -> Binder<'db, T>
+        where
+            T: TypeFoldable<DbInterner<'db>>,
+        {
+            self.debruijn.shift_in(1);
+            let result = t.super_fold_with(self);
+            self.debruijn.shift_out(1);
+            result
+        }
+
+        fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+            if !t.has_infer() {
+                return t;
+            }
+
+            if let TyKind::Infer(infer) = t.kind() {
+                let var = self.var_values.iter().position(|arg| {
+                    arg.as_type().is_some_and(|ty| match ty.kind() {
+                        TyKind::Infer(it) => infer == it,
+                        _ => false,
+                    })
+                });
+                var.map_or_else(
+                    || Ty::new_error(self.interner, ErrorGuaranteed),
+                    |i| {
+                        Ty::new_bound(
+                            self.interner,
+                            self.debruijn,
+                            BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) },
+                        )
+                    },
                 )
-            },
-        )
-    };
-    let mut fallback_const = |debruijn, infer| {
-        let var = vars.var_values.iter().position(|arg| {
-            arg.as_const().is_some_and(|ty| match ty.kind() {
-                ConstKind::Infer(it) => infer == it,
-                _ => false,
-            })
-        });
-        var.map_or_else(
-            || Const::new_error(interner, ErrorGuaranteed),
-            |i| Const::new_bound(interner, debruijn, BoundConst { var: BoundVar::from_usize(i) }),
-        )
-    };
-    let mut fallback_region = |debruijn, infer| {
-        let var = vars.var_values.iter().position(|arg| {
-            arg.as_region().is_some_and(|ty| match ty.kind() {
-                RegionKind::ReVar(it) => infer == it,
-                _ => false,
-            })
-        });
-        var.map_or_else(
-            || Region::error(interner),
-            |i| {
-                Region::new_bound(
-                    interner,
-                    debruijn,
-                    BoundRegion { kind: BoundRegionKind::Anon, var: BoundVar::from_usize(i) },
+            } else {
+                t.super_fold_with(self)
+            }
+        }
+
+        fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+            if !c.has_infer() {
+                return c;
+            }
+
+            if let ConstKind::Infer(infer) = c.kind() {
+                let var = self.var_values.iter().position(|arg| {
+                    arg.as_const().is_some_and(|ty| match ty.kind() {
+                        ConstKind::Infer(it) => infer == it,
+                        _ => false,
+                    })
+                });
+                var.map_or_else(
+                    || Const::new_error(self.interner, ErrorGuaranteed),
+                    |i| {
+                        Const::new_bound(
+                            self.interner,
+                            self.debruijn,
+                            BoundConst { var: BoundVar::from_usize(i) },
+                        )
+                    },
                 )
-            },
-        )
-    };
-    // FIXME also map the types in the adjustments
-    // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferenceTable`.
-    let ty = table.resolve_with_fallback(
-        ty,
-        &mut fallback_ty,
-        &mut fallback_const,
-        &mut fallback_region,
-    );
+            } else {
+                c.super_fold_with(self)
+            }
+        }
+
+        fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+            if let RegionKind::ReVar(infer) = r.kind() {
+                let var = self.var_values.iter().position(|arg| {
+                    arg.as_region().is_some_and(|ty| match ty.kind() {
+                        RegionKind::ReVar(it) => infer == it,
+                        _ => false,
+                    })
+                });
+                var.map_or_else(
+                    || Region::error(self.interner),
+                    |i| {
+                        Region::new_bound(
+                            self.interner,
+                            self.debruijn,
+                            BoundRegion {
+                                kind: BoundRegionKind::Anon,
+                                var: BoundVar::from_usize(i),
+                            },
+                        )
+                    },
+                )
+            } else {
+                r
+            }
+        }
+    }
+
+    // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferCtxt`.
+    let (adjustments, ty) = (adjustments, ty).fold_with(&mut Resolver {
+        interner,
+        debruijn: DebruijnIndex::ZERO,
+        var_values: vars.var_values,
+    });
     Ok((adjustments, ty))
 }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index b7ab109..db33036 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -3,19 +3,18 @@
 use std::{iter::repeat_with, mem};
 
 use either::Either;
-use hir_def::hir::ClosureKind;
 use hir_def::{
-    BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId,
-    expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs, Path},
+    BlockId, FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId,
+    expr_store::path::{GenericArgs as HirGenericArgs, Path},
     hir::{
-        ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId,
-        Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef,
+        Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
+        LabelId, Literal, Pat, PatId, Statement, UnaryOp,
     },
     lang_item::{LangItem, LangItemTarget},
     resolver::ValueNs,
 };
+use hir_def::{FunctionId, hir::ClosureKind};
 use hir_expand::name::Name;
-use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::{
     CoroutineArgs, CoroutineArgsParts, InferTy, Interner,
@@ -25,32 +24,25 @@
 use tracing::debug;
 
 use crate::{
-    Adjust, Adjustment, AutoBorrow, CallableDefId, DeclContext, DeclOrigin,
-    IncorrectGenericsLenKind, Rawness, TraitEnvironment,
-    autoderef::overloaded_deref_ty,
+    Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, TraitEnvironment,
+    autoderef::InferenceContextAutoderef,
     consteval,
     db::InternedCoroutine,
     generics::generics,
     infer::{
-        AllowTwoPhase, BreakableKind,
-        coerce::{CoerceMany, CoerceNever},
-        find_continuable,
+        AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable,
         pat::contains_explicit_ref_binding,
     },
-    lang_items::lang_items_for_bin_op,
-    lower::{
-        LifetimeElisionKind, lower_mutability,
-        path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
-    },
-    method_resolution::{self, VisibleFromModule},
+    lower::{GenericPredicates, lower_mutability},
+    method_resolution::{self, CandidateId, MethodCallee, MethodError},
     next_solver::{
-        Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind,
-        TypeError,
+        ErrorGuaranteed, FnSig, GenericArgs, TraitRef, Ty, TyKind, TypeError,
         infer::{
-            InferOk,
+            BoundRegionConversionTime, InferOk,
             traits::{Obligation, ObligationCause},
         },
         obligation_ctxt::ObligationCtxt,
+        util::clauses_as_obligations,
     },
     traits::FnTrait,
 };
@@ -103,12 +95,7 @@
     ) -> Ty<'db> {
         let ty = self.infer_expr_inner(expr, expected, is_read);
         if let Some(target) = expected.only_has_type(&mut self.table) {
-            let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) {
-                CoerceNever::Yes
-            } else {
-                CoerceNever::No
-            };
-            match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, coerce_never) {
+            match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, is_read) {
                 Ok(res) => res,
                 Err(_) => {
                     self.result
@@ -203,6 +190,23 @@
         }
     }
 
+    /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
+    /// yes whether it contains mutable or just immutables ones.
+    //
+    // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
+    // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
+    fn contains_explicit_ref_binding(&self, pat: PatId) -> bool {
+        if let Pat::Bind { id, .. } = self.body[pat]
+            && matches!(self.body[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
+        {
+            return true;
+        }
+
+        let mut result = false;
+        self.body.walk_pats_shallow(pat, |pat| result |= self.contains_explicit_ref_binding(pat));
+        result
+    }
+
     fn is_syntactic_place_expr(&self, expr: ExprId) -> bool {
         match &self.body[expr] {
             // Lang item paths cannot currently be local variables or statics.
@@ -250,6 +254,15 @@
         }
     }
 
+    #[expect(clippy::needless_return)]
+    pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) {
+        if self.is_syntactic_place_expr(lhs) {
+            return;
+        }
+
+        // FIXME: Emit diagnostic.
+    }
+
     fn infer_expr_coerce_never(
         &mut self,
         expr: ExprId,
@@ -269,7 +282,7 @@
             }
 
             if let Some(target) = expected.only_has_type(&mut self.table) {
-                self.coerce(expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes)
+                self.coerce(expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes)
                     .expect("never-to-any coercion should always succeed")
             } else {
                 ty
@@ -320,16 +333,28 @@
                     expected.coercion_target_type(&mut self.table),
                     &coercion_sites,
                 );
-                coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty);
+                coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes);
                 match else_branch {
                     Some(else_branch) => {
                         let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
                         let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
-                        coerce.coerce(self, &ObligationCause::new(), else_branch, else_ty);
+                        coerce.coerce(
+                            self,
+                            &ObligationCause::new(),
+                            else_branch,
+                            else_ty,
+                            ExprIsRead::Yes,
+                        );
                         self.diverges = condition_diverges | then_diverges & else_diverges;
                     }
                     None => {
-                        coerce.coerce_forced_unit(self, tgt_expr, &ObligationCause::new(), true);
+                        coerce.coerce_forced_unit(
+                            self,
+                            tgt_expr,
+                            &ObligationCause::new(),
+                            true,
+                            ExprIsRead::Yes,
+                        );
                         self.diverges = condition_diverges;
                     }
                 }
@@ -407,12 +432,20 @@
                     expected,
                 ),
             Expr::Match { expr, arms } => {
-                let scrutinee_is_read = arms
-                    .iter()
-                    .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat));
+                let mut scrutinee_is_read = true;
+                let mut contains_ref_bindings = false;
+                for arm in arms {
+                    scrutinee_is_read &= self.pat_guaranteed_to_constitute_read_for_never(arm.pat);
+                    contains_ref_bindings |= self.contains_explicit_ref_binding(arm.pat);
+                }
                 let scrutinee_is_read =
                     if scrutinee_is_read { ExprIsRead::Yes } else { ExprIsRead::No };
-                let input_ty = self.infer_expr(*expr, &Expectation::none(), scrutinee_is_read);
+                let input_ty = self.demand_scrutinee_type(
+                    *expr,
+                    contains_ref_bindings,
+                    arms.is_empty(),
+                    scrutinee_is_read,
+                );
 
                 if arms.is_empty() {
                     self.diverges = Diverges::Always;
@@ -421,7 +454,6 @@
                     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.table.structurally_resolve_type(input_ty);
                         self.infer_top_pat(arm.pat, input_ty, None);
                     }
 
@@ -447,7 +479,13 @@
 
                         let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes);
                         all_arms_diverge &= self.diverges;
-                        coerce.coerce(self, &ObligationCause::new(), arm.expr, arm_ty);
+                        coerce.coerce(
+                            self,
+                            &ObligationCause::new(),
+                            arm.expr,
+                            arm_ty,
+                            ExprIsRead::Yes,
+                        );
                     }
 
                     self.diverges = matchee_diverges | all_arms_diverge;
@@ -499,6 +537,7 @@
                                 &ObligationCause::new(),
                                 expr.unwrap_or(tgt_expr),
                                 val_ty,
+                                ExprIsRead::Yes,
                             );
 
                             // Avoiding borrowck
@@ -536,7 +575,7 @@
                             unit,
                             yield_ty,
                             AllowTwoPhase::No,
-                            CoerceNever::Yes,
+                            ExprIsRead::Yes,
                         );
                     }
                     resume_ty
@@ -662,70 +701,13 @@
                 }
             }
             &Expr::Box { expr } => self.infer_expr_box(expr, expected),
-            Expr::UnaryOp { expr, op } => {
-                let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
-                let inner_ty = self.table.try_structurally_resolve_type(inner_ty);
-                // FIXME: Note down method resolution her
-                match op {
-                    UnaryOp::Deref => {
-                        if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref)
-                            && let Some(deref_fn) = deref_trait
-                                .trait_items(self.db)
-                                .method_by_name(&Name::new_symbol_root(sym::deref))
-                        {
-                            // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
-                            // the mutability is not wrong, and will be fixed in `self.infer_mut`).
-                            self.write_method_resolution(tgt_expr, deref_fn, self.types.empty_args);
-                        }
-                        if let Some(derefed) = inner_ty.builtin_deref(self.db, true) {
-                            self.table.try_structurally_resolve_type(derefed)
-                        } else {
-                            let infer_ok = overloaded_deref_ty(&self.table, inner_ty);
-                            match infer_ok {
-                                Some(infer_ok) => self.table.register_infer_ok(infer_ok),
-                                None => self.err_ty(),
-                            }
-                        }
-                    }
-                    UnaryOp::Neg => {
-                        match inner_ty.kind() {
-                            // Fast path for builtins
-                            TyKind::Int(_)
-                            | TyKind::Uint(_)
-                            | TyKind::Float(_)
-                            | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty,
-                            // Otherwise we resolve via the std::ops::Neg trait
-                            _ => self
-                                .resolve_associated_type(inner_ty, self.resolve_ops_neg_output()),
-                        }
-                    }
-                    UnaryOp::Not => {
-                        match inner_ty.kind() {
-                            // Fast path for builtins
-                            TyKind::Bool
-                            | TyKind::Int(_)
-                            | TyKind::Uint(_)
-                            | TyKind::Float(_)
-                            | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => inner_ty,
-                            // Otherwise we resolve via the std::ops::Not trait
-                            _ => self
-                                .resolve_associated_type(inner_ty, self.resolve_ops_not_output()),
-                        }
-                    }
-                }
-            }
+            Expr::UnaryOp { expr, op } => self.infer_unop_expr(*op, *expr, expected, tgt_expr),
             Expr::BinaryOp { lhs, rhs, op } => match op {
-                Some(BinaryOp::LogicOp(_)) => {
-                    let bool_ty = self.types.bool;
-                    self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes);
-                    let lhs_diverges = self.diverges;
-                    self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty), ExprIsRead::Yes);
-                    // Depending on the LHS' value, the RHS can never execute.
-                    self.diverges = lhs_diverges;
-                    bool_ty
+                Some(BinaryOp::Assignment { op: Some(op) }) => {
+                    self.infer_assign_op_expr(tgt_expr, *op, *lhs, *rhs)
                 }
-                Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
-                _ => self.err_ty(),
+                Some(op) => self.infer_binop_expr(tgt_expr, *op, *lhs, *rhs),
+                None => self.err_ty(),
             },
             &Expr::Assignment { target, value } => {
                 // In ordinary (non-destructuring) assignments, the type of
@@ -833,55 +815,39 @@
                 }
             }
             Expr::Index { base, index } => {
-                let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
-                let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
+                let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes);
+                let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes);
 
-                if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
-                    let canonicalized = self.canonicalize(base_ty);
-                    let receiver_adjustments = method_resolution::resolve_indexing_op(
-                        &mut self.table,
-                        canonicalized,
-                        index_trait,
-                    );
-                    let (self_ty, mut adj) = receiver_adjustments
-                        .map_or((self.err_ty(), Vec::new()), |adj| {
-                            adj.apply(&mut self.table, base_ty)
-                        });
-
-                    // mutability will be fixed up in `InferenceContext::infer_mut`;
-                    adj.push(Adjustment::borrow(
-                        self.interner(),
-                        Mutability::Not,
-                        self_ty,
-                        self.table.next_region_var(),
-                    ));
-                    self.write_expr_adj(*base, adj.into_boxed_slice());
-                    if let Some(func) = index_trait
-                        .trait_items(self.db)
-                        .method_by_name(&Name::new_symbol_root(sym::index))
-                    {
-                        let subst = GenericArgs::new_from_iter(
-                            self.interner(),
-                            [self_ty.into(), index_ty.into()],
+                let base_t = self.table.structurally_resolve_type(base_t);
+                match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) {
+                    Some((trait_index_ty, trait_element_ty)) => {
+                        // two-phase not needed because index_ty is never mutable
+                        self.demand_coerce(
+                            *index,
+                            idx_t,
+                            trait_index_ty,
+                            AllowTwoPhase::No,
+                            ExprIsRead::Yes,
                         );
-                        self.write_method_resolution(tgt_expr, func, subst);
+                        self.table.select_obligations_where_possible();
+                        trait_element_ty
                     }
-                    let assoc = self.resolve_ops_index_output();
-                    self.resolve_associated_type_with_params(self_ty, assoc, &[index_ty.into()])
-                } else {
-                    self.err_ty()
+                    // FIXME: Report an error.
+                    None => self.types.error,
                 }
             }
             Expr::Tuple { exprs, .. } => {
-                let mut tys =
-                    match expected.only_has_type(&mut self.table).as_ref().map(|t| t.kind()) {
-                        Some(TyKind::Tuple(substs)) => substs
-                            .iter()
-                            .chain(repeat_with(|| self.table.next_ty_var()))
-                            .take(exprs.len())
-                            .collect::<Vec<_>>(),
-                        _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(),
-                    };
+                let mut tys = match expected
+                    .only_has_type(&mut self.table)
+                    .map(|t| self.table.try_structurally_resolve_type(t).kind())
+                {
+                    Some(TyKind::Tuple(substs)) => substs
+                        .iter()
+                        .chain(repeat_with(|| self.table.next_ty_var()))
+                        .take(exprs.len())
+                        .collect::<Vec<_>>(),
+                    _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(),
+                };
 
                 for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
                     *ty =
@@ -1014,7 +980,7 @@
                                     ty,
                                     fnptr_ty,
                                     AllowTwoPhase::No,
-                                    CoerceNever::Yes,
+                                    ExprIsRead::Yes,
                                 );
                             }
                             TyKind::Ref(_, base_ty, mutbl) => {
@@ -1024,7 +990,7 @@
                                     ty,
                                     ptr_ty,
                                     AllowTwoPhase::No,
-                                    CoerceNever::Yes,
+                                    ExprIsRead::Yes,
                                 );
                             }
                             _ => {}
@@ -1073,6 +1039,77 @@
         ty
     }
 
+    fn demand_scrutinee_type(
+        &mut self,
+        scrut: ExprId,
+        contains_ref_bindings: bool,
+        no_arms: bool,
+        scrutinee_is_read: ExprIsRead,
+    ) -> Ty<'db> {
+        // Not entirely obvious: if matches may create ref bindings, we want to
+        // use the *precise* type of the scrutinee, *not* some supertype, as
+        // the "scrutinee type" (issue #23116).
+        //
+        // arielb1 [writes here in this comment thread][c] that there
+        // is certainly *some* potential danger, e.g., for an example
+        // like:
+        //
+        // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956
+        //
+        // ```
+        // let Foo(x) = f()[0];
+        // ```
+        //
+        // Then if the pattern matches by reference, we want to match
+        // `f()[0]` as a lexpr, so we can't allow it to be
+        // coerced. But if the pattern matches by value, `f()[0]` is
+        // still syntactically a lexpr, but we *do* want to allow
+        // coercions.
+        //
+        // However, *likely* we are ok with allowing coercions to
+        // happen if there are no explicit ref mut patterns - all
+        // implicit ref mut patterns must occur behind a reference, so
+        // they will have the "correct" variance and lifetime.
+        //
+        // This does mean that the following pattern would be legal:
+        //
+        // ```
+        // struct Foo(Bar);
+        // struct Bar(u32);
+        // impl Deref for Foo {
+        //     type Target = Bar;
+        //     fn deref(&self) -> &Bar { &self.0 }
+        // }
+        // impl DerefMut for Foo {
+        //     fn deref_mut(&mut self) -> &mut Bar { &mut self.0 }
+        // }
+        // fn foo(x: &mut Foo) {
+        //     {
+        //         let Bar(z): &mut Bar = x;
+        //         *z = 42;
+        //     }
+        //     assert_eq!(foo.0.0, 42);
+        // }
+        // ```
+        //
+        // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which
+        // is problematic as the HIR is being scraped, but ref bindings may be
+        // implicit after #42640. We need to make sure that pat_adjustments
+        // (once introduced) is populated by the time we get here.
+        //
+        // See #44848.
+        if contains_ref_bindings || no_arms {
+            self.infer_expr_no_expect(scrut, scrutinee_is_read)
+        } else {
+            // ...but otherwise we want to use any supertype of the
+            // scrutinee. This is sort of a workaround, see note (*) in
+            // `check_pat` for some details.
+            let scrut_ty = self.table.next_ty_var();
+            self.infer_expr_coerce_never(scrut, &Expectation::HasType(scrut_ty), scrutinee_is_read);
+            scrut_ty
+        }
+    }
+
     fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> {
         let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id);
         let ty = match self.infer_path(path, id) {
@@ -1089,6 +1126,47 @@
         ty
     }
 
+    fn infer_unop_expr(
+        &mut self,
+        unop: UnaryOp,
+        oprnd: ExprId,
+        expected: &Expectation<'db>,
+        expr: ExprId,
+    ) -> Ty<'db> {
+        let expected_inner = match unop {
+            UnaryOp::Not | UnaryOp::Neg => expected,
+            UnaryOp::Deref => &Expectation::None,
+        };
+        let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes);
+
+        oprnd_t = self.table.structurally_resolve_type(oprnd_t);
+        match unop {
+            UnaryOp::Deref => {
+                if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
+                    oprnd_t = ty;
+                } else {
+                    // FIXME: Report an error.
+                    oprnd_t = self.types.error;
+                }
+            }
+            UnaryOp::Not => {
+                let result = self.infer_user_unop(expr, oprnd_t, unop);
+                // If it's builtin, we can reuse the type, this helps inference.
+                if !(oprnd_t.is_integral() || oprnd_t.kind() == TyKind::Bool) {
+                    oprnd_t = result;
+                }
+            }
+            UnaryOp::Neg => {
+                let result = self.infer_user_unop(expr, oprnd_t, unop);
+                // If it's builtin, we can reuse the type, this helps inference.
+                if !oprnd_t.is_numeric() {
+                    oprnd_t = result;
+                }
+            }
+        }
+        oprnd_t
+    }
+
     fn infer_async_block(
         &mut self,
         tgt_expr: ExprId,
@@ -1106,8 +1184,7 @@
         let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
             let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected);
             if let Some(target) = expected.only_has_type(&mut this.table) {
-                match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, CoerceNever::Yes)
-                {
+                match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) {
                     Ok(res) => res,
                     Err(_) => {
                         this.result
@@ -1220,7 +1297,10 @@
     }
 
     fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> {
-        let elem_ty = match expected.to_option(&mut self.table).as_ref().map(|t| t.kind()) {
+        let elem_ty = match expected
+            .to_option(&mut self.table)
+            .map(|t| self.table.try_structurally_resolve_type(t).kind())
+        {
             Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st,
             _ => self.table.next_ty_var(),
         };
@@ -1236,7 +1316,13 @@
                 let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements);
                 for &expr in elements.iter() {
                     let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
-                    coerce.coerce(self, &ObligationCause::new(), expr, cur_elem_ty);
+                    coerce.coerce(
+                        self,
+                        &ObligationCause::new(),
+                        expr,
+                        cur_elem_ty,
+                        ExprIsRead::Yes,
+                    );
                 }
                 (
                     coerce.complete(self),
@@ -1274,7 +1360,7 @@
         let return_expr_ty =
             self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
         let mut coerce_many = self.return_coercion.take().unwrap();
-        coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty);
+        coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty, ExprIsRead::Yes);
         self.return_coercion = Some(coerce_many);
     }
 
@@ -1285,7 +1371,13 @@
                     self.infer_return(expr);
                 } else {
                     let mut coerce = self.return_coercion.take().unwrap();
-                    coerce.coerce_forced_unit(self, ret, &ObligationCause::new(), true);
+                    coerce.coerce_forced_unit(
+                        self,
+                        ret,
+                        &ObligationCause::new(),
+                        true,
+                        ExprIsRead::Yes,
+                    );
                     self.return_coercion = Some(coerce);
                 }
             }
@@ -1350,103 +1442,6 @@
         }
     }
 
-    fn infer_overloadable_binop(
-        &mut self,
-        lhs: ExprId,
-        op: BinaryOp,
-        rhs: ExprId,
-        tgt_expr: ExprId,
-    ) -> Ty<'db> {
-        let lhs_expectation = Expectation::none();
-        let is_read = if matches!(op, BinaryOp::Assignment { .. }) {
-            ExprIsRead::Yes
-        } else {
-            ExprIsRead::No
-        };
-        let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read);
-        let rhs_ty = self.table.next_ty_var();
-
-        let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
-            let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
-            let func = trait_id.trait_items(self.db).method_by_name(&name)?;
-            Some((trait_id, func))
-        });
-        let func = match trait_func {
-            Some((_, it)) => it,
-            None => {
-                // HACK: `rhs_ty` is a general inference variable with no clue at all at this
-                // point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin
-                // type applicable to `op`.
-                let ret_ty = if self.is_builtin_binop(lhs_ty, lhs_ty, op) {
-                    // Assume both operands are builtin so we can continue inference. No guarantee
-                    // on the correctness, rustc would complain as necessary lang items don't seem
-                    // to exist anyway.
-                    self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op)
-                } else {
-                    self.err_ty()
-                };
-
-                self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes);
-
-                return ret_ty;
-            }
-        };
-
-        // HACK: We can use this substitution for the function because the function itself doesn't
-        // have its own generic parameters.
-        let args = GenericArgs::new_from_iter(self.interner(), [lhs_ty.into(), rhs_ty.into()]);
-
-        self.write_method_resolution(tgt_expr, func, args);
-
-        let method_ty = self.db.value_ty(func.into()).unwrap().instantiate(self.interner(), args);
-        self.register_obligations_for_call(method_ty);
-
-        self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes);
-
-        let ret_ty = match method_ty.callable_sig(self.interner()) {
-            Some(sig) => {
-                let sig = sig.skip_binder();
-                let p_left = sig.inputs_and_output.as_slice()[0];
-                if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. })
-                    && let TyKind::Ref(lt, _, mtbl) = p_left.kind()
-                {
-                    self.write_expr_adj(
-                        lhs,
-                        Box::new([Adjustment {
-                            kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)),
-                            target: p_left,
-                        }]),
-                    );
-                }
-                let p_right = sig.inputs_and_output.as_slice()[1];
-                if matches!(op, BinaryOp::CmpOp(..))
-                    && let TyKind::Ref(lt, _, mtbl) = p_right.kind()
-                {
-                    self.write_expr_adj(
-                        rhs,
-                        Box::new([Adjustment {
-                            kind: Adjust::Borrow(AutoBorrow::Ref(lt, mtbl)),
-                            target: p_right,
-                        }]),
-                    );
-                }
-                sig.output()
-            }
-            None => self.err_ty(),
-        };
-
-        let ret_ty = self.process_remote_user_written_ty(ret_ty);
-
-        if self.is_builtin_binop(lhs_ty, rhs_ty, op) {
-            // use knowledge of built-in binary ops, which can sometimes help inference
-            let builtin_ret = self.enforce_builtin_binop_types(lhs_ty, rhs_ty, op);
-            self.unify(builtin_ret, ret_ty);
-            builtin_ret
-        } else {
-            ret_ty
-        }
-    }
-
     fn infer_block(
         &mut self,
         expr: ExprId,
@@ -1548,20 +1543,13 @@
                         // 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) {
-                        let coerce_never = if this
-                            .expr_guaranteed_to_constitute_read_for_never(expr, ExprIsRead::Yes)
-                        {
-                            CoerceNever::Yes
-                        } else {
-                            CoerceNever::No
-                        };
                         if this
                             .coerce(
                                 expr.into(),
                                 this.types.unit,
                                 t,
                                 AllowTwoPhase::No,
-                                coerce_never,
+                                ExprIsRead::Yes,
                             )
                             .is_err()
                         {
@@ -1591,7 +1579,7 @@
         name: &Name,
     ) -> Option<(Ty<'db>, Either<FieldId, TupleFieldId>, Vec<Adjustment<'db>>, bool)> {
         let interner = self.interner();
-        let mut autoderef = self.table.autoderef(receiver_ty);
+        let mut autoderef = self.table.autoderef_with_tracking(receiver_ty);
         let mut private_field = None;
         let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
             let (field_id, parameters) = match derefed_ty.kind() {
@@ -1640,14 +1628,16 @@
 
         Some(match res {
             Some((field_id, ty)) => {
-                let adjustments = autoderef.adjust_steps();
+                let adjustments =
+                    self.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
                 let ty = self.process_remote_user_written_ty(ty);
 
                 (ty, field_id, adjustments, true)
             }
             None => {
                 let (field_id, subst) = private_field?;
-                let adjustments = autoderef.adjust_steps();
+                let adjustments =
+                    self.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
                 let ty = self.db.field_types(field_id.parent)[field_id.local_id]
                     .instantiate(self.interner(), subst);
                 let ty = self.process_remote_user_written_ty(ty);
@@ -1666,6 +1656,7 @@
     ) -> Ty<'db> {
         // Field projections don't constitute reads.
         let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No);
+        let receiver_ty = self.table.structurally_resolve_type(receiver_ty);
 
         if name.is_missing() {
             // Bail out early, don't even try to look up field. Also, we don't issue an unresolved
@@ -1689,46 +1680,39 @@
             None => {
                 // no field found, lets attempt to resolve it like a function so that IDE things
                 // work out while people are typing
-                let canonicalized_receiver = self.canonicalize(receiver_ty);
-                let resolved = method_resolution::lookup_method(
-                    &canonicalized_receiver,
-                    &mut self.table,
-                    Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
-                        .as_ref()
-                        .left_or_else(|&it| it),
-                    VisibleFromModule::Filter(self.resolver.module()),
-                    name,
+                let resolved = self.lookup_method_including_private(
+                    receiver_ty,
+                    name.clone(),
+                    None,
+                    receiver,
+                    tgt_expr,
                 );
                 self.push_diagnostic(InferenceDiagnostic::UnresolvedField {
                     expr: tgt_expr,
                     receiver: receiver_ty,
                     name: name.clone(),
-                    method_with_same_name_exists: resolved.is_some(),
+                    method_with_same_name_exists: resolved.is_ok(),
                 });
                 match resolved {
-                    Some((adjust, func, _)) => {
-                        let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
-                        let args = self.substs_for_method_call(tgt_expr, func.into(), None);
-                        self.write_expr_adj(receiver, adjustments.into_boxed_slice());
-                        self.write_method_resolution(tgt_expr, func, args);
-
-                        self.check_method_call(
-                            tgt_expr,
-                            &[],
-                            self.db
-                                .value_ty(func.into())
-                                .unwrap()
-                                .instantiate(self.interner(), args),
-                            ty,
-                            expected,
-                        )
+                    Ok((func, _is_visible)) => {
+                        self.check_method_call(tgt_expr, &[], func.sig, receiver_ty, expected)
                     }
-                    None => self.err_ty(),
+                    Err(_) => self.err_ty(),
                 }
             }
         }
     }
 
+    fn instantiate_erroneous_method(&mut self, def_id: FunctionId) -> MethodCallee<'db> {
+        // FIXME: Using fresh infer vars for the method args isn't optimal,
+        // we can do better by going thorough the full probe/confirm machinery.
+        let args = self.table.fresh_args_for_item(def_id.into());
+        let sig = self.db.callable_item_signature(def_id.into()).instantiate(self.interner(), args);
+        let sig =
+            self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, sig);
+        MethodCallee { def_id, args, sig }
+    }
+
     fn infer_call(
         &mut self,
         tgt_expr: ExprId,
@@ -1737,13 +1721,14 @@
         expected: &Expectation<'db>,
     ) -> Ty<'db> {
         let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
+        let callee_ty = self.table.try_structurally_resolve_type(callee_ty);
         let interner = self.interner();
-        let mut derefs = self.table.autoderef(callee_ty);
+        let mut derefs = InferenceContextAutoderef::new_from_inference_context(self, callee_ty);
         let (res, derefed_callee) = loop {
             let Some((callee_deref_ty, _)) = derefs.next() else {
                 break (None, callee_ty);
             };
-            if let Some(res) = derefs.table.callable_sig(callee_deref_ty, args.len()) {
+            if let Some(res) = derefs.ctx().table.callable_sig(callee_deref_ty, args.len()) {
                 break (Some(res), callee_deref_ty);
             }
         };
@@ -1753,7 +1738,8 @@
             || res.is_none();
         let (param_tys, ret_ty) = match res {
             Some((func, params, ret_ty)) => {
-                let mut adjustments = derefs.adjust_steps();
+                let infer_ok = derefs.adjust_steps_as_infer_ok();
+                let mut adjustments = self.table.register_infer_ok(infer_ok);
                 if let Some(fn_x) = func {
                     self.write_fn_trait_method_resolution(
                         fn_x,
@@ -1819,7 +1805,7 @@
             indices_to_skip,
             is_varargs,
         );
-        self.table.normalize_associated_types_in(ret_ty)
+        ret_ty
     }
 
     fn infer_method_call(
@@ -1834,56 +1820,26 @@
         let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
         let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty);
 
-        if matches!(receiver_ty.kind(), TyKind::Error(_) | TyKind::Infer(InferTy::TyVar(_))) {
-            // Don't probe on error type, or on a fully unresolved infer var.
-            // FIXME: Emit an error if we're probing on an infer var (type annotations needed).
-            for &arg in args {
-                // Make sure we infer and record the arguments.
-                self.infer_expr_no_expect(arg, ExprIsRead::Yes);
-            }
-            return receiver_ty;
-        }
-
-        let canonicalized_receiver = self.canonicalize(receiver_ty);
-
-        let resolved = method_resolution::lookup_method(
-            &canonicalized_receiver,
-            &mut self.table,
-            Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
-                .as_ref()
-                .left_or_else(|&it| it),
-            VisibleFromModule::Filter(self.resolver.module()),
-            method_name,
+        let resolved = self.lookup_method_including_private(
+            receiver_ty,
+            method_name.clone(),
+            generic_args,
+            receiver,
+            tgt_expr,
         );
         match resolved {
-            Some((adjust, func, visible)) => {
+            Ok((func, visible)) => {
                 if !visible {
                     self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
                         id: tgt_expr.into(),
-                        item: func.into(),
+                        item: func.def_id.into(),
                     })
                 }
-
-                let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
-                self.write_expr_adj(receiver, adjustments.into_boxed_slice());
-
-                let gen_args = self.substs_for_method_call(tgt_expr, func.into(), generic_args);
-                self.write_method_resolution(tgt_expr, func, gen_args);
-                let interner = DbInterner::new_with(self.db, None, None);
-                self.check_method_call(
-                    tgt_expr,
-                    args,
-                    self.db
-                        .value_ty(func.into())
-                        .expect("we have a function def")
-                        .instantiate(interner, gen_args),
-                    ty,
-                    expected,
-                )
+                self.check_method_call(tgt_expr, args, func.sig, receiver_ty, expected)
             }
             // Failed to resolve, report diagnostic and try to resolve as call to field access or
             // assoc function
-            None => {
+            Err(_) => {
                 let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name)
                 {
                     Some((ty, field_id, adjustments, _public)) => {
@@ -1894,65 +1850,65 @@
                     None => None,
                 };
 
-                let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
-                    &canonicalized_receiver,
-                    &mut self.table,
-                    Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
-                        .as_ref()
-                        .left_or_else(|&it| it),
-                    VisibleFromModule::Filter(self.resolver.module()),
-                    Some(method_name),
-                    method_resolution::LookupMode::Path,
-                    |_ty, item, visible| match item {
-                        hir_def::AssocItemId::FunctionId(function_id) if visible => {
-                            Some(function_id)
-                        }
-                        _ => None,
-                    },
-                );
+                let assoc_func_with_same_name = self.with_method_resolution(|ctx| {
+                    if !matches!(
+                        receiver_ty.kind(),
+                        TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_)
+                    ) {
+                        ctx.probe_for_name(
+                            method_resolution::Mode::Path,
+                            method_name.clone(),
+                            receiver_ty,
+                        )
+                    } else {
+                        Err(MethodError::ErrorReported)
+                    }
+                });
+                let assoc_func_with_same_name = match assoc_func_with_same_name {
+                    Ok(method_resolution::Pick {
+                        item: CandidateId::FunctionId(def_id), ..
+                    })
+                    | Err(MethodError::PrivateMatch(method_resolution::Pick {
+                        item: CandidateId::FunctionId(def_id),
+                        ..
+                    })) => Some(self.instantiate_erroneous_method(def_id)),
+                    _ => None,
+                };
 
                 self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall {
                     expr: tgt_expr,
                     receiver: receiver_ty,
                     name: method_name.clone(),
                     field_with_same_name: field_with_same_name_exists,
-                    assoc_func_with_same_name,
+                    assoc_func_with_same_name: assoc_func_with_same_name.map(|it| it.def_id),
                 });
 
                 let recovered = match assoc_func_with_same_name {
-                    Some(f) => {
-                        let args = self.substs_for_method_call(tgt_expr, f.into(), generic_args);
-                        let interner = DbInterner::new_with(self.db, None, None);
-                        let f = self
-                            .db
-                            .value_ty(f.into())
-                            .expect("we have a function def")
-                            .instantiate(interner, args);
-                        let sig = f.callable_sig(self.interner()).expect("we have a function def");
-                        Some((f, sig, true))
-                    }
+                    Some(it) => Some((
+                        Ty::new_fn_def(
+                            self.interner(),
+                            CallableDefId::FunctionId(it.def_id).into(),
+                            it.args,
+                        ),
+                        it.sig,
+                        true,
+                    )),
                     None => field_with_same_name_exists.and_then(|field_ty| {
                         let callable_sig = field_ty.callable_sig(self.interner())?;
-                        Some((field_ty, callable_sig, false))
+                        Some((field_ty, callable_sig.skip_binder(), false))
                     }),
                 };
                 match recovered {
-                    Some((callee_ty, sig, strip_first)) => {
-                        let sig = sig.skip_binder();
-                        self.check_call(
-                            tgt_expr,
-                            args,
-                            callee_ty,
-                            sig.inputs_and_output
-                                .inputs()
-                                .get(strip_first as usize..)
-                                .unwrap_or(&[]),
-                            sig.output(),
-                            &[],
-                            true,
-                            expected,
-                        )
-                    }
+                    Some((callee_ty, sig, strip_first)) => self.check_call(
+                        tgt_expr,
+                        args,
+                        callee_ty,
+                        sig.inputs_and_output.inputs().get(strip_first as usize..).unwrap_or(&[]),
+                        sig.output(),
+                        &[],
+                        true,
+                        expected,
+                    ),
                     None => {
                         for &arg in args.iter() {
                             self.infer_expr_no_expect(arg, ExprIsRead::Yes);
@@ -1968,38 +1924,20 @@
         &mut self,
         tgt_expr: ExprId,
         args: &[ExprId],
-        method_ty: Ty<'db>,
+        sig: FnSig<'db>,
         receiver_ty: Ty<'db>,
         expected: &Expectation<'db>,
     ) -> Ty<'db> {
-        self.register_obligations_for_call(method_ty);
-        let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
-            match method_ty.callable_sig(self.interner()) {
-                Some(sig) => {
-                    let sig = sig.skip_binder();
-                    (
-                        if !sig.inputs_and_output.inputs().is_empty() {
-                            (
-                                sig.inputs_and_output.as_slice()[0],
-                                sig.inputs_and_output.inputs()[1..].to_vec(),
-                            )
-                        } else {
-                            (self.types.error, Vec::new())
-                        },
-                        sig.output(),
-                        sig.c_variadic,
-                    )
-                }
-                None => {
-                    let formal_receiver_ty = self.table.next_ty_var();
-                    let ret_ty = self.table.next_ty_var();
-                    ((formal_receiver_ty, Vec::new()), ret_ty, true)
-                }
-            };
+        let (formal_receiver_ty, param_tys) = if !sig.inputs_and_output.inputs().is_empty() {
+            (sig.inputs_and_output.as_slice()[0], &sig.inputs_and_output.inputs()[1..])
+        } else {
+            (self.types.error, &[] as _)
+        };
+        let ret_ty = sig.output();
         self.table.unify(formal_receiver_ty, receiver_ty);
 
-        self.check_call_arguments(tgt_expr, &param_tys, ret_ty, expected, args, &[], is_varargs);
-        self.table.normalize_associated_types_in(ret_ty)
+        self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic);
+        ret_ty
     }
 
     /// Generic function that factors out common logic from function calls,
@@ -2110,20 +2048,13 @@
             // fulfillment error to be more accurate.
             let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty);
 
-            let coerce_never = if this
-                .expr_guaranteed_to_constitute_read_for_never(provided_arg, ExprIsRead::Yes)
-            {
-                CoerceNever::Yes
-            } else {
-                CoerceNever::No
-            };
             let coerce_error = this
                 .coerce(
                     provided_arg.into(),
                     checked_ty,
                     coerced_ty,
                     AllowTwoPhase::Yes,
-                    coerce_never,
+                    ExprIsRead::Yes,
                 )
                 .err();
             if coerce_error.is_some() {
@@ -2204,144 +2135,19 @@
         if !args_count_matches {}
     }
 
-    fn substs_for_method_call(
-        &mut self,
-        expr: ExprId,
-        def: GenericDefId,
-        generic_args: Option<&HirGenericArgs>,
-    ) -> GenericArgs<'db> {
-        struct LowererCtx<'a, 'b, 'db> {
-            ctx: &'a mut InferenceContext<'b, 'db>,
-            expr: ExprId,
-        }
-
-        impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, 'db> {
-            fn report_len_mismatch(
-                &mut self,
-                def: GenericDefId,
-                provided_count: u32,
-                expected_count: u32,
-                kind: IncorrectGenericsLenKind,
-            ) {
-                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsLen {
-                    expr: self.expr,
-                    provided_count,
-                    expected_count,
-                    kind,
-                    def,
-                });
-            }
-
-            fn report_arg_mismatch(
-                &mut self,
-                param_id: GenericParamId,
-                arg_idx: u32,
-                has_self_arg: bool,
-            ) {
-                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
-                    expr: self.expr,
-                    param_id,
-                    arg_idx,
-                    has_self_arg,
-                });
-            }
-
-            fn provided_kind(
-                &mut self,
-                param_id: GenericParamId,
-                param: GenericParamDataRef<'_>,
-                arg: &HirGenericArg,
-            ) -> GenericArg<'db> {
-                match (param, arg) {
-                    (
-                        GenericParamDataRef::LifetimeParamData(_),
-                        HirGenericArg::Lifetime(lifetime),
-                    ) => self.ctx.make_body_lifetime(*lifetime).into(),
-                    (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => {
-                        self.ctx.make_body_ty(*type_ref).into()
-                    }
-                    (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => {
-                        let GenericParamId::ConstParamId(const_id) = param_id else {
-                            unreachable!("non-const param ID for const param");
-                        };
-                        let const_ty = self.ctx.db.const_param_ty_ns(const_id);
-                        self.ctx.make_body_const(*konst, const_ty).into()
-                    }
-                    _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
-                }
-            }
-
-            fn provided_type_like_const(
-                &mut self,
-                const_ty: Ty<'db>,
-                arg: TypeLikeConst<'_>,
-            ) -> Const<'db> {
-                match arg {
-                    TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
-                    TypeLikeConst::Infer => self.ctx.table.next_const_var(),
-                }
-            }
-
-            fn inferred_kind(
-                &mut self,
-                _def: GenericDefId,
-                param_id: GenericParamId,
-                _param: GenericParamDataRef<'_>,
-                _infer_args: bool,
-                _preceding_args: &[GenericArg<'db>],
-            ) -> GenericArg<'db> {
-                // Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
-                // and I think it's also required in the presence of `impl Trait` (that must be inferred).
-                self.ctx.table.next_var_for_param(param_id)
-            }
-
-            fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> {
-                self.ctx.table.next_var_for_param(param_id)
-            }
-
-            fn report_elided_lifetimes_in_path(
-                &mut self,
-                _def: GenericDefId,
-                _expected_count: u32,
-                _hard_error: bool,
-            ) {
-                unreachable!("we set `LifetimeElisionKind::Infer`")
-            }
-
-            fn report_elision_failure(&mut self, _def: GenericDefId, _expected_count: u32) {
-                unreachable!("we set `LifetimeElisionKind::Infer`")
-            }
-
-            fn report_missing_lifetime(&mut self, _def: GenericDefId, _expected_count: u32) {
-                unreachable!("we set `LifetimeElisionKind::Infer`")
-            }
-        }
-
-        substs_from_args_and_bindings(
-            self.db,
-            self.body,
-            generic_args,
-            def,
-            true,
-            LifetimeElisionKind::Infer,
-            false,
-            None,
-            &mut LowererCtx { ctx: self, expr },
-        )
-    }
-
     fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) {
         let callable_ty = self.table.try_structurally_resolve_type(callable_ty);
         if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() {
-            let generic_predicates =
-                self.db.generic_predicates(GenericDefId::from_callable(self.db, fn_def.0));
-            if let Some(predicates) = generic_predicates.instantiate(self.interner(), parameters) {
-                let interner = self.interner();
-                let param_env = self.table.trait_env.env;
-                self.table.register_predicates(predicates.map(|predicate| {
-                    Obligation::new(interner, ObligationCause::new(), param_env, predicate)
-                }));
-            }
+            let generic_predicates = GenericPredicates::query_all(
+                self.db,
+                GenericDefId::from_callable(self.db, fn_def.0),
+            );
+            let param_env = self.table.trait_env.env;
+            self.table.register_predicates(clauses_as_obligations(
+                generic_predicates.iter_instantiated_copied(self.interner(), parameters.as_slice()),
+                ObligationCause::new(),
+                param_env,
+            ));
             // add obligation for trait implementation, if this is a trait method
             match fn_def.0 {
                 CallableDefId::FunctionId(f) => {
@@ -2410,122 +2216,6 @@
         indices
     }
 
-    /// Dereferences a single level of immutable referencing.
-    fn deref_ty_if_possible(&mut self, ty: Ty<'db>) -> Ty<'db> {
-        let ty = self.table.try_structurally_resolve_type(ty);
-        match ty.kind() {
-            TyKind::Ref(_, inner, Mutability::Not) => {
-                self.table.try_structurally_resolve_type(inner)
-            }
-            _ => ty,
-        }
-    }
-
-    /// Enforces expectations on lhs type and rhs type depending on the operator and returns the
-    /// output type of the binary op.
-    fn enforce_builtin_binop_types(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> Ty<'db> {
-        // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
-        let lhs = self.deref_ty_if_possible(lhs);
-        let rhs = self.deref_ty_if_possible(rhs);
-
-        let (op, is_assign) = match op {
-            BinaryOp::Assignment { op: Some(inner) } => (BinaryOp::ArithOp(inner), true),
-            _ => (op, false),
-        };
-
-        let output_ty = match op {
-            BinaryOp::LogicOp(_) => {
-                let bool_ = self.types.bool;
-                self.unify(lhs, bool_);
-                self.unify(rhs, bool_);
-                bool_
-            }
-
-            BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
-                // result type is same as LHS always
-                lhs
-            }
-
-            BinaryOp::ArithOp(_) => {
-                // LHS, RHS, and result will have the same type
-                self.unify(lhs, rhs);
-                lhs
-            }
-
-            BinaryOp::CmpOp(_) => {
-                // LHS and RHS will have the same type
-                self.unify(lhs, rhs);
-                self.types.bool
-            }
-
-            BinaryOp::Assignment { op: None } => {
-                stdx::never!("Simple assignment operator is not binary op.");
-                lhs
-            }
-
-            BinaryOp::Assignment { .. } => unreachable!("handled above"),
-        };
-
-        if is_assign { self.types.unit } else { output_ty }
-    }
-
-    fn is_builtin_binop(&mut self, lhs: Ty<'db>, rhs: Ty<'db>, op: BinaryOp) -> bool {
-        // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
-        let lhs = self.deref_ty_if_possible(lhs);
-        let rhs = self.deref_ty_if_possible(rhs);
-
-        let op = match op {
-            BinaryOp::Assignment { op: Some(inner) } => BinaryOp::ArithOp(inner),
-            _ => op,
-        };
-
-        match op {
-            BinaryOp::LogicOp(_) => true,
-
-            BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
-                lhs.is_integral() && rhs.is_integral()
-            }
-
-            BinaryOp::ArithOp(
-                ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div | ArithOp::Rem,
-            ) => {
-                lhs.is_integral() && rhs.is_integral()
-                    || lhs.is_floating_point() && rhs.is_floating_point()
-            }
-
-            BinaryOp::ArithOp(ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) => {
-                lhs.is_integral() && rhs.is_integral()
-                    || lhs.is_floating_point() && rhs.is_floating_point()
-                    || matches!((lhs.kind(), rhs.kind()), (TyKind::Bool, TyKind::Bool))
-            }
-
-            BinaryOp::CmpOp(_) => {
-                let is_scalar = |kind| {
-                    matches!(
-                        kind,
-                        TyKind::Bool
-                            | TyKind::Char
-                            | TyKind::Int(_)
-                            | TyKind::Uint(_)
-                            | TyKind::Float(_)
-                            | TyKind::FnDef(..)
-                            | TyKind::FnPtr(..)
-                            | TyKind::RawPtr(..)
-                            | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_))
-                    )
-                };
-                is_scalar(lhs.kind()) && is_scalar(rhs.kind())
-            }
-
-            BinaryOp::Assignment { op: None } => {
-                stdx::never!("Simple assignment operator is not binary op.");
-                false
-            }
-
-            BinaryOp::Assignment { .. } => unreachable!("handled above"),
-        }
-    }
-
     pub(super) fn with_breakable_ctx<T>(
         &mut self,
         kind: BreakableKind,
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 71a9c94..a257547 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -1,25 +1,15 @@
 //! Finds if an expression is an immutable context or a mutable context, which is used in selecting
 //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
 
-use hir_def::{
-    hir::{
-        Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement,
-        UnaryOp,
-    },
-    lang_item::LangItem,
+use hir_def::hir::{
+    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, UnaryOp,
 };
-use hir_expand::name::Name;
-use intern::sym;
 use rustc_ast_ir::Mutability;
-use rustc_type_ir::inherent::IntoKind;
 
-use crate::next_solver::infer::traits::{Obligation, ObligationCause};
-use crate::next_solver::{GenericArgs, TraitRef};
 use crate::{
-    Adjust, Adjustment, AutoBorrow, OverloadedDeref,
-    infer::{Expectation, InferenceContext, expr::ExprIsRead},
+    Adjust, AutoBorrow, OverloadedDeref,
+    infer::{InferenceContext, place_op::PlaceOp},
     lower::lower_mutability,
-    next_solver::TyKind,
 };
 
 impl<'db> InferenceContext<'_, 'db> {
@@ -28,13 +18,33 @@
     }
 
     fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
+        let krate = self.krate();
         if let Some(adjustments) = self.result.expr_adjustments.get_mut(&tgt_expr) {
-            for adj in adjustments.iter_mut().rev() {
+            let mut adjustments = adjustments.iter_mut().rev().peekable();
+            while let Some(adj) = adjustments.next() {
                 match &mut adj.kind {
                     Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
-                    Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
+                    Adjust::Deref(Some(d)) => {
+                        if mutability == Mutability::Mut {
+                            let source_ty = match adjustments.peek() {
+                                Some(prev_adj) => prev_adj.target,
+                                None => self.result.type_of_expr[tgt_expr],
+                            };
+                            if let Some(infer_ok) = Self::try_mutable_overloaded_place_op(
+                                &self.table,
+                                krate,
+                                source_ty,
+                                None,
+                                PlaceOp::Deref,
+                            ) {
+                                self.table.register_predicates(infer_ok.obligations);
+                            }
+                            *d = OverloadedDeref(Some(mutability));
+                        }
+                    }
                     Adjust::Borrow(b) => match b {
-                        AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m) => mutability = *m,
+                        AutoBorrow::Ref(m) => mutability = (*m).into(),
+                        AutoBorrow::RawPtr(m) => mutability = *m,
                     },
                 }
             }
@@ -128,75 +138,15 @@
                 self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
             }
             &Expr::Index { base, index } => {
-                if mutability == Mutability::Mut
-                    && let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr)
-                    && let Some(index_trait) =
-                        LangItem::IndexMut.resolve_trait(self.db, self.table.trait_env.krate)
-                    && let Some(index_fn) = index_trait
-                        .trait_items(self.db)
-                        .method_by_name(&Name::new_symbol_root(sym::index_mut))
-                {
-                    *f = index_fn;
-                    let mut base_ty = None;
-                    let base_adjustments =
-                        self.result.expr_adjustments.get_mut(&base).and_then(|it| it.last_mut());
-                    if let Some(Adjustment {
-                        kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
-                        target,
-                    }) = base_adjustments
-                    {
-                        if let TyKind::Ref(_, ty, _) = target.kind() {
-                            base_ty = Some(ty);
-                        }
-                        *mutability = Mutability::Mut;
-                    }
-
-                    // Apply `IndexMut` obligation for non-assignee expr
-                    if let Some(base_ty) = base_ty {
-                        let index_ty = if let Some(ty) = self.result.type_of_expr.get(index) {
-                            *ty
-                        } else {
-                            self.infer_expr(index, &Expectation::none(), ExprIsRead::Yes)
-                        };
-                        let trait_ref = TraitRef::new(
-                            self.interner(),
-                            index_trait.into(),
-                            GenericArgs::new_from_iter(
-                                self.interner(),
-                                [base_ty.into(), index_ty.into()],
-                            ),
-                        );
-                        self.table.register_predicate(Obligation::new(
-                            self.interner(),
-                            ObligationCause::new(),
-                            self.table.trait_env.env,
-                            trait_ref,
-                        ));
-                    }
+                if mutability == Mutability::Mut {
+                    self.convert_place_op_to_mutable(PlaceOp::Index, tgt_expr, base, Some(index));
                 }
                 self.infer_mut_expr(base, mutability);
                 self.infer_mut_expr(index, Mutability::Not);
             }
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-                let mut mutability = mutability;
-                if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr)
-                    && mutability == Mutability::Mut
-                    && let Some(deref_trait) =
-                        LangItem::DerefMut.resolve_trait(self.db, self.table.trait_env.krate)
-                {
-                    let ty = self.result.type_of_expr.get(*expr);
-                    let is_mut_ptr = ty.is_some_and(|ty| {
-                        let ty = self.table.shallow_resolve(*ty);
-                        matches!(ty.kind(), TyKind::RawPtr(_, Mutability::Mut))
-                    });
-                    if is_mut_ptr {
-                        mutability = Mutability::Not;
-                    } else if let Some(deref_fn) = deref_trait
-                        .trait_items(self.db)
-                        .method_by_name(&Name::new_symbol_root(sym::deref_mut))
-                    {
-                        *f = deref_fn;
-                    }
+                if mutability == Mutability::Mut {
+                    self.convert_place_op_to_mutable(PlaceOp::Deref, tgt_expr, *expr, None);
                 }
                 self.infer_mut_expr(*expr, mutability);
             }
diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs
new file mode 100644
index 0000000..57d4900
--- /dev/null
+++ b/crates/hir-ty/src/infer/op.rs
@@ -0,0 +1,468 @@
+//! Inference of binary and unary operators.
+
+use std::collections::hash_map;
+
+use hir_def::{GenericParamId, TraitId, hir::ExprId, lang_item::LangItem};
+use intern::{Symbol, sym};
+use rustc_ast_ir::Mutability;
+use rustc_type_ir::inherent::{IntoKind, Ty as _};
+use syntax::ast::{ArithOp, BinaryOp, UnaryOp};
+use tracing::debug;
+
+use crate::{
+    Adjust, Adjustment, AutoBorrow,
+    infer::{AllowTwoPhase, AutoBorrowMutability, Expectation, InferenceContext, expr::ExprIsRead},
+    method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
+    next_solver::{
+        GenericArgs, TraitRef, Ty, TyKind,
+        fulfill::NextSolverError,
+        infer::traits::{Obligation, ObligationCause},
+        obligation_ctxt::ObligationCtxt,
+    },
+};
+
+impl<'a, 'db> InferenceContext<'a, 'db> {
+    /// Checks a `a <op>= b`
+    pub(crate) fn infer_assign_op_expr(
+        &mut self,
+        expr: ExprId,
+        op: ArithOp,
+        lhs: ExprId,
+        rhs: ExprId,
+    ) -> Ty<'db> {
+        let (lhs_ty, rhs_ty, return_ty) =
+            self.infer_overloaded_binop(expr, lhs, rhs, BinaryOp::Assignment { op: Some(op) });
+
+        let category = BinOpCategory::from(op);
+        let ty = if !lhs_ty.is_ty_var()
+            && !rhs_ty.is_ty_var()
+            && is_builtin_binop(lhs_ty, rhs_ty, category)
+        {
+            self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category);
+            self.types.unit
+        } else {
+            return_ty
+        };
+
+        self.check_lhs_assignable(lhs);
+
+        ty
+    }
+
+    /// Checks a potentially overloaded binary operator.
+    pub(crate) fn infer_binop_expr(
+        &mut self,
+        expr: ExprId,
+        op: BinaryOp,
+        lhs_expr: ExprId,
+        rhs_expr: ExprId,
+    ) -> Ty<'db> {
+        debug!(
+            "check_binop(expr.hir_id={:?}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
+            expr, expr, op, lhs_expr, rhs_expr
+        );
+
+        match op {
+            BinaryOp::LogicOp(_) => {
+                // && and || are a simple case.
+                self.infer_expr_coerce(
+                    lhs_expr,
+                    &Expectation::HasType(self.types.bool),
+                    ExprIsRead::Yes,
+                );
+                let lhs_diverges = self.diverges;
+                self.infer_expr_coerce(
+                    rhs_expr,
+                    &Expectation::HasType(self.types.bool),
+                    ExprIsRead::Yes,
+                );
+
+                // Depending on the LHS' value, the RHS can never execute.
+                self.diverges = lhs_diverges;
+
+                self.types.bool
+            }
+            _ => {
+                // Otherwise, we always treat operators as if they are
+                // overloaded. This is the way to be most flexible w/r/t
+                // types that get inferred.
+                let (lhs_ty, rhs_ty, return_ty) =
+                    self.infer_overloaded_binop(expr, lhs_expr, rhs_expr, op);
+
+                // Supply type inference hints if relevant. Probably these
+                // hints should be enforced during select as part of the
+                // `consider_unification_despite_ambiguity` routine, but this
+                // more convenient for now.
+                //
+                // The basic idea is to help type inference by taking
+                // advantage of things we know about how the impls for
+                // scalar types are arranged. This is important in a
+                // scenario like `1_u32 << 2`, because it lets us quickly
+                // deduce that the result type should be `u32`, even
+                // though we don't know yet what type 2 has and hence
+                // can't pin this down to a specific impl.
+                let category = BinOpCategory::from(op);
+                if !lhs_ty.is_ty_var()
+                    && !rhs_ty.is_ty_var()
+                    && is_builtin_binop(lhs_ty, rhs_ty, category)
+                {
+                    let builtin_return_ty =
+                        self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category);
+                    self.demand_eqtype(builtin_return_ty, return_ty);
+                    builtin_return_ty
+                } else {
+                    return_ty
+                }
+            }
+        }
+    }
+
+    fn enforce_builtin_binop_types(
+        &mut self,
+        lhs_ty: Ty<'db>,
+        rhs_ty: Ty<'db>,
+        category: BinOpCategory,
+    ) -> Ty<'db> {
+        debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category));
+
+        // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
+        // (See https://github.com/rust-lang/rust/issues/57447.)
+        let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
+
+        match category {
+            BinOpCategory::Shortcircuit => {
+                self.demand_suptype(self.types.bool, lhs_ty);
+                self.demand_suptype(self.types.bool, rhs_ty);
+                self.types.bool
+            }
+
+            BinOpCategory::Shift => {
+                // result type is same as LHS always
+                lhs_ty
+            }
+
+            BinOpCategory::Math | BinOpCategory::Bitwise => {
+                // both LHS and RHS and result will have the same type
+                self.demand_suptype(lhs_ty, rhs_ty);
+                lhs_ty
+            }
+
+            BinOpCategory::Comparison => {
+                // both LHS and RHS and result will have the same type
+                self.demand_suptype(lhs_ty, rhs_ty);
+                self.types.bool
+            }
+        }
+    }
+
+    fn infer_overloaded_binop(
+        &mut self,
+        expr: ExprId,
+        lhs_expr: ExprId,
+        rhs_expr: ExprId,
+        op: BinaryOp,
+    ) -> (Ty<'db>, Ty<'db>, Ty<'db>) {
+        debug!("infer_overloaded_binop(expr.hir_id={:?}, op={:?})", expr, op);
+
+        let lhs_ty = match op {
+            BinaryOp::Assignment { .. } => {
+                // rust-lang/rust#52126: We have to use strict
+                // equivalence on the LHS of an assign-op like `+=`;
+                // overwritten or mutably-borrowed places cannot be
+                // coerced to a supertype.
+                self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes)
+            }
+            _ => {
+                // Find a suitable supertype of the LHS expression's type, by coercing to
+                // a type variable, to pass as the `Self` to the trait, avoiding invariant
+                // trait matching creating lifetime constraints that are too strict.
+                // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
+                // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
+                let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::No);
+                let fresh_var = self.table.next_ty_var();
+                self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::No)
+            }
+        };
+        let lhs_ty = self.table.resolve_vars_with_obligations(lhs_ty);
+
+        // N.B., as we have not yet type-checked the RHS, we don't have the
+        // type at hand. Make a variable to represent it. The whole reason
+        // for this indirection is so that, below, we can check the expr
+        // using this variable as the expected type, which sometimes lets
+        // us do better coercions than we would be able to do otherwise,
+        // particularly for things like `String + &String`.
+        let rhs_ty_var = self.table.next_ty_var();
+        let result = self.lookup_op_method(
+            lhs_ty,
+            Some((rhs_expr, rhs_ty_var)),
+            self.lang_item_for_bin_op(op),
+        );
+
+        // see `NB` above
+        let rhs_ty =
+            self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty_var), ExprIsRead::No);
+        let rhs_ty = self.table.resolve_vars_with_obligations(rhs_ty);
+
+        let return_ty = match result {
+            Ok(method) => {
+                let by_ref_binop = !is_op_by_value(op);
+                if (matches!(op, BinaryOp::Assignment { .. }) || by_ref_binop)
+                    && let TyKind::Ref(_, _, mutbl) =
+                        method.sig.inputs_and_output.inputs()[0].kind()
+                {
+                    let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes);
+                    let autoref = Adjustment {
+                        kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
+                        target: method.sig.inputs_and_output.inputs()[0],
+                    };
+                    self.write_expr_adj(lhs_expr, Box::new([autoref]));
+                }
+                if by_ref_binop
+                    && let TyKind::Ref(_, _, mutbl) =
+                        method.sig.inputs_and_output.inputs()[1].kind()
+                {
+                    // Allow two-phase borrows for binops in initial deployment
+                    // since they desugar to methods
+                    let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes);
+
+                    let autoref = Adjustment {
+                        kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
+                        target: method.sig.inputs_and_output.inputs()[1],
+                    };
+                    // HACK(eddyb) Bypass checks due to reborrows being in
+                    // some cases applied on the RHS, on top of which we need
+                    // to autoref, which is not allowed by write_expr_adj.
+                    // self.write_expr_adj(rhs_expr, Box::new([autoref]));
+                    match self.result.expr_adjustments.entry(rhs_expr) {
+                        hash_map::Entry::Occupied(mut entry) => {
+                            let mut adjustments = Vec::from(std::mem::take(entry.get_mut()));
+                            adjustments.reserve_exact(1);
+                            adjustments.push(autoref);
+                            entry.insert(adjustments.into_boxed_slice());
+                        }
+                        hash_map::Entry::Vacant(entry) => {
+                            entry.insert(Box::new([autoref]));
+                        }
+                    };
+                }
+                self.write_method_resolution(expr, method.def_id, method.args);
+
+                method.sig.output()
+            }
+            Err(_errors) => {
+                // FIXME: Report diagnostic.
+                self.types.error
+            }
+        };
+
+        (lhs_ty, rhs_ty, return_ty)
+    }
+
+    pub(crate) fn infer_user_unop(
+        &mut self,
+        ex: ExprId,
+        operand_ty: Ty<'db>,
+        op: UnaryOp,
+    ) -> Ty<'db> {
+        match self.lookup_op_method(operand_ty, None, self.lang_item_for_unop(op)) {
+            Ok(method) => {
+                self.write_method_resolution(ex, method.def_id, method.args);
+                method.sig.output()
+            }
+            Err(_errors) => {
+                // FIXME: Report diagnostic.
+                self.types.error
+            }
+        }
+    }
+
+    fn lookup_op_method(
+        &mut self,
+        lhs_ty: Ty<'db>,
+        opt_rhs: Option<(ExprId, Ty<'db>)>,
+        (opname, trait_did): (Symbol, Option<TraitId>),
+    ) -> Result<MethodCallee<'db>, Vec<NextSolverError<'db>>> {
+        let Some(trait_did) = trait_did else {
+            // Bail if the operator trait is not defined.
+            return Err(vec![]);
+        };
+
+        debug!(
+            "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
+            lhs_ty, opname, trait_did
+        );
+
+        let opt_rhs_ty = opt_rhs.map(|it| it.1);
+        let cause = ObligationCause::new();
+
+        // We don't consider any other candidates if this lookup fails
+        // so we can freely treat opaque types as inference variables here
+        // to allow more code to compile.
+        let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
+        let method = self.table.lookup_method_for_operator(
+            cause.clone(),
+            opname,
+            trait_did,
+            lhs_ty,
+            opt_rhs_ty,
+            treat_opaques,
+        );
+        match method {
+            Some(ok) => {
+                let method = self.table.register_infer_ok(ok);
+                self.table.select_obligations_where_possible();
+                Ok(method)
+            }
+            None => {
+                // Guide inference for the RHS expression if it's provided --
+                // this will allow us to better error reporting, at the expense
+                // of making some error messages a bit more specific.
+                if let Some((rhs_expr, rhs_ty)) = opt_rhs
+                    && rhs_ty.is_ty_var()
+                {
+                    self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty), ExprIsRead::No);
+                }
+
+                // Construct an obligation `self_ty : Trait<input_tys>`
+                let args = GenericArgs::for_item(
+                    self.interner(),
+                    trait_did.into(),
+                    |param_idx, param_id, _| match param_id {
+                        GenericParamId::LifetimeParamId(_) | GenericParamId::ConstParamId(_) => {
+                            unreachable!("did not expect operand trait to have lifetime/const args")
+                        }
+                        GenericParamId::TypeParamId(_) => {
+                            if param_idx == 0 {
+                                lhs_ty.into()
+                            } else {
+                                opt_rhs_ty.expect("expected RHS for binop").into()
+                            }
+                        }
+                    },
+                );
+                let obligation = Obligation::new(
+                    self.interner(),
+                    cause,
+                    self.table.trait_env.env,
+                    TraitRef::new_from_args(self.interner(), trait_did.into(), args),
+                );
+                let mut ocx = ObligationCtxt::new(self.infcx());
+                ocx.register_obligation(obligation);
+                Err(ocx.evaluate_obligations_error_on_ambiguity())
+            }
+        }
+    }
+
+    fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Symbol, Option<TraitId>) {
+        let (method_name, trait_lang_item) =
+            crate::lang_items::lang_items_for_bin_op(op).expect("invalid operator provided");
+        (method_name, trait_lang_item.resolve_trait(self.db, self.krate()))
+    }
+
+    fn lang_item_for_unop(&self, op: UnaryOp) -> (Symbol, Option<TraitId>) {
+        let (method_name, trait_lang_item) = match op {
+            UnaryOp::Not => (sym::not, LangItem::Not),
+            UnaryOp::Neg => (sym::neg, LangItem::Neg),
+            UnaryOp::Deref => panic!("Deref is not overloadable"),
+        };
+        (method_name, trait_lang_item.resolve_trait(self.db, self.krate()))
+    }
+}
+
+// Binary operator categories. These categories summarize the behavior
+// with respect to the builtin operations supported.
+#[derive(Clone, Copy)]
+enum BinOpCategory {
+    /// &&, || -- cannot be overridden
+    Shortcircuit,
+
+    /// <<, >> -- when shifting a single integer, rhs can be any
+    /// integer type. For simd, types must match.
+    Shift,
+
+    /// +, -, etc -- takes equal types, produces same type as input,
+    /// applicable to ints/floats/simd
+    Math,
+
+    /// &, |, ^ -- takes equal types, produces same type as input,
+    /// applicable to ints/floats/simd/bool
+    Bitwise,
+
+    /// ==, !=, etc -- takes equal types, produces bools, except for simd,
+    /// which produce the input type
+    Comparison,
+}
+
+impl From<BinaryOp> for BinOpCategory {
+    fn from(op: BinaryOp) -> BinOpCategory {
+        match op {
+            BinaryOp::LogicOp(_) => BinOpCategory::Shortcircuit,
+            BinaryOp::ArithOp(op) | BinaryOp::Assignment { op: Some(op) } => op.into(),
+            BinaryOp::CmpOp(_) => BinOpCategory::Comparison,
+            BinaryOp::Assignment { op: None } => unreachable!(
+                "assignment is lowered into `Expr::Assignment`, not into `Expr::BinaryOp`"
+            ),
+        }
+    }
+}
+
+impl From<ArithOp> for BinOpCategory {
+    fn from(op: ArithOp) -> BinOpCategory {
+        use ArithOp::*;
+        match op {
+            Shl | Shr => BinOpCategory::Shift,
+            Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
+            BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
+        }
+    }
+}
+
+/// Returns `true` if the binary operator takes its arguments by value.
+fn is_op_by_value(op: BinaryOp) -> bool {
+    !matches!(op, BinaryOp::CmpOp(_))
+}
+
+/// Dereferences a single level of immutable referencing.
+fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
+    match ty.kind() {
+        TyKind::Ref(_, ty, Mutability::Not) => ty,
+        _ => ty,
+    }
+}
+
+/// Returns `true` if this is a built-in arithmetic operation (e.g.,
+/// u32 + u32, i16x4 == i16x4) and false if these types would have to be
+/// overloaded to be legal. There are two reasons that we distinguish
+/// builtin operations from overloaded ones (vs trying to drive
+/// everything uniformly through the trait system and intrinsics or
+/// something like that):
+///
+/// 1. Builtin operations can trivially be evaluated in constants.
+/// 2. For comparison operators applied to SIMD types the result is
+///    not of type `bool`. For example, `i16x4 == i16x4` yields a
+///    type like `i16x4`. This means that the overloaded trait
+///    `PartialEq` is not applicable.
+///
+/// Reason #2 is the killer. I tried for a while to always use
+/// overloaded logic and just check the types in constants/codegen after
+/// the fact, and it worked fine, except for SIMD types. -nmatsakis
+fn is_builtin_binop<'db>(lhs: Ty<'db>, rhs: Ty<'db>, category: BinOpCategory) -> bool {
+    // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
+    // (See https://github.com/rust-lang/rust/issues/57447.)
+    let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
+
+    match category {
+        BinOpCategory::Shortcircuit => true,
+        BinOpCategory::Shift => lhs.is_integral() && rhs.is_integral(),
+        BinOpCategory::Math => {
+            lhs.is_integral() && rhs.is_integral()
+                || lhs.is_floating_point() && rhs.is_floating_point()
+        }
+        BinOpCategory::Bitwise => {
+            lhs.is_integral() && rhs.is_integral()
+                || lhs.is_floating_point() && rhs.is_floating_point()
+                || lhs.is_bool() && rhs.is_bool()
+        }
+        BinOpCategory::Comparison => lhs.is_scalar() && rhs.is_scalar(),
+    }
+}
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 8019844..257224b 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -16,8 +16,7 @@
     DeclContext, DeclOrigin, InferenceDiagnostic,
     consteval::{self, try_const_usize, usize_const},
     infer::{
-        AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch,
-        coerce::CoerceNever, expr::ExprIsRead,
+        AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead,
     },
     lower::lower_mutability,
     next_solver::{GenericArgs, Ty, TyKind},
@@ -306,7 +305,7 @@
                     expected,
                     ty_inserted_vars,
                     AllowTwoPhase::No,
-                    CoerceNever::Yes,
+                    ExprIsRead::No,
                 ) {
                     Ok(coerced_ty) => {
                         self.write_pat_ty(pat, coerced_ty);
@@ -374,16 +373,17 @@
             Pat::Expr(expr) => {
                 let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
                 // LHS of assignment doesn't constitute reads.
+                let expr_is_read = ExprIsRead::No;
                 let result =
-                    self.infer_expr_coerce(*expr, &Expectation::has_type(expected), ExprIsRead::No);
+                    self.infer_expr_coerce(*expr, &Expectation::has_type(expected), expr_is_read);
                 // We are returning early to avoid the unifiability check below.
                 let lhs_ty = self.insert_type_vars_shallow(result);
                 let ty = match self.coerce(
-                    pat.into(),
+                    (*expr).into(),
                     expected,
                     lhs_ty,
                     AllowTwoPhase::No,
-                    CoerceNever::Yes,
+                    expr_is_read,
                 ) {
                     Ok(ty) => ty,
                     Err(_) => {
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 9ade842..6e3d158 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -13,11 +13,12 @@
     InferenceDiagnostic, ValueTyDefId,
     generics::generics,
     infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
-    lower::LifetimeElisionKind,
-    method_resolution::{self, VisibleFromModule},
+    lower::{GenericPredicates, LifetimeElisionKind},
+    method_resolution::{self, CandidateId, MethodError},
     next_solver::{
         GenericArg, GenericArgs, TraitRef, Ty,
         infer::traits::{Obligation, ObligationCause},
+        util::clauses_as_obligations,
     },
 };
 
@@ -31,7 +32,7 @@
             }
             ValuePathResolution::NonGeneric(ty) => return Some(ty),
         };
-        let args = self.process_remote_user_written_ty(substs);
+        let args = self.insert_type_vars(substs);
 
         self.add_required_obligations_for_value_path(generic_def, args);
 
@@ -221,14 +222,14 @@
         def: GenericDefId,
         subst: GenericArgs<'db>,
     ) {
-        let predicates = self.db.generic_predicates(def);
         let interner = self.interner();
+        let predicates = GenericPredicates::query_all(self.db, def);
         let param_env = self.table.trait_env.env;
-        if let Some(predicates) = predicates.instantiate(self.interner(), subst) {
-            self.table.register_predicates(predicates.map(|predicate| {
-                Obligation::new(interner, ObligationCause::new(), param_env, predicate)
-            }));
-        }
+        self.table.register_predicates(clauses_as_obligations(
+            predicates.iter_instantiated_copied(interner, subst.as_slice()),
+            ObligationCause::new(),
+            param_env,
+        ));
 
         // We need to add `Self: Trait` obligation when `def` is a trait assoc item.
         let container = match def {
@@ -265,7 +266,7 @@
                 match item {
                     AssocItemId::FunctionId(func) => {
                         if segment.name == &self.db.function_signature(func).name {
-                            Some(AssocItemId::FunctionId(func))
+                            Some(CandidateId::FunctionId(func))
                         } else {
                             None
                         }
@@ -273,7 +274,7 @@
 
                     AssocItemId::ConstId(konst) => {
                         if self.db.const_signature(konst).name.as_ref() == Some(segment.name) {
-                            Some(AssocItemId::ConstId(konst))
+                            Some(CandidateId::ConstId(konst))
                         } else {
                             None
                         }
@@ -282,9 +283,8 @@
                 }
             })?;
         let def = match item {
-            AssocItemId::FunctionId(f) => ValueNs::FunctionId(f),
-            AssocItemId::ConstId(c) => ValueNs::ConstId(c),
-            AssocItemId::TypeAliasId(_) => unreachable!(),
+            CandidateId::FunctionId(f) => ValueNs::FunctionId(f),
+            CandidateId::ConstId(c) => ValueNs::ConstId(c),
         };
 
         self.write_assoc_resolution(id, item, trait_ref.args);
@@ -305,39 +305,23 @@
             return Some(result);
         }
 
-        let canonical_ty = self.canonicalize(ty);
-
-        let mut not_visible = None;
-        let res = method_resolution::iterate_method_candidates(
-            &canonical_ty,
-            &mut self.table,
-            Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
-                .as_ref()
-                .left_or_else(|&it| it),
-            VisibleFromModule::Filter(self.resolver.module()),
-            Some(name),
-            method_resolution::LookupMode::Path,
-            |_ty, item, visible| {
-                if visible {
-                    Some((item, true))
-                } else {
-                    if not_visible.is_none() {
-                        not_visible = Some((item, false));
-                    }
-                    None
+        let res = self.with_method_resolution(|ctx| {
+            ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty)
+        });
+        let (item, visible) = match res {
+            Ok(res) => (res.item, true),
+            Err(error) => match error {
+                MethodError::PrivateMatch(candidate_id) => (candidate_id.item, false),
+                _ => {
+                    self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id });
+                    return None;
                 }
             },
-        );
-        let res = res.or(not_visible);
-        if res.is_none() {
-            self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id });
-        }
-        let (item, visible) = res?;
+        };
 
         let (def, container) = match item {
-            AssocItemId::FunctionId(f) => (ValueNs::FunctionId(f), f.lookup(self.db).container),
-            AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container),
-            AssocItemId::TypeAliasId(_) => unreachable!(),
+            CandidateId::FunctionId(f) => (ValueNs::FunctionId(f), f.lookup(self.db).container),
+            CandidateId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container),
         };
         let substs = match container {
             ItemContainerId::ImplId(impl_id) => {
@@ -372,6 +356,10 @@
 
         self.write_assoc_resolution(id, item, substs);
         if !visible {
+            let item = match item {
+                CandidateId::FunctionId(it) => it.into(),
+                CandidateId::ConstId(it) => it.into(),
+            };
             self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item });
         }
         Some((def, substs))
diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs
new file mode 100644
index 0000000..50018bb
--- /dev/null
+++ b/crates/hir-ty/src/infer/place_op.rs
@@ -0,0 +1,329 @@
+//! Inference of *place operators*: deref and indexing (operators that create places, as opposed to values).
+
+use base_db::Crate;
+use hir_def::{hir::ExprId, lang_item::LangItem};
+use intern::sym;
+use rustc_ast_ir::Mutability;
+use rustc_type_ir::inherent::{IntoKind, Ty as _};
+use tracing::debug;
+
+use crate::{
+    Adjust, Adjustment, AutoBorrow, PointerCast,
+    autoderef::InferenceContextAutoderef,
+    infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, unify::InferenceTable},
+    method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
+    next_solver::{
+        ClauseKind, Ty, TyKind,
+        infer::{
+            InferOk,
+            traits::{Obligation, ObligationCause},
+        },
+    },
+};
+
+#[derive(Debug, Copy, Clone)]
+pub(super) enum PlaceOp {
+    Deref,
+    Index,
+}
+
+impl<'a, 'db> InferenceContext<'a, 'db> {
+    pub(super) fn try_overloaded_deref(
+        &self,
+        base_ty: Ty<'db>,
+    ) -> Option<InferOk<'db, MethodCallee<'db>>> {
+        self.try_overloaded_place_op(base_ty, None, PlaceOp::Deref)
+    }
+
+    /// For the overloaded place expressions (`*x`, `x[3]`), the trait
+    /// returns a type of `&T`, but the actual type we assign to the
+    /// *expression* is `T`. So this function just peels off the return
+    /// type by one layer to yield `T`.
+    fn make_overloaded_place_return_type(&self, method: MethodCallee<'db>) -> Ty<'db> {
+        // extract method return type, which will be &T;
+        let ret_ty = method.sig.output();
+
+        // method returns &T, but the type as visible to user is T, so deref
+        ret_ty.builtin_deref(true).unwrap()
+    }
+
+    /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
+    pub(super) fn lookup_derefing(
+        &mut self,
+        expr: ExprId,
+        oprnd_expr: ExprId,
+        oprnd_ty: Ty<'db>,
+    ) -> Option<Ty<'db>> {
+        if let Some(ty) = oprnd_ty.builtin_deref(true) {
+            return Some(ty);
+        }
+
+        let ok = self.try_overloaded_deref(oprnd_ty)?;
+        let method = self.table.register_infer_ok(ok);
+        if let TyKind::Ref(_, _, Mutability::Not) = method.sig.inputs_and_output.inputs()[0].kind()
+        {
+            self.write_expr_adj(
+                oprnd_expr,
+                Box::new([Adjustment {
+                    kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
+                    target: method.sig.inputs_and_output.inputs()[0],
+                }]),
+            );
+        } else {
+            panic!("input to deref is not a ref?");
+        }
+        let ty = self.make_overloaded_place_return_type(method);
+        self.write_method_resolution(expr, method.def_id, method.args);
+        Some(ty)
+    }
+
+    /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
+    pub(super) fn lookup_indexing(
+        &mut self,
+        expr: ExprId,
+        base_expr: ExprId,
+        base_ty: Ty<'db>,
+        idx_ty: Ty<'db>,
+    ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> {
+        // FIXME(#18741) -- this is almost but not quite the same as the
+        // autoderef that normal method probing does. They could likely be
+        // consolidated.
+
+        let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty);
+        let mut result = None;
+        while result.is_none() && autoderef.next().is_some() {
+            result = Self::try_index_step(expr, base_expr, &mut autoderef, idx_ty);
+        }
+        result
+    }
+
+    /// To type-check `base_expr[index_expr]`, we progressively autoderef
+    /// (and otherwise adjust) `base_expr`, looking for a type which either
+    /// supports builtin indexing or overloaded indexing.
+    /// This loop implements one step in that search; the autoderef loop
+    /// is implemented by `lookup_indexing`.
+    fn try_index_step(
+        expr: ExprId,
+        base_expr: ExprId,
+        autoderef: &mut InferenceContextAutoderef<'_, 'a, 'db>,
+        index_ty: Ty<'db>,
+    ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> {
+        let ty = autoderef.final_ty();
+        let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty);
+        debug!(
+            "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
+             index_ty={:?})",
+            expr, base_expr, adjusted_ty, index_ty
+        );
+
+        for unsize in [false, true] {
+            let mut self_ty = adjusted_ty;
+            if unsize {
+                // We only unsize arrays here.
+                if let TyKind::Array(element_ty, ct) = adjusted_ty.kind() {
+                    let ctx = autoderef.ctx();
+                    ctx.table.register_predicate(Obligation::new(
+                        ctx.interner(),
+                        ObligationCause::new(),
+                        ctx.table.trait_env.env,
+                        ClauseKind::ConstArgHasType(ct, ctx.types.usize),
+                    ));
+                    self_ty = Ty::new_slice(ctx.interner(), element_ty);
+                } else {
+                    continue;
+                }
+            }
+
+            // If some lookup succeeds, write callee into table and extract index/element
+            // type from the method signature.
+            // If some lookup succeeded, install method in table
+            let input_ty = autoderef.ctx().table.next_ty_var();
+            let method =
+                autoderef.ctx().try_overloaded_place_op(self_ty, Some(input_ty), PlaceOp::Index);
+
+            if let Some(result) = method {
+                debug!("try_index_step: success, using overloaded indexing");
+                let method = autoderef.ctx().table.register_infer_ok(result);
+
+                let infer_ok = autoderef.adjust_steps_as_infer_ok();
+                let mut adjustments = autoderef.ctx().table.register_infer_ok(infer_ok);
+                if let TyKind::Ref(region, _, Mutability::Not) =
+                    method.sig.inputs_and_output.inputs()[0].kind()
+                {
+                    adjustments.push(Adjustment {
+                        kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)),
+                        target: Ty::new_imm_ref(autoderef.ctx().interner(), region, adjusted_ty),
+                    });
+                } else {
+                    panic!("input to index is not a ref?");
+                }
+                if unsize {
+                    adjustments.push(Adjustment {
+                        kind: Adjust::Pointer(PointerCast::Unsize),
+                        target: method.sig.inputs_and_output.inputs()[0],
+                    });
+                }
+                autoderef.ctx().write_expr_adj(base_expr, adjustments.into_boxed_slice());
+
+                autoderef.ctx().write_method_resolution(expr, method.def_id, method.args);
+
+                return Some((input_ty, autoderef.ctx().make_overloaded_place_return_type(method)));
+            }
+        }
+
+        None
+    }
+
+    /// Try to resolve an overloaded place op. We only deal with the immutable
+    /// variant here (Deref/Index). In some contexts we would need the mutable
+    /// variant (DerefMut/IndexMut); those would be later converted by
+    /// `convert_place_derefs_to_mutable`.
+    pub(super) fn try_overloaded_place_op(
+        &self,
+        base_ty: Ty<'db>,
+        opt_rhs_ty: Option<Ty<'db>>,
+        op: PlaceOp,
+    ) -> Option<InferOk<'db, MethodCallee<'db>>> {
+        debug!("try_overloaded_place_op({:?},{:?})", base_ty, op);
+
+        let (Some(imm_tr), imm_op) = (match op {
+            PlaceOp::Deref => (LangItem::Deref.resolve_trait(self.db, self.krate()), sym::deref),
+            PlaceOp::Index => (LangItem::Index.resolve_trait(self.db, self.krate()), sym::index),
+        }) else {
+            // Bail if `Deref` or `Index` isn't defined.
+            return None;
+        };
+
+        // FIXME(trait-system-refactor-initiative#231): we may want to treat
+        // opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`.
+        let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
+        self.table.lookup_method_for_operator(
+            ObligationCause::new(),
+            imm_op,
+            imm_tr,
+            base_ty,
+            opt_rhs_ty,
+            treat_opaques,
+        )
+    }
+
+    pub(super) fn try_mutable_overloaded_place_op(
+        table: &InferenceTable<'db>,
+        krate: Crate,
+        base_ty: Ty<'db>,
+        opt_rhs_ty: Option<Ty<'db>>,
+        op: PlaceOp,
+    ) -> Option<InferOk<'db, MethodCallee<'db>>> {
+        debug!("try_mutable_overloaded_place_op({:?},{:?})", base_ty, op);
+
+        let (Some(mut_tr), mut_op) = (match op {
+            PlaceOp::Deref => (LangItem::DerefMut.resolve_trait(table.db, krate), sym::deref_mut),
+            PlaceOp::Index => (LangItem::IndexMut.resolve_trait(table.db, krate), sym::index_mut),
+        }) else {
+            // Bail if `DerefMut` or `IndexMut` isn't defined.
+            return None;
+        };
+
+        // We have to replace the operator with the mutable variant for the
+        // program to compile, so we don't really have a choice here and want
+        // to just try using `DerefMut` even if its not in the item bounds
+        // of the opaque.
+        let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
+        table.lookup_method_for_operator(
+            ObligationCause::new(),
+            mut_op,
+            mut_tr,
+            base_ty,
+            opt_rhs_ty,
+            treat_opaques,
+        )
+    }
+
+    pub(super) fn convert_place_op_to_mutable(
+        &mut self,
+        op: PlaceOp,
+        expr: ExprId,
+        base_expr: ExprId,
+        index_expr: Option<ExprId>,
+    ) {
+        debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
+        if !self.result.method_resolutions.contains_key(&expr) {
+            debug!("convert_place_op_to_mutable - builtin, nothing to do");
+            return;
+        }
+
+        // Need to deref because overloaded place ops take self by-reference.
+        let base_ty = self
+            .expr_ty_after_adjustments(base_expr)
+            .builtin_deref(false)
+            .expect("place op takes something that is not a ref");
+
+        let arg_ty = match op {
+            PlaceOp::Deref => None,
+            PlaceOp::Index => {
+                // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
+                // in try_index_step. This is the arg at index 1.
+                //
+                // FIXME: rustc does not use the type of `index_expr` with the following explanation.
+                //
+                // Note: we should *not* use `expr_ty` of index_expr here because autoderef
+                // during coercions can cause type of index_expr to differ from `T` (#72002).
+                // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
+                // during coercions can also cause type of index_expr to differ from `T`,
+                // which can potentially cause regionck failure (#74933).
+                Some(self.expr_ty_after_adjustments(
+                    index_expr.expect("`PlaceOp::Index` should have `index_expr`"),
+                ))
+            }
+        };
+        let method =
+            Self::try_mutable_overloaded_place_op(&self.table, self.krate(), base_ty, arg_ty, op);
+        let method = match method {
+            Some(ok) => self.table.register_infer_ok(ok),
+            // Couldn't find the mutable variant of the place op, keep the
+            // current, immutable version.
+            None => return,
+        };
+        debug!("convert_place_op_to_mutable: method={:?}", method);
+        self.result.method_resolutions.insert(expr, (method.def_id, method.args));
+
+        let TyKind::Ref(region, _, Mutability::Mut) =
+            method.sig.inputs_and_output.inputs()[0].kind()
+        else {
+            panic!("input to mutable place op is not a mut ref?");
+        };
+
+        // Convert the autoref in the base expr to mutable with the correct
+        // region and mutability.
+        let base_expr_ty = self.expr_ty(base_expr);
+        let interner = self.interner();
+        if let Some(adjustments) = self.result.expr_adjustments.get_mut(&base_expr) {
+            let mut source = base_expr_ty;
+            for adjustment in &mut adjustments[..] {
+                if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
+                    debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
+                    let mutbl = AutoBorrowMutability::Mut {
+                        // Deref/indexing can be desugared to a method call,
+                        // so maybe we could use two-phase here.
+                        // See the documentation of AllowTwoPhase for why that's
+                        // not the case today.
+                        allow_two_phase_borrow: AllowTwoPhase::No,
+                    };
+                    adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(mutbl));
+                    adjustment.target = Ty::new_ref(interner, region, source, mutbl.into());
+                }
+                source = adjustment.target;
+            }
+
+            // If we have an autoref followed by unsizing at the end, fix the unsize target.
+            if let [
+                ..,
+                Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
+                Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target },
+            ] = adjustments[..]
+            {
+                *target = method.sig.inputs_and_output.inputs()[0];
+            }
+        }
+    }
+}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 59e8d84..fb195d4 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -7,9 +7,8 @@
 use intern::sym;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    DebruijnIndex, InferConst, InferTy, RegionVid, TyVid, TypeFoldable, TypeFolder,
-    TypeSuperFoldable, TypeVisitableExt, UpcastFrom,
-    inherent::{Const as _, IntoKind, Ty as _},
+    TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom,
+    inherent::{Const as _, GenericArg as _, IntoKind, SliceLike, Ty as _},
     solve::{Certainty, GoalSource},
 };
 use smallvec::SmallVec;
@@ -18,15 +17,14 @@
 use crate::{
     TraitEnvironment,
     db::HirDatabase,
-    infer::InferenceContext,
     next_solver::{
-        self, AliasTy, Binder, Canonical, ClauseKind, Const, ConstKind, DbInterner,
-        ErrorGuaranteed, GenericArg, GenericArgs, Predicate, PredicateKind, Region, RegionKind,
-        SolverDefId, TraitRef, Ty, TyKind, TypingMode,
+        AliasTy, Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg,
+        GenericArgs, Goal, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty,
+        TyKind, TypingMode,
         fulfill::{FulfillmentCtxt, NextSolverError},
         infer::{
             DbInternerInferExt, InferCtxt, InferOk, InferResult,
-            at::ToTrace,
+            at::{At, ToTrace},
             snapshot::CombinedSnapshot,
             traits::{Obligation, ObligationCause, PredicateObligation},
         },
@@ -38,15 +36,6 @@
     },
 };
 
-impl<'db> InferenceContext<'_, 'db> {
-    pub(super) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T>
-    where
-        T: rustc_type_ir::TypeFoldable<DbInterner<'db>>,
-    {
-        self.table.canonicalize(t)
-    }
-}
-
 struct NestedObligationsForSelfTy<'a, 'db> {
     ctx: &'a InferenceTable<'db>,
     self_ty: TyVid,
@@ -292,10 +281,7 @@
         T: TypeFoldable<DbInterner<'db>> + Clone,
     {
         let ty = self.resolve_vars_with_obligations(ty);
-        self.infer_ctxt
-            .at(&ObligationCause::new(), self.trait_env.env)
-            .deeply_normalize(ty.clone())
-            .unwrap_or(ty)
+        self.at(&ObligationCause::new()).deeply_normalize(ty.clone()).unwrap_or(ty)
     }
 
     /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow
@@ -316,19 +302,19 @@
             .unwrap_or(alias)
     }
 
-    pub(crate) fn next_ty_var(&mut self) -> Ty<'db> {
+    pub(crate) fn next_ty_var(&self) -> Ty<'db> {
         self.infer_ctxt.next_ty_var()
     }
 
-    pub(crate) fn next_const_var(&mut self) -> Const<'db> {
+    pub(crate) fn next_const_var(&self) -> Const<'db> {
         self.infer_ctxt.next_const_var()
     }
 
-    pub(crate) fn next_int_var(&mut self) -> Ty<'db> {
+    pub(crate) fn next_int_var(&self) -> Ty<'db> {
         self.infer_ctxt.next_int_var()
     }
 
-    pub(crate) fn next_float_var(&mut self) -> Ty<'db> {
+    pub(crate) fn next_float_var(&self) -> Ty<'db> {
         self.infer_ctxt.next_float_var()
     }
 
@@ -338,101 +324,12 @@
         var
     }
 
-    pub(crate) fn next_region_var(&mut self) -> Region<'db> {
+    pub(crate) fn next_region_var(&self) -> Region<'db> {
         self.infer_ctxt.next_region_var()
     }
 
-    pub(crate) fn next_var_for_param(&mut self, id: GenericParamId) -> GenericArg<'db> {
-        match id {
-            GenericParamId::TypeParamId(_) => self.next_ty_var().into(),
-            GenericParamId::ConstParamId(_) => self.next_const_var().into(),
-            GenericParamId::LifetimeParamId(_) => self.next_region_var().into(),
-        }
-    }
-
-    pub(crate) fn resolve_with_fallback<T>(
-        &mut self,
-        t: T,
-        fallback_ty: &mut dyn FnMut(DebruijnIndex, InferTy) -> Ty<'db>,
-        fallback_const: &mut dyn FnMut(DebruijnIndex, InferConst) -> Const<'db>,
-        fallback_region: &mut dyn FnMut(DebruijnIndex, RegionVid) -> Region<'db>,
-    ) -> T
-    where
-        T: TypeFoldable<DbInterner<'db>>,
-    {
-        struct Resolver<'a, 'db> {
-            table: &'a mut InferenceTable<'db>,
-            binder: DebruijnIndex,
-            fallback_ty: &'a mut dyn FnMut(DebruijnIndex, InferTy) -> Ty<'db>,
-            fallback_const: &'a mut dyn FnMut(DebruijnIndex, InferConst) -> Const<'db>,
-            fallback_region: &'a mut dyn FnMut(DebruijnIndex, RegionVid) -> Region<'db>,
-        }
-
-        impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'_, 'db> {
-            fn cx(&self) -> DbInterner<'db> {
-                self.table.interner()
-            }
-
-            fn fold_binder<T>(&mut self, t: Binder<'db, T>) -> Binder<'db, T>
-            where
-                T: TypeFoldable<DbInterner<'db>>,
-            {
-                self.binder.shift_in(1);
-                let result = t.super_fold_with(self);
-                self.binder.shift_out(1);
-                result
-            }
-
-            fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
-                if !t.has_infer() {
-                    return t;
-                }
-
-                if let TyKind::Infer(infer) = t.kind() {
-                    (self.fallback_ty)(self.binder, infer)
-                } else {
-                    t.super_fold_with(self)
-                }
-            }
-
-            fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
-                if !c.has_infer() {
-                    return c;
-                }
-
-                if let ConstKind::Infer(infer) = c.kind() {
-                    (self.fallback_const)(self.binder, infer)
-                } else {
-                    c.super_fold_with(self)
-                }
-            }
-
-            fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
-                if let RegionKind::ReVar(infer) = r.kind() {
-                    (self.fallback_region)(self.binder, infer)
-                } else {
-                    r
-                }
-            }
-        }
-
-        t.fold_with(&mut Resolver {
-            table: self,
-            binder: DebruijnIndex::ZERO,
-            fallback_ty,
-            fallback_const,
-            fallback_region,
-        })
-    }
-
-    pub(crate) fn instantiate_canonical<T>(
-        &mut self,
-        canonical: rustc_type_ir::Canonical<DbInterner<'db>, T>,
-    ) -> T
-    where
-        T: rustc_type_ir::TypeFoldable<DbInterner<'db>>,
-    {
-        self.infer_ctxt.instantiate_canonical(&canonical).0
+    pub(crate) fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> {
+        self.infer_ctxt.next_var_for_param(id)
     }
 
     pub(crate) fn resolve_completely<T>(&mut self, value: T) -> T
@@ -456,7 +353,11 @@
     /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
     /// caller needs to deal with them.
     pub(crate) fn try_unify<T: ToTrace<'db>>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> {
-        self.infer_ctxt.at(&ObligationCause::new(), self.trait_env.env).eq(t1, t2)
+        self.at(&ObligationCause::new()).eq(t1, t2)
+    }
+
+    pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> {
+        self.infer_ctxt.at(cause, self.trait_env.env)
     }
 
     pub(crate) fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> {
@@ -486,15 +387,6 @@
         self.infer_ctxt.fresh_args_for_item(def)
     }
 
-    /// Like `fresh_args_for_item()`, but first uses the args from `first`.
-    pub(crate) fn fill_rest_fresh_args(
-        &self,
-        def_id: SolverDefId,
-        first: impl IntoIterator<Item = GenericArg<'db>>,
-    ) -> GenericArgs<'db> {
-        self.infer_ctxt.fill_rest_fresh_args(def_id, first)
-    }
-
     /// Try to resolve `ty` to a structural type, normalizing aliases.
     ///
     /// In case there is still ambiguity, the returned type may be an inference
@@ -535,17 +427,6 @@
         self.fulfillment_cx = snapshot.obligations;
     }
 
-    #[tracing::instrument(skip_all)]
-    pub(crate) fn run_in_snapshot<T>(
-        &mut self,
-        f: impl FnOnce(&mut InferenceTable<'db>) -> T,
-    ) -> T {
-        let snapshot = self.snapshot();
-        let result = f(self);
-        self.rollback_to(snapshot);
-        result
-    }
-
     pub(crate) fn commit_if_ok<T, E>(
         &mut self,
         f: impl FnOnce(&mut InferenceTable<'db>) -> Result<T, E>,
@@ -566,22 +447,19 @@
     /// choice (during e.g. method resolution or deref).
     #[tracing::instrument(level = "debug", skip(self))]
     pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult {
-        let goal = next_solver::Goal { param_env: self.trait_env.env, predicate };
+        let goal = Goal { param_env: self.trait_env.env, predicate };
         let canonicalized = self.canonicalize(goal);
 
         next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized)
     }
 
     pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) {
-        let goal = next_solver::Goal { param_env: self.trait_env.env, predicate };
+        let goal = Goal { param_env: self.trait_env.env, predicate };
         self.register_obligation_in_env(goal)
     }
 
     #[tracing::instrument(level = "debug", skip(self))]
-    fn register_obligation_in_env(
-        &mut self,
-        goal: next_solver::Goal<'db, next_solver::Predicate<'db>>,
-    ) {
+    fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) {
         let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal);
         tracing::debug!(?result);
         match result {
@@ -619,7 +497,7 @@
         self.fulfillment_cx.register_predicate_obligation(&self.infer_ctxt, obligation);
     }
 
-    pub(super) fn register_predicates<I>(&mut self, obligations: I)
+    pub(crate) fn register_predicates<I>(&mut self, obligations: I)
     where
         I: IntoIterator<Item = PredicateObligation<'db>>,
     {
@@ -628,6 +506,23 @@
         });
     }
 
+    /// checking later, during regionck, that `arg` is well-formed.
+    pub(crate) fn register_wf_obligation(&mut self, term: Term<'db>, cause: ObligationCause) {
+        self.register_predicate(Obligation::new(
+            self.interner(),
+            cause,
+            self.trait_env.env,
+            ClauseKind::WellFormed(term),
+        ));
+    }
+
+    /// Registers obligations that all `args` are well-formed.
+    pub(crate) fn add_wf_bounds(&mut self, args: GenericArgs<'db>) {
+        for term in args.iter().filter_map(|it| it.as_term()) {
+            self.register_wf_obligation(term, ObligationCause::new());
+        }
+    }
+
     pub(crate) fn callable_sig(
         &mut self,
         ty: Ty<'db>,
@@ -714,26 +609,20 @@
     }
 
     /// Whenever you lower a user-written type, you should call this.
-    pub(crate) fn process_user_written_ty<T>(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable<DbInterner<'db>>,
-    {
+    pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
         self.process_remote_user_written_ty(ty)
         // FIXME: Register a well-formed obligation.
     }
 
     /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
     /// while `process_user_written_ty()` should (but doesn't currently).
-    pub(crate) fn process_remote_user_written_ty<T>(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable<DbInterner<'db>>,
-    {
+    pub(crate) fn process_remote_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
         let ty = self.insert_type_vars(ty);
         // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495:
         // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs
         // to normalize before inspecting the `TyKind`.
         // FIXME(next-solver): We should not deeply normalize here, only shallowly.
-        self.normalize_associated_types_in(ty)
+        self.try_structurally_resolve_type(ty)
     }
 
     /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
@@ -852,7 +741,7 @@
         {
             let value = if self.should_normalize {
                 let cause = ObligationCause::new();
-                let at = self.ctx.infer_ctxt.at(&cause, self.ctx.trait_env.env);
+                let at = self.ctx.at(&cause);
                 let universes = vec![None; outer_exclusive_binder(value).as_usize()];
                 match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
                     at, value, universes,
diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs
index 3ef7f50..d0d0aa7 100644
--- a/crates/hir-ty/src/lang_items.rs
+++ b/crates/hir-ty/src/lang_items.rs
@@ -1,8 +1,7 @@
 //! Functions to detect special lang items
 
 use hir_def::{AdtId, lang_item::LangItem, signatures::StructFlags};
-use hir_expand::name::Name;
-use intern::sym;
+use intern::{Symbol, sym};
 
 use crate::db::HirDatabase;
 
@@ -11,48 +10,48 @@
     db.struct_signature(id).flags.contains(StructFlags::IS_BOX)
 }
 
-pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
+pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Symbol, LangItem)> {
     use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
     Some(match op {
         BinaryOp::LogicOp(_) => return None,
         BinaryOp::ArithOp(aop) => match aop {
-            ArithOp::Add => (Name::new_symbol_root(sym::add), LangItem::Add),
-            ArithOp::Mul => (Name::new_symbol_root(sym::mul), LangItem::Mul),
-            ArithOp::Sub => (Name::new_symbol_root(sym::sub), LangItem::Sub),
-            ArithOp::Div => (Name::new_symbol_root(sym::div), LangItem::Div),
-            ArithOp::Rem => (Name::new_symbol_root(sym::rem), LangItem::Rem),
-            ArithOp::Shl => (Name::new_symbol_root(sym::shl), LangItem::Shl),
-            ArithOp::Shr => (Name::new_symbol_root(sym::shr), LangItem::Shr),
-            ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor), LangItem::BitXor),
-            ArithOp::BitOr => (Name::new_symbol_root(sym::bitor), LangItem::BitOr),
-            ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand), LangItem::BitAnd),
+            ArithOp::Add => (sym::add, LangItem::Add),
+            ArithOp::Mul => (sym::mul, LangItem::Mul),
+            ArithOp::Sub => (sym::sub, LangItem::Sub),
+            ArithOp::Div => (sym::div, LangItem::Div),
+            ArithOp::Rem => (sym::rem, LangItem::Rem),
+            ArithOp::Shl => (sym::shl, LangItem::Shl),
+            ArithOp::Shr => (sym::shr, LangItem::Shr),
+            ArithOp::BitXor => (sym::bitxor, LangItem::BitXor),
+            ArithOp::BitOr => (sym::bitor, LangItem::BitOr),
+            ArithOp::BitAnd => (sym::bitand, LangItem::BitAnd),
         },
         BinaryOp::Assignment { op: Some(aop) } => match aop {
-            ArithOp::Add => (Name::new_symbol_root(sym::add_assign), LangItem::AddAssign),
-            ArithOp::Mul => (Name::new_symbol_root(sym::mul_assign), LangItem::MulAssign),
-            ArithOp::Sub => (Name::new_symbol_root(sym::sub_assign), LangItem::SubAssign),
-            ArithOp::Div => (Name::new_symbol_root(sym::div_assign), LangItem::DivAssign),
-            ArithOp::Rem => (Name::new_symbol_root(sym::rem_assign), LangItem::RemAssign),
-            ArithOp::Shl => (Name::new_symbol_root(sym::shl_assign), LangItem::ShlAssign),
-            ArithOp::Shr => (Name::new_symbol_root(sym::shr_assign), LangItem::ShrAssign),
-            ArithOp::BitXor => (Name::new_symbol_root(sym::bitxor_assign), LangItem::BitXorAssign),
-            ArithOp::BitOr => (Name::new_symbol_root(sym::bitor_assign), LangItem::BitOrAssign),
-            ArithOp::BitAnd => (Name::new_symbol_root(sym::bitand_assign), LangItem::BitAndAssign),
+            ArithOp::Add => (sym::add_assign, LangItem::AddAssign),
+            ArithOp::Mul => (sym::mul_assign, LangItem::MulAssign),
+            ArithOp::Sub => (sym::sub_assign, LangItem::SubAssign),
+            ArithOp::Div => (sym::div_assign, LangItem::DivAssign),
+            ArithOp::Rem => (sym::rem_assign, LangItem::RemAssign),
+            ArithOp::Shl => (sym::shl_assign, LangItem::ShlAssign),
+            ArithOp::Shr => (sym::shr_assign, LangItem::ShrAssign),
+            ArithOp::BitXor => (sym::bitxor_assign, LangItem::BitXorAssign),
+            ArithOp::BitOr => (sym::bitor_assign, LangItem::BitOrAssign),
+            ArithOp::BitAnd => (sym::bitand_assign, LangItem::BitAndAssign),
         },
         BinaryOp::CmpOp(cop) => match cop {
-            CmpOp::Eq { negated: false } => (Name::new_symbol_root(sym::eq), LangItem::PartialEq),
-            CmpOp::Eq { negated: true } => (Name::new_symbol_root(sym::ne), LangItem::PartialEq),
+            CmpOp::Eq { negated: false } => (sym::eq, LangItem::PartialEq),
+            CmpOp::Eq { negated: true } => (sym::ne, LangItem::PartialEq),
             CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
-                (Name::new_symbol_root(sym::le), LangItem::PartialOrd)
+                (sym::le, LangItem::PartialOrd)
             }
             CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
-                (Name::new_symbol_root(sym::lt), LangItem::PartialOrd)
+                (sym::lt, LangItem::PartialOrd)
             }
             CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
-                (Name::new_symbol_root(sym::ge), LangItem::PartialOrd)
+                (sym::ge, LangItem::PartialOrd)
             }
             CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
-                (Name::new_symbol_root(sym::gt), LangItem::PartialOrd)
+                (sym::gt, LangItem::PartialOrd)
             }
         },
         BinaryOp::Assignment { op: None } => return None,
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index fdacc1d..b29c7d2 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -89,13 +89,12 @@
     could_coerce, could_unify, could_unify_deeply,
 };
 pub use lower::{
-    LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId,
+    GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId,
     associated_type_shorthand_candidates, diagnostics::*,
 };
-pub use method_resolution::check_orphan_rules;
 pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db};
 pub use target_feature::TargetFeatures;
-pub use traits::TraitEnvironment;
+pub use traits::{TraitEnvironment, check_orphan_rules};
 pub use utils::{
     TargetFeatureIsSafeInTarget, Unsafety, all_super_traits, direct_super_traits,
     is_fn_unsafe_to_call, target_feature_is_safe_in_target,
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index a181ae0..a20c299 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -8,12 +8,9 @@
 pub(crate) mod diagnostics;
 pub(crate) mod path;
 
-use std::{
-    cell::OnceCell,
-    iter, mem,
-    ops::{self, Deref, Not as _},
-};
+use std::{cell::OnceCell, iter, mem};
 
+use arrayvec::ArrayVec;
 use base_db::Crate;
 use either::Either;
 use hir_def::{
@@ -45,7 +42,7 @@
     AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate,
     ExistentialProjection, ExistentialTraitRef, FnSig, OutlivesPredicate,
     TyKind::{self},
-    TypeVisitableExt,
+    TypeVisitableExt, Upcast,
     inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _},
 };
 use salsa::plumbing::AsId;
@@ -56,13 +53,13 @@
 use crate::{
     FnAbi, ImplTraitId, TraitEnvironment, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
     consteval::intern_const_ref,
-    db::HirDatabase,
+    db::{HirDatabase, InternedOpaqueTyId},
     generics::{Generics, generics, trait_self_param_idx},
     next_solver::{
-        AliasTy, Binder, BoundExistentialPredicates, Clause, Clauses, Const, DbInterner,
-        EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst,
-        ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, TraitRef, Ty, Tys,
-        UnevaluatedConst, abi::Safety,
+        AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const,
+        DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArg, GenericArgs,
+        ParamConst, ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, TraitRef,
+        Ty, Tys, UnevaluatedConst, abi::Safety,
     },
 };
 
@@ -75,7 +72,7 @@
 
 #[derive(PartialEq, Eq, Debug, Hash)]
 pub struct ImplTrait<'db> {
-    pub(crate) predicates: Vec<Clause<'db>>,
+    pub(crate) predicates: Box<[Clause<'db>]>,
 }
 
 pub type ImplTraitIdx<'db> = Idx<ImplTrait<'db>>;
@@ -473,7 +470,7 @@
                         let idx = self
                             .impl_trait_mode
                             .opaque_type_data
-                            .alloc(ImplTrait { predicates: Vec::default() });
+                            .alloc(ImplTrait { predicates: Box::default() });
 
                         let impl_trait_id = origin.either(
                             |f| ImplTraitId::ReturnTypeImplTrait(f, idx),
@@ -916,8 +913,7 @@
                 });
                 predicates.extend(sized_clause);
             }
-            predicates.shrink_to_fit();
-            predicates
+            predicates.into_boxed_slice()
         });
         ImplTrait { predicates }
     }
@@ -982,50 +978,89 @@
     Some((trait_ref, create_diagnostics(ctx.diagnostics)))
 }
 
-pub(crate) fn return_type_impl_traits<'db>(
-    db: &'db dyn HirDatabase,
-    def: hir_def::FunctionId,
-) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> {
-    // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
-    let data = db.function_signature(def);
-    let resolver = def.resolver(db);
-    let mut ctx_ret =
-        TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer)
-            .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
-    if let Some(ret_type) = data.ret_type {
-        let _ret = ctx_ret.lower_ty(ret_type);
-    }
-    let return_type_impl_traits =
-        ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data };
-    if return_type_impl_traits.impl_traits.is_empty() {
-        None
-    } else {
-        Some(Arc::new(EarlyBinder::bind(return_type_impl_traits)))
+impl<'db> ImplTraitId<'db> {
+    #[inline]
+    pub fn predicates(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        let (impl_traits, idx) = match self {
+            ImplTraitId::ReturnTypeImplTrait(owner, idx) => {
+                (ImplTraits::return_type_impl_traits(db, owner), idx)
+            }
+            ImplTraitId::TypeAliasImplTrait(owner, idx) => {
+                (ImplTraits::type_alias_impl_traits(db, owner), idx)
+            }
+        };
+        impl_traits
+            .as_deref()
+            .expect("owner should have opaque type")
+            .as_ref()
+            .map_bound(|it| &*it.impl_traits[idx].predicates)
     }
 }
 
-pub(crate) fn type_alias_impl_traits<'db>(
-    db: &'db dyn HirDatabase,
-    def: hir_def::TypeAliasId,
-) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> {
-    let data = db.type_alias_signature(def);
-    let resolver = def.resolver(db);
-    let mut ctx = TyLoweringContext::new(
-        db,
-        &resolver,
-        &data.store,
-        def.into(),
-        LifetimeElisionKind::AnonymousReportError,
-    )
-    .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
-    if let Some(type_ref) = data.ty {
-        let _ty = ctx.lower_ty(type_ref);
+impl InternedOpaqueTyId {
+    #[inline]
+    pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        self.loc(db).predicates(db)
     }
-    let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data };
-    if type_alias_impl_traits.impl_traits.is_empty() {
-        None
-    } else {
-        Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits)))
+}
+
+#[salsa::tracked]
+impl<'db> ImplTraits<'db> {
+    #[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
+    pub(crate) fn return_type_impl_traits(
+        db: &'db dyn HirDatabase,
+        def: hir_def::FunctionId,
+    ) -> Option<Box<EarlyBinder<'db, ImplTraits<'db>>>> {
+        // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
+        let data = db.function_signature(def);
+        let resolver = def.resolver(db);
+        let mut ctx_ret = TyLoweringContext::new(
+            db,
+            &resolver,
+            &data.store,
+            def.into(),
+            LifetimeElisionKind::Infer,
+        )
+        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+        if let Some(ret_type) = data.ret_type {
+            let _ret = ctx_ret.lower_ty(ret_type);
+        }
+        let mut return_type_impl_traits =
+            ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data };
+        if return_type_impl_traits.impl_traits.is_empty() {
+            None
+        } else {
+            return_type_impl_traits.impl_traits.shrink_to_fit();
+            Some(Box::new(EarlyBinder::bind(return_type_impl_traits)))
+        }
+    }
+
+    #[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
+    pub(crate) fn type_alias_impl_traits(
+        db: &'db dyn HirDatabase,
+        def: hir_def::TypeAliasId,
+    ) -> Option<Box<EarlyBinder<'db, ImplTraits<'db>>>> {
+        let data = db.type_alias_signature(def);
+        let resolver = def.resolver(db);
+        let mut ctx = TyLoweringContext::new(
+            db,
+            &resolver,
+            &data.store,
+            def.into(),
+            LifetimeElisionKind::AnonymousReportError,
+        )
+        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+        if let Some(type_ref) = data.ty {
+            let _ty = ctx.lower_ty(type_ref);
+        }
+        let mut type_alias_impl_traits =
+            ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data };
+        if type_alias_impl_traits.impl_traits.is_empty() {
+            None
+        } else {
+            type_alias_impl_traits.impl_traits.shrink_to_fit();
+            Some(Box::new(EarlyBinder::bind(type_alias_impl_traits)))
+        }
     }
 }
 
@@ -1331,12 +1366,13 @@
 /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
 /// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
 #[tracing::instrument(skip(db), ret)]
-pub(crate) fn generic_predicates_for_param_query<'db>(
+#[salsa::tracked(returns(ref), unsafe(non_update_return_type), cycle_result = generic_predicates_for_param_cycle_result)]
+pub(crate) fn generic_predicates_for_param<'db>(
     db: &'db dyn HirDatabase,
     def: GenericDefId,
     param_id: TypeOrConstParamId,
     assoc_name: Option<Name>,
-) -> GenericPredicates<'db> {
+) -> EarlyBinder<'db, Box<[Clause<'db>]>> {
     let generics = generics(db, def);
     let interner = DbInterner::new_with(db, None, None);
     let resolver = def.resolver(db);
@@ -1436,44 +1472,140 @@
             predicates.extend(implicitly_sized_predicates);
         };
     }
-    GenericPredicates(predicates.is_empty().not().then(|| predicates.into()))
+    EarlyBinder::bind(predicates.into_boxed_slice())
 }
 
-pub(crate) fn generic_predicates_for_param_cycle_result(
-    _db: &dyn HirDatabase,
+pub(crate) fn generic_predicates_for_param_cycle_result<'db>(
+    _db: &'db dyn HirDatabase,
     _def: GenericDefId,
     _param_id: TypeOrConstParamId,
     _assoc_name: Option<Name>,
-) -> GenericPredicates<'_> {
-    GenericPredicates(None)
+) -> EarlyBinder<'db, Box<[Clause<'db>]>> {
+    EarlyBinder::bind(Box::new([]))
+}
+
+#[inline]
+pub(crate) fn type_alias_bounds<'db>(
+    db: &'db dyn HirDatabase,
+    type_alias: TypeAliasId,
+) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+    type_alias_bounds_with_diagnostics(db, type_alias).0.as_ref().map_bound(|it| &**it)
+}
+
+#[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
+pub fn type_alias_bounds_with_diagnostics<'db>(
+    db: &'db dyn HirDatabase,
+    type_alias: TypeAliasId,
+) -> (EarlyBinder<'db, Box<[Clause<'db>]>>, Diagnostics) {
+    let type_alias_data = db.type_alias_signature(type_alias);
+    let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
+    let mut ctx = TyLoweringContext::new(
+        db,
+        &resolver,
+        &type_alias_data.store,
+        type_alias.into(),
+        LifetimeElisionKind::AnonymousReportError,
+    );
+    let interner = ctx.interner;
+    let def_id = type_alias.into();
+
+    let item_args = GenericArgs::identity_for_item(interner, def_id);
+    let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args);
+
+    let mut bounds = Vec::new();
+    for bound in &type_alias_data.bounds {
+        ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| {
+            bounds.push(pred);
+        });
+    }
+
+    if !ctx.unsized_types.contains(&interner_ty) {
+        let sized_trait = LangItem::Sized
+            .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate"));
+        if let Some(sized_trait) = sized_trait {
+            let trait_ref = TraitRef::new_from_args(
+                interner,
+                sized_trait.into(),
+                GenericArgs::new_from_iter(interner, [interner_ty.into()]),
+            );
+            bounds.push(trait_ref.upcast(interner));
+        };
+    }
+
+    (EarlyBinder::bind(bounds.into_boxed_slice()), create_diagnostics(ctx.diagnostics))
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct GenericPredicates<'db>(Option<Arc<[Clause<'db>]>>);
+pub struct GenericPredicates<'db> {
+    // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait predicate for the
+    // parent. Then come the explicit predicates for the parent, then the explicit trait predicate for the child,
+    // then the implicit trait predicate for the child, if `is_trait` is `true`.
+    predicates: EarlyBinder<'db, Box<[Clause<'db>]>>,
+    own_predicates_start: u32,
+    is_trait: bool,
+    parent_is_trait: bool,
+}
 
+#[salsa::tracked]
 impl<'db> GenericPredicates<'db> {
-    #[inline]
-    pub fn instantiate(
-        &self,
-        interner: DbInterner<'db>,
-        args: GenericArgs<'db>,
-    ) -> Option<impl Iterator<Item = Clause<'db>>> {
-        self.0
-            .as_ref()
-            .map(|it| EarlyBinder::bind(it.iter().copied()).iter_instantiated(interner, args))
-    }
-
-    #[inline]
-    pub fn instantiate_identity(&self) -> Option<impl Iterator<Item = Clause<'db>>> {
-        self.0.as_ref().map(|it| it.iter().copied())
+    /// Resolve the where clause(s) of an item with generics.
+    ///
+    /// Diagnostics are computed only for this item's predicates, not for parents.
+    #[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
+    pub fn query_with_diagnostics(
+        db: &'db dyn HirDatabase,
+        def: GenericDefId,
+    ) -> (GenericPredicates<'db>, Diagnostics) {
+        generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true)
     }
 }
 
-impl<'db> ops::Deref for GenericPredicates<'db> {
-    type Target = [Clause<'db>];
+impl<'db> GenericPredicates<'db> {
+    #[inline]
+    pub fn query(db: &'db dyn HirDatabase, def: GenericDefId) -> &'db GenericPredicates<'db> {
+        &Self::query_with_diagnostics(db, def).0
+    }
 
-    fn deref(&self) -> &Self::Target {
-        self.0.as_deref().unwrap_or(&[])
+    #[inline]
+    pub fn query_all(
+        db: &'db dyn HirDatabase,
+        def: GenericDefId,
+    ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        Self::query(db, def).all_predicates()
+    }
+
+    #[inline]
+    pub fn query_own(
+        db: &'db dyn HirDatabase,
+        def: GenericDefId,
+    ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        Self::query(db, def).own_predicates()
+    }
+
+    #[inline]
+    pub fn query_explicit(
+        db: &'db dyn HirDatabase,
+        def: GenericDefId,
+    ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        Self::query(db, def).explicit_predicates()
+    }
+
+    #[inline]
+    pub fn all_predicates(&self) -> EarlyBinder<'db, &[Clause<'db>]> {
+        self.predicates.as_ref().map_bound(|it| &**it)
+    }
+
+    #[inline]
+    pub fn own_predicates(&self) -> EarlyBinder<'db, &[Clause<'db>]> {
+        self.predicates.as_ref().map_bound(|it| &it[self.own_predicates_start as usize..])
+    }
+
+    /// Returns the predicates, minus the implicit `Self: Trait` predicate for a trait.
+    #[inline]
+    pub fn explicit_predicates(&self) -> EarlyBinder<'db, &[Clause<'db>]> {
+        self.predicates.as_ref().map_bound(|it| {
+            &it[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)]
+        })
     }
 }
 
@@ -1492,136 +1624,31 @@
     db: &'db dyn HirDatabase,
     def: GenericDefId,
 ) -> Arc<TraitEnvironment<'db>> {
-    let generics = generics(db, def);
-    if generics.has_no_predicates() && generics.is_empty() {
-        return TraitEnvironment::empty(def.krate(db));
-    }
-
-    let resolver = def.resolver(db);
-    let mut ctx = TyLoweringContext::new(
-        db,
-        &resolver,
-        generics.store(),
-        def,
-        LifetimeElisionKind::AnonymousReportError,
-    );
-    let mut traits_in_scope = Vec::new();
-    let mut clauses = Vec::new();
-    for maybe_parent_generics in
-        std::iter::successors(Some(&generics), |generics| generics.parent_generics())
-    {
-        ctx.store = maybe_parent_generics.store();
-        for pred in maybe_parent_generics.where_predicates() {
-            for pred in ctx.lower_where_predicate(pred, false, &generics, PredicateFilter::All) {
-                if let rustc_type_ir::ClauseKind::Trait(tr) = pred.kind().skip_binder() {
-                    traits_in_scope.push((tr.self_ty(), tr.def_id().0));
-                }
-                clauses.push(pred);
-            }
-        }
-    }
-
-    if let Some(trait_id) = def.assoc_trait_container(db) {
-        // add `Self: Trait<T1, T2, ...>` to the environment in trait
-        // function default implementations (and speculative code
-        // inside consts or type aliases)
-        cov_mark::hit!(trait_self_implements_self);
-        let trait_ref = TraitRef::identity(ctx.interner, trait_id.into());
-        let clause = Clause(Predicate::new(
-            ctx.interner,
-            Binder::dummy(rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(
-                TraitPredicate { trait_ref, polarity: rustc_type_ir::PredicatePolarity::Positive },
-            ))),
-        ));
-        clauses.push(clause);
-    }
-
-    let explicitly_unsized_tys = ctx.unsized_types;
-
-    let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate());
-    if let Some(sized_trait) = sized_trait {
-        let (mut generics, mut def_id) =
-            (crate::next_solver::generics::generics(db, def.into()), def);
-        loop {
-            let self_idx = trait_self_param_idx(db, def_id);
-            for (idx, p) in generics.own_params.iter().enumerate() {
-                if let Some(self_idx) = self_idx
-                    && p.index() as usize == self_idx
-                {
-                    continue;
-                }
-                let GenericParamId::TypeParamId(param_id) = p.id else {
-                    continue;
-                };
-                let idx = idx as u32 + generics.parent_count as u32;
-                let param_ty = Ty::new_param(ctx.interner, param_id, idx);
-                if explicitly_unsized_tys.contains(&param_ty) {
-                    continue;
-                }
-                let trait_ref = TraitRef::new_from_args(
-                    ctx.interner,
-                    sized_trait.into(),
-                    GenericArgs::new_from_iter(ctx.interner, [param_ty.into()]),
-                );
-                let clause = Clause(Predicate::new(
-                    ctx.interner,
-                    Binder::dummy(rustc_type_ir::PredicateKind::Clause(
-                        rustc_type_ir::ClauseKind::Trait(TraitPredicate {
-                            trait_ref,
-                            polarity: rustc_type_ir::PredicatePolarity::Positive,
-                        }),
-                    )),
-                ));
-                clauses.push(clause);
-            }
-
-            if let Some(g) = generics.parent {
-                generics = crate::next_solver::generics::generics(db, g.into());
-                def_id = g;
-            } else {
-                break;
-            }
-        }
-    }
-
-    let clauses = rustc_type_ir::elaborate::elaborate(ctx.interner, clauses);
-    let clauses = Clauses::new_from_iter(ctx.interner, clauses);
+    let module = def.module(db);
+    let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
+    let predicates = GenericPredicates::query_all(db, def);
+    let traits_in_scope = predicates
+        .iter_identity_copied()
+        .filter_map(|pred| match pred.kind().skip_binder() {
+            ClauseKind::Trait(tr) => Some((tr.self_ty(), tr.def_id().0)),
+            _ => None,
+        })
+        .collect();
+    let clauses = rustc_type_ir::elaborate::elaborate(interner, predicates.iter_identity_copied());
+    let clauses = Clauses::new_from_iter(interner, clauses);
     let env = ParamEnv { clauses };
 
-    TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
+    // FIXME: We should normalize projections here, like rustc does.
+
+    TraitEnvironment::new(module.krate(), module.containing_block(), traits_in_scope, env)
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum PredicateFilter {
     SelfTrait,
     All,
 }
 
-/// Resolve the where clause(s) of an item with generics.
-#[tracing::instrument(skip(db))]
-pub(crate) fn generic_predicates_query<'db>(
-    db: &'db dyn HirDatabase,
-    def: GenericDefId,
-) -> GenericPredicates<'db> {
-    generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0
-}
-
-pub(crate) fn generic_predicates_without_parent_query<'db>(
-    db: &'db dyn HirDatabase,
-    def: GenericDefId,
-) -> GenericPredicates<'db> {
-    generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0
-}
-
-/// Resolve the where clause(s) of an item with generics,
-/// except the ones inherited from the parent
-pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>(
-    db: &'db dyn HirDatabase,
-    def: GenericDefId,
-) -> (GenericPredicates<'db>, Diagnostics) {
-    generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def)
-}
-
 /// Resolve the where clause(s) of an item with generics,
 /// with a given filter
 #[tracing::instrument(skip(db, filter), ret)]
@@ -1644,15 +1671,35 @@
         def,
         LifetimeElisionKind::AnonymousReportError,
     );
+    let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate());
 
     let mut predicates = Vec::new();
-    for maybe_parent_generics in
+    let all_generics =
         std::iter::successors(Some(&generics), |generics| generics.parent_generics())
-    {
-        ctx.store = maybe_parent_generics.store();
-        for pred in maybe_parent_generics.where_predicates() {
-            tracing::debug!(?pred);
-            if filter(maybe_parent_generics.def()) {
+            .collect::<ArrayVec<_, 2>>();
+    let mut is_trait = false;
+    let mut parent_is_trait = false;
+    if all_generics.len() > 1 {
+        add_implicit_trait_predicate(
+            interner,
+            all_generics.last().unwrap().def(),
+            predicate_filter,
+            &mut predicates,
+            &mut parent_is_trait,
+        );
+    }
+    // We need to lower parent predicates first - see the comment below lowering of implicit `Sized` predicates
+    // for why.
+    let mut own_predicates_start = 0;
+    for &maybe_parent_generics in all_generics.iter().rev() {
+        let current_def_predicates_start = predicates.len();
+        // Collect only diagnostics from the child, not including parents.
+        ctx.diagnostics.clear();
+
+        if filter(maybe_parent_generics.def()) {
+            ctx.store = maybe_parent_generics.store();
+            for pred in maybe_parent_generics.where_predicates() {
+                tracing::debug!(?pred);
                 predicates.extend(ctx.lower_where_predicate(
                     pred,
                     false,
@@ -1660,66 +1707,132 @@
                     predicate_filter,
                 ));
             }
+
+            push_const_arg_has_type_predicates(db, &mut predicates, maybe_parent_generics);
+
+            if let Some(sized_trait) = sized_trait {
+                let mut add_sized_clause = |param_idx, param_id, param_data| {
+                    let (
+                        GenericParamId::TypeParamId(param_id),
+                        GenericParamDataRef::TypeParamData(param_data),
+                    ) = (param_id, param_data)
+                    else {
+                        return;
+                    };
+
+                    if param_data.provenance == TypeParamProvenance::TraitSelf {
+                        return;
+                    }
+
+                    let param_ty = Ty::new_param(interner, param_id, param_idx);
+                    if ctx.unsized_types.contains(&param_ty) {
+                        return;
+                    }
+                    let trait_ref = TraitRef::new_from_args(
+                        interner,
+                        sized_trait.into(),
+                        GenericArgs::new_from_iter(interner, [param_ty.into()]),
+                    );
+                    let clause = Clause(Predicate::new(
+                        interner,
+                        Binder::dummy(rustc_type_ir::PredicateKind::Clause(
+                            rustc_type_ir::ClauseKind::Trait(TraitPredicate {
+                                trait_ref,
+                                polarity: rustc_type_ir::PredicatePolarity::Positive,
+                            }),
+                        )),
+                    ));
+                    predicates.push(clause);
+                };
+                let parent_params_len = maybe_parent_generics.len_parent();
+                maybe_parent_generics.iter_self().enumerate().for_each(
+                    |(param_idx, (param_id, param_data))| {
+                        add_sized_clause(
+                            (param_idx + parent_params_len) as u32,
+                            param_id,
+                            param_data,
+                        );
+                    },
+                );
+            }
+
+            // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can
+            // be declared on the parent (e.g. the trait). It is nevertheless fine to register the implicit `Sized`
+            // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent.
+            // But we do have to lower the parent first.
+        }
+
+        if maybe_parent_generics.def() == def {
+            own_predicates_start = current_def_predicates_start as u32;
         }
     }
 
-    let explicitly_unsized_tys = ctx.unsized_types;
+    add_implicit_trait_predicate(interner, def, predicate_filter, &mut predicates, &mut is_trait);
 
-    let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate());
-    if let Some(sized_trait) = sized_trait {
-        let mut add_sized_clause = |param_idx, param_id, param_data| {
-            let (
-                GenericParamId::TypeParamId(param_id),
-                GenericParamDataRef::TypeParamData(param_data),
-            ) = (param_id, param_data)
-            else {
-                return;
-            };
+    let diagnostics = create_diagnostics(ctx.diagnostics);
+    let predicates = GenericPredicates {
+        own_predicates_start,
+        is_trait,
+        parent_is_trait,
+        predicates: EarlyBinder::bind(predicates.into_boxed_slice()),
+    };
+    return (predicates, diagnostics);
 
-            if param_data.provenance == TypeParamProvenance::TraitSelf {
-                return;
-            }
-
-            let param_ty = Ty::new_param(interner, param_id, param_idx);
-            if explicitly_unsized_tys.contains(&param_ty) {
-                return;
-            }
-            let trait_ref = TraitRef::new_from_args(
-                interner,
-                sized_trait.into(),
-                GenericArgs::new_from_iter(interner, [param_ty.into()]),
-            );
-            let clause = Clause(Predicate::new(
-                interner,
-                Binder::dummy(rustc_type_ir::PredicateKind::Clause(
-                    rustc_type_ir::ClauseKind::Trait(TraitPredicate {
-                        trait_ref,
-                        polarity: rustc_type_ir::PredicatePolarity::Positive,
-                    }),
-                )),
-            ));
-            predicates.push(clause);
-        };
-        if generics.parent_generics().is_some_and(|parent| filter(parent.def())) {
-            generics.iter_parent().enumerate().for_each(|(param_idx, (param_id, param_data))| {
-                add_sized_clause(param_idx as u32, param_id, param_data);
-            });
-        }
-        if filter(def) {
-            let parent_params_len = generics.len_parent();
-            generics.iter_self().enumerate().for_each(|(param_idx, (param_id, param_data))| {
-                add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data);
-            });
+    fn add_implicit_trait_predicate<'db>(
+        interner: DbInterner<'db>,
+        def: GenericDefId,
+        predicate_filter: PredicateFilter,
+        predicates: &mut Vec<Clause<'db>>,
+        set_is_trait: &mut bool,
+    ) {
+        // For traits, add `Self: Trait` predicate. This is
+        // not part of the predicates that a user writes, but it
+        // is something that one must prove in order to invoke a
+        // method or project an associated type.
+        //
+        // In the chalk setup, this predicate is not part of the
+        // "predicates" for a trait item. But it is useful in
+        // rustc because if you directly (e.g.) invoke a trait
+        // method like `Trait::method(...)`, you must naturally
+        // prove that the trait applies to the types that were
+        // used, and adding the predicate into this list ensures
+        // that this is done.
+        if let GenericDefId::TraitId(def_id) = def
+            && predicate_filter == PredicateFilter::All
+        {
+            *set_is_trait = true;
+            predicates.push(TraitRef::identity(interner, def_id.into()).upcast(interner));
         }
     }
+}
 
-    // FIXME: rustc gathers more predicates by recursing through resulting trait predicates.
-    // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715
+fn push_const_arg_has_type_predicates<'db>(
+    db: &'db dyn HirDatabase,
+    predicates: &mut Vec<Clause<'db>>,
+    generics: &Generics,
+) {
+    let interner = DbInterner::new_with(db, None, None);
+    let const_params_offset = generics.len_parent() + generics.len_lifetimes_self();
+    for (param_index, (param_idx, param_data)) in generics.iter_self_type_or_consts().enumerate() {
+        if !matches!(param_data, TypeOrConstParamData::ConstParamData(_)) {
+            continue;
+        }
 
-    (
-        GenericPredicates(predicates.is_empty().not().then(|| predicates.into())),
-        create_diagnostics(ctx.diagnostics),
-    )
+        let param_id = ConstParamId::from_unchecked(TypeOrConstParamId {
+            parent: generics.def(),
+            local_id: param_idx,
+        });
+        predicates.push(Clause(
+            ClauseKind::ConstArgHasType(
+                Const::new_param(
+                    interner,
+                    ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 },
+                ),
+                db.const_param_ty_ns(param_id),
+            )
+            .upcast(interner),
+        ));
+    }
 }
 
 /// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
@@ -2112,7 +2225,8 @@
                 |pred| pred != def && pred == GenericDefId::TraitId(trait_ref.def_id.0),
             )
             .0
-            .deref()
+            .predicates
+            .instantiate_identity()
             {
                 tracing::debug!(?pred);
                 let sup_trait_ref = match pred.kind().skip_binder() {
@@ -2158,10 +2272,11 @@
             }
 
             let predicates =
-                db.generic_predicates_for_param(def, param_id.into(), assoc_name.clone());
+                generic_predicates_for_param(db, def, param_id.into(), assoc_name.clone());
             predicates
-                .iter()
-                .find_map(|pred| match (*pred).kind().skip_binder() {
+                .as_ref()
+                .iter_identity_copied()
+                .find_map(|pred| match pred.kind().skip_binder() {
                     rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate),
                     _ => None,
                 })
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index 9ba0da6..6d3ce74 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -774,7 +774,7 @@
                 }
             }
 
-            fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> {
+            fn parent_arg(&mut self, _param_idx: u32, param_id: GenericParamId) -> GenericArg<'db> {
                 match param_id {
                     GenericParamId::TypeParamId(_) => {
                         Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into()
@@ -992,7 +992,7 @@
         preceding_args: &[GenericArg<'db>],
     ) -> GenericArg<'db>;
 
-    fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db>;
+    fn parent_arg(&mut self, param_idx: u32, param_id: GenericParamId) -> GenericArg<'db>;
 }
 
 /// Returns true if there was an error.
@@ -1129,7 +1129,9 @@
 
     let mut substs = Vec::with_capacity(def_generics.len());
 
-    substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id)));
+    substs.extend(
+        def_generics.iter_parent_id().enumerate().map(|(idx, id)| ctx.parent_arg(idx as u32, id)),
+    );
 
     let mut args = args_slice.iter().enumerate().peekable();
     let mut params = def_generics.iter_self().peekable();
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 1e30897..799bfb3 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -2,683 +2,349 @@
 //! For details about how this works in rustc, see the method lookup page in the
 //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
 //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
-use std::ops::ControlFlow;
+
+mod confirm;
+mod probe;
+
+use either::Either;
+use hir_expand::name::Name;
+use span::Edition;
+use tracing::{debug, instrument};
 
 use base_db::Crate;
 use hir_def::{
-    AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
-    ModuleId, TraitId, TypeAliasId,
+    AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
+    ModuleId, TraitId,
+    expr_store::path::GenericArgs as HirGenericArgs,
+    hir::ExprId,
     nameres::{DefMap, block_def_map, crate_def_map},
-    signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags},
+    resolver::Resolver,
 };
-use hir_expand::name::Name;
-use intern::sym;
-use rustc_ast_ir::Mutability;
+use intern::{Symbol, sym};
 use rustc_hash::{FxHashMap, FxHashSet};
 use rustc_type_ir::{
-    FloatTy, IntTy, TypeVisitableExt, UintTy,
-    inherent::{
-        AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _,
-    },
+    TypeVisitableExt,
+    fast_reject::{TreatParams, simplify_type},
+    inherent::{BoundExistentialPredicates, IntoKind, SliceLike},
 };
-use smallvec::{SmallVec, smallvec};
-use stdx::never;
+use stdx::impl_from;
 use triomphe::Arc;
 
 use crate::{
-    TraitEnvironment,
-    autoderef::{self, AutoderefKind},
+    TraitEnvironment, all_super_traits,
     db::HirDatabase,
-    infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable},
-    lang_items::is_box,
+    infer::{InferenceContext, unify::InferenceTable},
+    lower::GenericPredicates,
     next_solver::{
-        Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId,
-        TraitRef, Ty, TyKind, TypingMode,
+        Binder, ClauseKind, DbInterner, FnSig, GenericArgs, PredicateKind, SimplifiedType,
+        SolverDefId, TraitRef, Ty, TyKind, TypingMode,
         infer::{
-            DbInternerInferExt, InferCtxt,
+            BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
             select::ImplSource,
-            traits::{Obligation, ObligationCause, PredicateObligation},
+            traits::{Obligation, ObligationCause, PredicateObligations},
         },
         obligation_ctxt::ObligationCtxt,
+        util::clauses_as_obligations,
     },
-    traits::next_trait_solve_canonical_in_ctxt,
-    utils::all_super_traits,
 };
 
-/// This is used as a key for indexing impls.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub enum TyFingerprint {
-    // These are lang item impls:
-    Str,
-    Slice,
-    Array,
-    Never,
-    Ref(Mutability),
-    RawPtr(Mutability),
-    Bool,
-    Char,
-    Int(IntTy),
-    Uint(UintTy),
-    Float(FloatTy),
-    // These can have user-defined impls:
-    Adt(hir_def::AdtId),
-    Dyn(TraitId),
-    ForeignType(TypeAliasId),
-    // These only exist for trait impls
-    Unit,
-    Unnameable,
-    Function(u32),
-}
-
-impl TyFingerprint {
-    /// Creates a TyFingerprint for looking up an inherent impl. Only certain
-    /// types can have inherent impls: if we have some `struct S`, we can have
-    /// an `impl S`, but not `impl &S`. Hence, this will return `None` for
-    /// reference types and such.
-    pub fn for_inherent_impl<'db>(ty: Ty<'db>) -> Option<TyFingerprint> {
-        let fp = match ty.kind() {
-            TyKind::Str => TyFingerprint::Str,
-            TyKind::Never => TyFingerprint::Never,
-            TyKind::Slice(..) => TyFingerprint::Slice,
-            TyKind::Array(..) => TyFingerprint::Array,
-            TyKind::Bool => TyFingerprint::Bool,
-            TyKind::Char => TyFingerprint::Char,
-            TyKind::Int(int) => TyFingerprint::Int(int),
-            TyKind::Uint(int) => TyFingerprint::Uint(int),
-            TyKind::Float(float) => TyFingerprint::Float(float),
-            TyKind::Adt(adt_def, _) => TyFingerprint::Adt(adt_def.def_id().0),
-            TyKind::RawPtr(_, mutability) => TyFingerprint::RawPtr(mutability),
-            TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(alias_id.0),
-            TyKind::Dynamic(bounds, _) => {
-                bounds.principal_def_id().map(|trait_| TyFingerprint::Dyn(trait_.0))?
-            }
-            _ => return None,
-        };
-        Some(fp)
-    }
-
-    /// Creates a TyFingerprint for looking up a trait impl.
-    pub fn for_trait_impl<'db>(ty: Ty<'db>) -> Option<TyFingerprint> {
-        let fp = match ty.kind() {
-            TyKind::Str => TyFingerprint::Str,
-            TyKind::Never => TyFingerprint::Never,
-            TyKind::Slice(..) => TyFingerprint::Slice,
-            TyKind::Array(..) => TyFingerprint::Array,
-            TyKind::Bool => TyFingerprint::Bool,
-            TyKind::Char => TyFingerprint::Char,
-            TyKind::Int(int) => TyFingerprint::Int(int),
-            TyKind::Uint(int) => TyFingerprint::Uint(int),
-            TyKind::Float(float) => TyFingerprint::Float(float),
-            TyKind::Adt(adt_def, _) => TyFingerprint::Adt(adt_def.def_id().0),
-            TyKind::RawPtr(_, mutability) => TyFingerprint::RawPtr(mutability),
-            TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(alias_id.0),
-            TyKind::Dynamic(bounds, _) => {
-                bounds.principal_def_id().map(|trait_| TyFingerprint::Dyn(trait_.0))?
-            }
-            TyKind::Ref(_, _, mutability) => TyFingerprint::Ref(mutability),
-            TyKind::Tuple(subst) => {
-                let first_ty = subst.as_slice().first();
-                match first_ty {
-                    Some(ty) => return TyFingerprint::for_trait_impl(*ty),
-                    None => TyFingerprint::Unit,
-                }
-            }
-            // FIXME(next-solver): Putting `Alias` here is *probably* incorrect, AFAIK it should return `None`. But this breaks
-            // flyimport, which uses an incorrect but fast method resolution algorithm. Therefore we put it here,
-            // because this function is only called by flyimport, and anyway we should get rid of `TyFingerprint`
-            // and switch to `rustc_type_ir`'s `SimplifiedType`.
-            TyKind::Alias(..)
-            | TyKind::FnDef(_, _)
-            | TyKind::Closure(_, _)
-            | TyKind::Coroutine(..)
-            | TyKind::CoroutineClosure(..)
-            | TyKind::CoroutineWitness(..) => TyFingerprint::Unnameable,
-            TyKind::FnPtr(sig, _) => {
-                TyFingerprint::Function(sig.skip_binder().inputs_and_output.inner().len() as u32)
-            }
-            TyKind::Param(_)
-            | TyKind::Bound(..)
-            | TyKind::Placeholder(..)
-            | TyKind::Infer(_)
-            | TyKind::Error(_)
-            | TyKind::Pat(..)
-            | TyKind::UnsafeBinder(..) => return None,
-        };
-        Some(fp)
-    }
-}
-
-pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
-    TyFingerprint::Int(IntTy::I8),
-    TyFingerprint::Int(IntTy::I16),
-    TyFingerprint::Int(IntTy::I32),
-    TyFingerprint::Int(IntTy::I64),
-    TyFingerprint::Int(IntTy::I128),
-    TyFingerprint::Int(IntTy::Isize),
-    TyFingerprint::Uint(UintTy::U8),
-    TyFingerprint::Uint(UintTy::U16),
-    TyFingerprint::Uint(UintTy::U32),
-    TyFingerprint::Uint(UintTy::U64),
-    TyFingerprint::Uint(UintTy::U128),
-    TyFingerprint::Uint(UintTy::Usize),
-];
-
-pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 4] = [
-    TyFingerprint::Float(FloatTy::F16),
-    TyFingerprint::Float(FloatTy::F32),
-    TyFingerprint::Float(FloatTy::F64),
-    TyFingerprint::Float(FloatTy::F128),
-];
-
-type TraitFpMap = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Box<[ImplId]>>>;
-type TraitFpMapCollector = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>;
-
-/// Trait impls defined or available in some crate.
-#[derive(Debug, Eq, PartialEq)]
-pub struct TraitImpls {
-    // If the `Option<TyFingerprint>` is `None`, the impl may apply to any self type.
-    map: TraitFpMap,
-}
-
-impl TraitImpls {
-    pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
-        let _p = tracing::info_span!("trait_impls_in_crate_query", ?krate).entered();
-        let mut impls = FxHashMap::default();
-
-        Self::collect_def_map(db, &mut impls, crate_def_map(db, krate));
-
-        Arc::new(Self::finish(impls))
-    }
-
-    pub(crate) fn trait_impls_in_block_query(
-        db: &dyn HirDatabase,
-        block: BlockId,
-    ) -> Option<Arc<Self>> {
-        let _p = tracing::info_span!("trait_impls_in_block_query").entered();
-        let mut impls = FxHashMap::default();
-
-        Self::collect_def_map(db, &mut impls, block_def_map(db, block));
-
-        if impls.is_empty() { None } else { Some(Arc::new(Self::finish(impls))) }
-    }
-
-    pub(crate) fn trait_impls_in_deps_query(
-        db: &dyn HirDatabase,
-        krate: Crate,
-    ) -> Arc<[Arc<Self>]> {
-        let _p = tracing::info_span!("trait_impls_in_deps_query", ?krate).entered();
-        Arc::from_iter(
-            db.transitive_deps(krate).into_iter().map(|krate| db.trait_impls_in_crate(krate)),
-        )
-    }
-
-    fn finish(map: TraitFpMapCollector) -> TraitImpls {
-        TraitImpls {
-            map: map
-                .into_iter()
-                .map(|(k, v)| (k, v.into_iter().map(|(k, v)| (k, v.into_boxed_slice())).collect()))
-                .collect(),
-        }
-    }
-
-    fn collect_def_map(db: &dyn HirDatabase, map: &mut TraitFpMapCollector, def_map: &DefMap) {
-        for (_module_id, module_data) in def_map.modules() {
-            for impl_id in module_data.scope.impls() {
-                // Reservation impls should be ignored during trait resolution, so we never need
-                // them during type analysis. See rust-lang/rust#64631 for details.
-                //
-                // FIXME: Reservation impls should be considered during coherence checks. If we are
-                // (ever) to implement coherence checks, this filtering should be done by the trait
-                // solver.
-                if db.attrs(impl_id.into()).by_key(sym::rustc_reservation_impl).exists() {
-                    continue;
-                }
-                let target_trait = match db.impl_trait(impl_id) {
-                    Some(tr) => tr.skip_binder().def_id.0,
-                    None => continue,
-                };
-                let self_ty = db.impl_self_ty(impl_id);
-                let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.instantiate_identity());
-                map.entry(target_trait).or_default().entry(self_ty_fp).or_default().push(impl_id);
-            }
-
-            // To better support custom derives, collect impls in all unnamed const items.
-            // const _: () = { ... };
-            for konst in module_data.scope.unnamed_consts() {
-                let body = db.body(konst.into());
-                for (_, block_def_map) in body.blocks(db) {
-                    Self::collect_def_map(db, map, block_def_map);
-                }
-            }
-        }
-    }
-
-    /// Queries all trait impls for the given type.
-    pub fn for_self_ty_without_blanket_impls(
-        &self,
-        fp: TyFingerprint,
-    ) -> impl Iterator<Item = ImplId> + '_ {
-        self.map
-            .values()
-            .flat_map(move |impls| impls.get(&Some(fp)).into_iter())
-            .flat_map(|it| it.iter().copied())
-    }
-
-    /// Queries all impls of the given trait.
-    pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ {
-        self.map
-            .get(&trait_)
-            .into_iter()
-            .flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
-    }
-
-    /// Queries all impls of `trait_` that may apply to `self_ty`.
-    pub fn for_trait_and_self_ty(
-        &self,
-        trait_: TraitId,
-        self_ty: TyFingerprint,
-    ) -> impl Iterator<Item = ImplId> + '_ {
-        self.map
-            .get(&trait_)
-            .into_iter()
-            .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None)))
-            .flat_map(|v| v.iter().copied())
-    }
-
-    /// Queries whether `self_ty` has potentially applicable implementations of `trait_`.
-    pub fn has_impls_for_trait_and_self_ty(&self, trait_: TraitId, self_ty: TyFingerprint) -> bool {
-        self.for_trait_and_self_ty(trait_, self_ty).next().is_some()
-    }
-
-    pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
-        self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
-    }
-}
-
-/// Inherent impls defined in some crate.
-///
-/// Inherent impls can only be defined in the crate that also defines the self type of the impl
-/// (note that some primitives are considered to be defined by both libcore and liballoc).
-///
-/// This makes inherent impl lookup easier than trait impl lookup since we only have to consider a
-/// single crate.
-#[derive(Debug, Eq, PartialEq)]
-pub struct InherentImpls {
-    map: FxHashMap<TyFingerprint, Vec<ImplId>>,
-    invalid_impls: Vec<ImplId>,
-}
-
-impl InherentImpls {
-    pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
-        let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
-        let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
-
-        let crate_def_map = crate_def_map(db, krate);
-        impls.collect_def_map(db, crate_def_map);
-        impls.shrink_to_fit();
-
-        Arc::new(impls)
-    }
-
-    pub(crate) fn inherent_impls_in_block_query(
-        db: &dyn HirDatabase,
-        block: BlockId,
-    ) -> Option<Arc<Self>> {
-        let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
-        let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
-
-        let block_def_map = block_def_map(db, block);
-        impls.collect_def_map(db, block_def_map);
-        impls.shrink_to_fit();
-
-        if impls.map.is_empty() && impls.invalid_impls.is_empty() {
-            None
-        } else {
-            Some(Arc::new(impls))
-        }
-    }
-
-    fn shrink_to_fit(&mut self) {
-        self.map.values_mut().for_each(Vec::shrink_to_fit);
-        self.map.shrink_to_fit();
-    }
-
-    fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
-        for (_module_id, module_data) in def_map.modules() {
-            for impl_id in module_data.scope.impls() {
-                let data = db.impl_signature(impl_id);
-                if data.target_trait.is_some() {
-                    continue;
-                }
-
-                let self_ty = db.impl_self_ty(impl_id);
-                let self_ty = self_ty.instantiate_identity();
-
-                match is_inherent_impl_coherent(db, def_map, impl_id, self_ty) {
-                    true => {
-                        // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
-                        if let Some(fp) = TyFingerprint::for_inherent_impl(self_ty) {
-                            self.map.entry(fp).or_default().push(impl_id);
-                        }
-                    }
-                    false => self.invalid_impls.push(impl_id),
-                }
-            }
-
-            // To better support custom derives, collect impls in all unnamed const items.
-            // const _: () = { ... };
-            for konst in module_data.scope.unnamed_consts() {
-                let body = db.body(konst.into());
-                for (_, block_def_map) in body.blocks(db) {
-                    self.collect_def_map(db, block_def_map);
-                }
-            }
-        }
-    }
-
-    pub fn for_self_ty<'db>(&self, self_ty: Ty<'db>) -> &[ImplId] {
-        match TyFingerprint::for_inherent_impl(self_ty) {
-            Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]),
-            None => &[],
-        }
-    }
-
-    pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
-        self.map.values().flat_map(|v| v.iter().copied())
-    }
-
-    pub fn invalid_impls(&self) -> &[ImplId] {
-        &self.invalid_impls
-    }
-}
-
-pub(crate) fn incoherent_inherent_impl_crates(
-    db: &dyn HirDatabase,
-    krate: Crate,
-    fp: TyFingerprint,
-) -> SmallVec<[Crate; 2]> {
-    let _p = tracing::info_span!("incoherent_inherent_impl_crates").entered();
-    let mut res = SmallVec::new();
-
-    // should pass crate for finger print and do reverse deps
-
-    for krate in db.transitive_deps(krate) {
-        let impls = db.inherent_impls_in_crate(krate);
-        if impls.map.get(&fp).is_some_and(|v| !v.is_empty()) {
-            res.push(krate);
-        }
-    }
-
-    res
-}
-
-pub fn def_crates<'db>(
-    db: &'db dyn HirDatabase,
-    ty: Ty<'db>,
-    cur_crate: Crate,
-) -> Option<SmallVec<[Crate; 2]>> {
-    match ty.kind() {
-        TyKind::Adt(adt_def, _) => {
-            let def_id = adt_def.def_id().0;
-            let rustc_has_incoherent_inherent_impls = match def_id {
-                hir_def::AdtId::StructId(id) => db
-                    .struct_signature(id)
-                    .flags
-                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-                hir_def::AdtId::UnionId(id) => db
-                    .union_signature(id)
-                    .flags
-                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-                hir_def::AdtId::EnumId(id) => db
-                    .enum_signature(id)
-                    .flags
-                    .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-            };
-            Some(if rustc_has_incoherent_inherent_impls {
-                db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id))
-            } else {
-                smallvec![def_id.module(db).krate()]
-            })
-        }
-        TyKind::Foreign(alias) => {
-            let alias = alias.0;
-            Some(
-                if db
-                    .type_alias_signature(alias)
-                    .flags
-                    .contains(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL)
-                {
-                    db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(alias))
-                } else {
-                    smallvec![alias.module(db).krate()]
-                },
-            )
-        }
-        TyKind::Dynamic(bounds, _) => {
-            let trait_id = bounds.principal_def_id()?.0;
-            Some(
-                if db
-                    .trait_signature(trait_id)
-                    .flags
-                    .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
-                {
-                    db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id))
-                } else {
-                    smallvec![trait_id.module(db).krate()]
-                },
-            )
-        }
-        // for primitives, there may be impls in various places (core and alloc
-        // mostly). We just check the whole crate graph for crates with impls
-        // (cached behind a query).
-        TyKind::Bool
-        | TyKind::Char
-        | TyKind::Int(_)
-        | TyKind::Uint(_)
-        | TyKind::Float(_)
-        | TyKind::Str
-        | TyKind::Slice(_)
-        | TyKind::Array(..)
-        | TyKind::RawPtr(..) => Some(db.incoherent_inherent_impl_crates(
-            cur_crate,
-            TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"),
-        )),
-        _ => None,
-    }
-}
-
-/// Look up the method with the given name.
-pub(crate) fn lookup_method<'db>(
-    ty: &Canonical<'db, Ty<'db>>,
-    table: &mut InferenceTable<'db>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: &Name,
-) -> Option<(ReceiverAdjustments, FunctionId, bool)> {
-    let mut not_visible = None;
-    let res = iterate_method_candidates(
-        ty,
-        table,
-        traits_in_scope,
-        visible_from_module,
-        Some(name),
-        LookupMode::MethodCall,
-        |adjustments, f, visible| match f {
-            AssocItemId::FunctionId(f) if visible => Some((adjustments, f, true)),
-            AssocItemId::FunctionId(f) if not_visible.is_none() => {
-                not_visible = Some((adjustments, f, false));
-                None
-            }
-            _ => None,
-        },
-    );
-    res.or(not_visible)
-}
-
-/// Whether we're looking up a dotted method call (like `v.len()`) or a path
-/// (like `Vec::new`).
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum LookupMode {
-    /// Looking up a method call like `v.len()`: We only consider candidates
-    /// that have a `self` parameter, and do autoderef.
-    MethodCall,
-    /// Looking up a path like `Vec::new` or `Vec::default`: We consider all
-    /// candidates including associated constants, but don't do autoderef.
-    Path,
-}
-
-#[derive(Clone, Copy)]
-pub enum VisibleFromModule {
-    /// Filter for results that are visible from the given module
-    Filter(ModuleId),
-    /// Include impls from the given block.
-    IncludeBlock(BlockId),
-    /// Do nothing special in regards visibility
-    None,
-}
-
-impl From<Option<ModuleId>> for VisibleFromModule {
-    fn from(module: Option<ModuleId>) -> Self {
-        match module {
-            Some(module) => Self::Filter(module),
-            None => Self::None,
-        }
-    }
-}
-
-impl From<Option<BlockId>> for VisibleFromModule {
-    fn from(block: Option<BlockId>) -> Self {
-        match block {
-            Some(block) => Self::IncludeBlock(block),
-            None => Self::None,
-        }
-    }
-}
+pub use self::probe::{
+    Candidate, CandidateKind, CandidateStep, CandidateWithPrivate, Mode, Pick, PickKind,
+};
 
 #[derive(Debug, Clone)]
-pub enum AutorefOrPtrAdjustment {
-    Autoref(Mutability),
-    ToConstPtr,
+pub struct MethodResolutionUnstableFeatures {
+    arbitrary_self_types: bool,
+    arbitrary_self_types_pointers: bool,
+    supertrait_item_shadowing: bool,
 }
 
-#[derive(Debug, Clone, Default)]
-pub struct ReceiverAdjustments {
-    autoref: Option<AutorefOrPtrAdjustment>,
-    autoderefs: usize,
-    unsize_array: bool,
-}
-
-impl ReceiverAdjustments {
-    pub(crate) fn apply<'db>(
-        &self,
-        table: &mut InferenceTable<'db>,
-        mut ty: Ty<'db>,
-    ) -> (Ty<'db>, Vec<Adjustment<'db>>) {
-        let mut adjust = Vec::new();
-        let mut autoderef = table.autoderef(ty);
-        autoderef.next();
-        for _ in 0..self.autoderefs {
-            match autoderef.next() {
-                None => {
-                    never!("autoderef not possible for {:?}", ty);
-                    ty = Ty::new_error(table.interner(), ErrorGuaranteed);
-                    break;
-                }
-                Some((new_ty, _)) => {
-                    ty = new_ty;
-                    let mutbl = match self.autoref {
-                        Some(AutorefOrPtrAdjustment::Autoref(m)) => Some(m),
-                        Some(AutorefOrPtrAdjustment::ToConstPtr) => Some(Mutability::Not),
-                        // FIXME should we know the mutability here, when autoref is `None`?
-                        None => None,
-                    };
-                    adjust.push(Adjustment {
-                        kind: Adjust::Deref(match autoderef.steps().last().unwrap().1 {
-                            AutoderefKind::Overloaded => Some(OverloadedDeref(mutbl)),
-                            AutoderefKind::Builtin => None,
-                        }),
-                        target: ty,
-                    });
-                }
-            }
+impl MethodResolutionUnstableFeatures {
+    pub fn from_def_map(def_map: &DefMap) -> Self {
+        Self {
+            arbitrary_self_types: def_map.is_unstable_feature_enabled(&sym::arbitrary_self_types),
+            arbitrary_self_types_pointers: def_map
+                .is_unstable_feature_enabled(&sym::arbitrary_self_types_pointers),
+            supertrait_item_shadowing: def_map
+                .is_unstable_feature_enabled(&sym::supertrait_item_shadowing),
         }
-        if let Some(autoref) = &self.autoref {
-            let lt = table.next_region_var();
-            match autoref {
-                AutorefOrPtrAdjustment::Autoref(m) => {
-                    let a = Adjustment::borrow(table.interner(), *m, ty, lt);
-                    ty = a.target;
-                    adjust.push(a);
+    }
+}
+
+pub struct MethodResolutionContext<'a, 'db> {
+    pub infcx: &'a InferCtxt<'db>,
+    pub resolver: &'a Resolver<'db>,
+    pub env: &'a TraitEnvironment<'db>,
+    pub traits_in_scope: &'a FxHashSet<TraitId>,
+    pub edition: Edition,
+    pub unstable_features: &'a MethodResolutionUnstableFeatures,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum CandidateId {
+    FunctionId(FunctionId),
+    ConstId(ConstId),
+}
+impl_from!(FunctionId, ConstId for CandidateId);
+
+impl CandidateId {
+    fn container(self, db: &dyn HirDatabase) -> ItemContainerId {
+        match self {
+            CandidateId::FunctionId(id) => id.loc(db).container,
+            CandidateId::ConstId(id) => id.loc(db).container,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct MethodCallee<'db> {
+    /// Impl method ID, for inherent methods, or trait method ID, otherwise.
+    pub def_id: FunctionId,
+    pub args: GenericArgs<'db>,
+
+    /// Instantiated method signature, i.e., it has been
+    /// instantiated, normalized, and has had late-bound
+    /// lifetimes replaced with inference variables.
+    pub sig: FnSig<'db>,
+}
+
+#[derive(Debug)]
+pub enum MethodError<'db> {
+    /// Did not find an applicable method.
+    NoMatch,
+
+    /// Multiple methods might apply.
+    Ambiguity(Vec<CandidateSource>),
+
+    /// Found an applicable method, but it is not visible.
+    PrivateMatch(Pick<'db>),
+
+    /// Found a `Self: Sized` bound where `Self` is a trait object.
+    IllegalSizedBound { candidates: Vec<FunctionId>, needs_mut: bool },
+
+    /// Error has already been emitted, no need to emit another one.
+    ErrorReported,
+}
+
+// A pared down enum describing just the places from which a method
+// candidate can arise. Used for error reporting only.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum CandidateSource {
+    Impl(ImplId),
+    Trait(TraitId),
+}
+
+impl<'a, 'db> InferenceContext<'a, 'db> {
+    /// Performs method lookup. If lookup is successful, it will return the callee
+    /// and store an appropriate adjustment for the self-expr. In some cases it may
+    /// report an error (e.g., invoking the `drop` method).
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn lookup_method_including_private(
+        &mut self,
+        self_ty: Ty<'db>,
+        name: Name,
+        generic_args: Option<&HirGenericArgs>,
+        receiver: ExprId,
+        call_expr: ExprId,
+    ) -> Result<(MethodCallee<'db>, bool), MethodError<'db>> {
+        let (pick, is_visible) = match self.lookup_probe(name, self_ty) {
+            Ok(it) => (it, true),
+            Err(MethodError::PrivateMatch(it)) => {
+                // FIXME: Report error.
+                (it, false)
+            }
+            Err(err) => return Err(err),
+        };
+
+        let result = self.confirm_method(&pick, self_ty, call_expr, generic_args);
+        debug!("result = {:?}", result);
+
+        if result.illegal_sized_bound {
+            // FIXME: Report an error.
+        }
+
+        self.write_expr_adj(receiver, result.adjustments);
+        self.write_method_resolution(call_expr, result.callee.def_id, result.callee.args);
+
+        Ok((result.callee, is_visible))
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn lookup_probe(
+        &self,
+        method_name: Name,
+        self_ty: Ty<'db>,
+    ) -> probe::PickResult<'db> {
+        self.with_method_resolution(|ctx| {
+            let pick = ctx.probe_for_name(probe::Mode::MethodCall, method_name, self_ty)?;
+            Ok(pick)
+        })
+    }
+
+    pub(crate) fn with_method_resolution<R>(
+        &self,
+        f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R,
+    ) -> R {
+        let traits_in_scope = self.get_traits_in_scope();
+        let traits_in_scope = match &traits_in_scope {
+            Either::Left(it) => it,
+            Either::Right(it) => *it,
+        };
+        let ctx = MethodResolutionContext {
+            infcx: &self.table.infer_ctxt,
+            resolver: &self.resolver,
+            env: &self.table.trait_env,
+            traits_in_scope,
+            edition: self.edition,
+            unstable_features: &self.unstable_features,
+        };
+        f(&ctx)
+    }
+}
+
+/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`.
+///
+/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while
+/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary
+/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque
+/// types as rigid to support `impl Deref<Target = impl FnOnce()>` and
+/// `Box<impl FnOnce()>`.
+///
+/// We only want to treat opaque types as rigid if we need to eagerly choose
+/// between multiple candidates. We otherwise treat them as ordinary inference
+/// variable to avoid rejecting otherwise correct code.
+#[derive(Debug)]
+#[expect(dead_code)]
+pub(super) enum TreatNotYetDefinedOpaques {
+    AsInfer,
+    AsRigid,
+}
+
+impl<'db> InferenceTable<'db> {
+    /// `lookup_method_in_trait` is used for overloaded operators.
+    /// It does a very narrow slice of what the normal probe/confirm path does.
+    /// In particular, it doesn't really do any probing: it simply constructs
+    /// an obligation for a particular trait with the given self type and checks
+    /// whether that trait is implemented.
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn lookup_method_for_operator(
+        &self,
+        cause: ObligationCause,
+        method_name: Symbol,
+        trait_def_id: TraitId,
+        self_ty: Ty<'db>,
+        opt_rhs_ty: Option<Ty<'db>>,
+        treat_opaques: TreatNotYetDefinedOpaques,
+    ) -> Option<InferOk<'db, MethodCallee<'db>>> {
+        // Construct a trait-reference `self_ty : Trait<input_tys>`
+        let args = GenericArgs::for_item(
+            self.interner(),
+            trait_def_id.into(),
+            |param_idx, param_id, _| match param_id {
+                GenericParamId::LifetimeParamId(_) | GenericParamId::ConstParamId(_) => {
+                    unreachable!("did not expect operator trait to have lifetime/const")
                 }
-                AutorefOrPtrAdjustment::ToConstPtr => {
-                    if let TyKind::RawPtr(pointee, Mutability::Mut) = ty.kind() {
-                        let a = Adjustment {
-                            kind: Adjust::Pointer(PointerCast::MutToConstPointer),
-                            target: Ty::new_ptr(table.interner(), pointee, Mutability::Not),
-                        };
-                        ty = a.target;
-                        adjust.push(a);
+                GenericParamId::TypeParamId(_) => {
+                    if param_idx == 0 {
+                        self_ty.into()
+                    } else if let Some(rhs_ty) = opt_rhs_ty {
+                        assert_eq!(param_idx, 1, "did not expect >1 param on operator trait");
+                        rhs_ty.into()
                     } else {
-                        never!("`ToConstPtr` target is not a raw mutable pointer");
+                        // FIXME: We should stop passing `None` for the failure case
+                        // when probing for call exprs. I.e. `opt_rhs_ty` should always
+                        // be set when it needs to be.
+                        self.next_var_for_param(param_id)
                     }
                 }
-            };
-        }
-        if self.unsize_array {
-            ty = 'it: {
-                if let TyKind::Ref(l, inner, m) = ty.kind()
-                    && let TyKind::Array(inner, _) = inner.kind()
-                {
-                    break 'it Ty::new_ref(
-                        table.interner(),
-                        l,
-                        Ty::new_slice(table.interner(), inner),
-                        m,
-                    );
-                }
-                // FIXME: report diagnostic if array unsizing happens without indirection.
-                ty
-            };
-            adjust.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: ty });
-        }
-        (ty, adjust)
-    }
+            },
+        );
 
-    fn with_autoref(&self, a: AutorefOrPtrAdjustment) -> ReceiverAdjustments {
-        Self { autoref: Some(a), ..*self }
-    }
-}
+        let obligation = Obligation::new(
+            self.interner(),
+            cause,
+            self.trait_env.env,
+            TraitRef::new_from_args(self.interner(), trait_def_id.into(), args),
+        );
 
-// This would be nicer if it just returned an iterator, but that runs into
-// lifetime problems, because we need to borrow temp `CrateImplDefs`.
-// FIXME add a context type here?
-pub(crate) fn iterate_method_candidates<'db, T>(
-    ty: &Canonical<'db, Ty<'db>>,
-    table: &mut InferenceTable<'db>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    mode: LookupMode,
-    mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
-) -> Option<T> {
-    let mut slot = None;
-    _ = iterate_method_candidates_dyn_impl(
-        ty,
-        table,
-        traits_in_scope,
-        visible_from_module,
-        name,
-        mode,
-        &mut |adj, item, visible| {
-            assert!(slot.is_none());
-            if let Some(it) = callback(adj, item, visible) {
-                slot = Some(it);
-                return ControlFlow::Break(());
+        // Now we want to know if this can be matched
+        let matches_trait = match treat_opaques {
+            TreatNotYetDefinedOpaques::AsInfer => self.infer_ctxt.predicate_may_hold(&obligation),
+            TreatNotYetDefinedOpaques::AsRigid => {
+                self.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation)
             }
-            ControlFlow::Continue(())
-        },
-    );
-    slot
+        };
+
+        if !matches_trait {
+            debug!("--> Cannot match obligation");
+            // Cannot be matched, no such method resolution is possible.
+            return None;
+        }
+
+        // Trait must have a method named `m_name` and it should not have
+        // type parameters or early-bound regions.
+        let interner = self.interner();
+        // We use `Ident::with_dummy_span` since no built-in operator methods have
+        // any macro-specific hygiene, so the span's context doesn't really matter.
+        let Some(method_item) =
+            trait_def_id.trait_items(self.db).method_by_name(&Name::new_symbol_root(method_name))
+        else {
+            panic!("expected associated item for operator trait")
+        };
+
+        let def_id = method_item;
+
+        debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
+        let mut obligations = PredicateObligations::new();
+
+        // Instantiate late-bound regions and instantiate the trait
+        // parameters into the method type to get the actual method type.
+        //
+        // N.B., instantiate late-bound regions before normalizing the
+        // function signature so that normalization does not need to deal
+        // with bound regions.
+        let fn_sig =
+            self.db.callable_item_signature(method_item.into()).instantiate(interner, args);
+        let fn_sig = self
+            .infer_ctxt
+            .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig);
+
+        // Register obligations for the parameters. This will include the
+        // `Self` parameter, which in turn has a bound of the main trait,
+        // so this also effectively registers `obligation` as well. (We
+        // used to register `obligation` explicitly, but that resulted in
+        // double error messages being reported.)
+        //
+        // Note that as the method comes from a trait, it should not have
+        // any late-bound regions appearing in its bounds.
+        let bounds = GenericPredicates::query_all(self.db, method_item.into());
+        let bounds = clauses_as_obligations(
+            bounds.iter_instantiated_copied(interner, args.as_slice()),
+            ObligationCause::new(),
+            self.trait_env.env,
+        );
+
+        obligations.extend(bounds);
+
+        // Also add an obligation for the method type being well-formed.
+        debug!(
+            "lookup_method_in_trait: matched method fn_sig={:?} obligation={:?}",
+            fn_sig, obligation
+        );
+        for ty in fn_sig.inputs_and_output {
+            obligations.push(Obligation::new(
+                interner,
+                obligation.cause.clone(),
+                self.trait_env.env,
+                Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))),
+            ));
+        }
+
+        let callee = MethodCallee { def_id, args, sig: fn_sig };
+        debug!("callee = {:?}", callee);
+
+        Some(InferOk { obligations, value: callee })
+    }
 }
 
 pub fn lookup_impl_const<'db>(
@@ -690,7 +356,7 @@
     let interner = infcx.interner;
     let db = interner.db;
 
-    let trait_id = match const_id.lookup(db).container {
+    let trait_id = match const_id.loc(db).container {
         ItemContainerId::TraitId(id) => id,
         _ => return (const_id, subs),
     };
@@ -719,7 +385,7 @@
 ) -> Option<usize> {
     let db = interner.db;
 
-    let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else {
+    let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
         return None;
     };
     let trait_params = db.generic_params(trait_id.into()).len();
@@ -755,7 +421,7 @@
     let interner = DbInterner::new_with(db, Some(env.krate), env.block);
     let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
 
-    let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else {
+    let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
         return (func, fn_subst);
     };
     let trait_params = db.generic_params(trait_id.into()).len();
@@ -833,981 +499,337 @@
     }
 }
 
-fn is_inherent_impl_coherent<'db>(
-    db: &'db dyn HirDatabase,
-    def_map: &DefMap,
-    impl_id: ImplId,
-    self_ty: Ty<'db>,
-) -> bool {
-    let self_ty = self_ty.kind();
-    let impl_allowed = match self_ty {
-        TyKind::Tuple(_)
-        | TyKind::FnDef(_, _)
-        | TyKind::Array(_, _)
-        | TyKind::Never
-        | TyKind::RawPtr(_, _)
-        | TyKind::Ref(_, _, _)
-        | TyKind::Slice(_)
-        | TyKind::Str
-        | TyKind::Bool
-        | TyKind::Char
-        | TyKind::Int(_)
-        | TyKind::Uint(_)
-        | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(),
+#[salsa::tracked(returns(ref))]
+fn crates_containing_incoherent_inherent_impls(db: &dyn HirDatabase) -> Box<[Crate]> {
+    // We assume that only sysroot crates contain `#[rustc_has_incoherent_inherent_impls]`
+    // impls, since this is an internal feature and only std uses it.
+    db.all_crates().iter().copied().filter(|krate| krate.data(db).origin.is_lang()).collect()
+}
 
-        TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate() == def_map.krate(),
-        TyKind::Dynamic(it, _) => it
-            .principal_def_id()
-            .is_some_and(|trait_id| trait_id.0.module(db).krate() == def_map.krate()),
-
+pub fn incoherent_inherent_impls(db: &dyn HirDatabase, self_ty: SimplifiedType) -> &[ImplId] {
+    let has_incoherent_impls = match self_ty.def() {
+        Some(def_id) => match def_id.try_into() {
+            Ok(def_id) => {
+                db.attrs(def_id).by_key(sym::rustc_has_incoherent_inherent_impls).exists()
+            }
+            Err(()) => true,
+        },
         _ => true,
     };
-    impl_allowed || {
-        let rustc_has_incoherent_inherent_impls = match self_ty {
-            TyKind::Tuple(_)
-            | TyKind::FnDef(_, _)
-            | TyKind::Array(_, _)
-            | TyKind::Never
-            | TyKind::RawPtr(_, _)
-            | TyKind::Ref(_, _, _)
-            | TyKind::Slice(_)
-            | TyKind::Str
-            | TyKind::Bool
-            | TyKind::Char
-            | TyKind::Int(_)
-            | TyKind::Uint(_)
-            | TyKind::Float(_) => true,
+    return if !has_incoherent_impls {
+        &[]
+    } else {
+        incoherent_inherent_impls_query(db, (), self_ty)
+    };
 
-            TyKind::Adt(adt_def, _) => match adt_def.def_id().0 {
-                hir_def::AdtId::StructId(id) => db
-                    .struct_signature(id)
-                    .flags
-                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-                hir_def::AdtId::UnionId(id) => db
-                    .union_signature(id)
-                    .flags
-                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-                hir_def::AdtId::EnumId(it) => db
-                    .enum_signature(it)
-                    .flags
-                    .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
-            },
-            TyKind::Dynamic(it, _) => it.principal_def_id().is_some_and(|trait_id| {
-                db.trait_signature(trait_id.0)
-                    .flags
-                    .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
-            }),
+    #[salsa::tracked(returns(ref))]
+    fn incoherent_inherent_impls_query(
+        db: &dyn HirDatabase,
+        _force_query_input_to_be_interned: (),
+        self_ty: SimplifiedType,
+    ) -> Box<[ImplId]> {
+        let _p = tracing::info_span!("incoherent_inherent_impl_crates").entered();
 
-            _ => false,
-        };
-        let items = impl_id.impl_items(db);
-        rustc_has_incoherent_inherent_impls
-            && !items.items.is_empty()
-            && items.items.iter().all(|&(_, assoc)| match assoc {
-                AssocItemId::FunctionId(it) => {
-                    db.function_signature(it).flags.contains(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
-                }
-                AssocItemId::ConstId(it) => {
-                    db.const_signature(it).flags.contains(ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
-                }
-                AssocItemId::TypeAliasId(it) => db
-                    .type_alias_signature(it)
-                    .flags
-                    .contains(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
-            })
+        let mut result = Vec::new();
+        for &krate in crates_containing_incoherent_inherent_impls(db) {
+            let impls = InherentImpls::for_crate(db, krate);
+            result.extend_from_slice(impls.for_self_ty(&self_ty));
+        }
+        result.into_boxed_slice()
     }
 }
 
-/// Checks whether the impl satisfies the orphan rules.
-///
-/// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an `impl`` is valid only if at least one of the following is true:
-/// - Trait is a local trait
-/// - All of
-///   - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
-///   - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
-pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool {
-    let Some(impl_trait) = db.impl_trait(impl_) else {
-        // not a trait impl
-        return true;
-    };
+pub fn simplified_type_module(db: &dyn HirDatabase, ty: &SimplifiedType) -> Option<ModuleId> {
+    match ty.def()? {
+        SolverDefId::AdtId(id) => Some(id.module(db)),
+        SolverDefId::TypeAliasId(id) => Some(id.module(db)),
+        SolverDefId::TraitId(id) => Some(id.module(db)),
+        _ => None,
+    }
+}
 
-    let local_crate = impl_.lookup(db).container.krate();
-    let is_local = |tgt_crate| tgt_crate == local_crate;
+#[derive(Debug, PartialEq, Eq)]
+pub struct InherentImpls {
+    map: FxHashMap<SimplifiedType, Box<[ImplId]>>,
+}
 
-    let trait_ref = impl_trait.instantiate_identity();
-    let trait_id = trait_ref.def_id.0;
-    if is_local(trait_id.module(db).krate()) {
-        // trait to be implemented is local
-        return true;
+#[salsa::tracked]
+impl InherentImpls {
+    #[salsa::tracked(returns(ref))]
+    pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Self {
+        let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
+
+        let crate_def_map = crate_def_map(db, krate);
+
+        Self::collect_def_map(db, crate_def_map)
     }
 
-    let unwrap_fundamental = |mut ty: Ty<'db>| {
-        // Unwrap all layers of fundamental types with a loop.
-        loop {
-            match ty.kind() {
-                TyKind::Ref(_, referenced, _) => ty = referenced,
-                TyKind::Adt(adt_def, subs) => {
-                    let AdtId::StructId(s) = adt_def.def_id().0 else {
-                        break ty;
+    #[salsa::tracked(returns(ref))]
+    pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
+        let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
+
+        let block_def_map = block_def_map(db, block);
+        let result = Self::collect_def_map(db, block_def_map);
+        if result.map.is_empty() { None } else { Some(Box::new(result)) }
+    }
+}
+
+impl InherentImpls {
+    fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
+        let mut map = FxHashMap::default();
+        collect(db, def_map, &mut map);
+        let mut map = map
+            .into_iter()
+            .map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
+            .collect::<FxHashMap<_, _>>();
+        map.shrink_to_fit();
+        return Self { map };
+
+        fn collect(
+            db: &dyn HirDatabase,
+            def_map: &DefMap,
+            map: &mut FxHashMap<SimplifiedType, Vec<ImplId>>,
+        ) {
+            for (_module_id, module_data) in def_map.modules() {
+                for impl_id in module_data.scope.impls() {
+                    let data = db.impl_signature(impl_id);
+                    if data.target_trait.is_some() {
+                        continue;
+                    }
+
+                    let interner = DbInterner::new_with(db, None, None);
+                    let self_ty = db.impl_self_ty(impl_id);
+                    let self_ty = self_ty.instantiate_identity();
+                    if let Some(self_ty) =
+                        simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer)
+                    {
+                        map.entry(self_ty).or_default().push(impl_id);
+                    }
+                }
+
+                // To better support custom derives, collect impls in all unnamed const items.
+                // const _: () = { ... };
+                for konst in module_data.scope.unnamed_consts() {
+                    let body = db.body(konst.into());
+                    for (_, block_def_map) in body.blocks(db) {
+                        collect(db, block_def_map, map);
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn for_self_ty(&self, self_ty: &SimplifiedType) -> &[ImplId] {
+        self.map.get(self_ty).map(|it| &**it).unwrap_or_default()
+    }
+
+    pub fn for_each_crate_and_block(
+        db: &dyn HirDatabase,
+        krate: Crate,
+        block: Option<BlockId>,
+        for_each: &mut dyn FnMut(&InherentImpls),
+    ) {
+        let blocks = std::iter::successors(block, |block| block.loc(db).module.containing_block());
+        blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
+        for_each(Self::for_crate(db, krate));
+    }
+}
+
+#[derive(Debug, PartialEq)]
+struct OneTraitImpls {
+    non_blanket_impls: FxHashMap<SimplifiedType, Box<[ImplId]>>,
+    blanket_impls: Box<[ImplId]>,
+}
+
+#[derive(Default)]
+struct OneTraitImplsBuilder {
+    non_blanket_impls: FxHashMap<SimplifiedType, Vec<ImplId>>,
+    blanket_impls: Vec<ImplId>,
+}
+
+impl OneTraitImplsBuilder {
+    fn finish(self) -> OneTraitImpls {
+        let mut non_blanket_impls = self
+            .non_blanket_impls
+            .into_iter()
+            .map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
+            .collect::<FxHashMap<_, _>>();
+        non_blanket_impls.shrink_to_fit();
+        let blanket_impls = self.blanket_impls.into_boxed_slice();
+        OneTraitImpls { non_blanket_impls, blanket_impls }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+pub struct TraitImpls {
+    map: FxHashMap<TraitId, OneTraitImpls>,
+}
+
+#[salsa::tracked]
+impl TraitImpls {
+    #[salsa::tracked(returns(ref))]
+    pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
+        let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
+
+        let crate_def_map = crate_def_map(db, krate);
+        let result = Self::collect_def_map(db, crate_def_map);
+        Arc::new(result)
+    }
+
+    #[salsa::tracked(returns(ref))]
+    pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
+        let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
+
+        let block_def_map = block_def_map(db, block);
+        let result = Self::collect_def_map(db, block_def_map);
+        if result.map.is_empty() { None } else { Some(Box::new(result)) }
+    }
+
+    #[salsa::tracked(returns(ref))]
+    pub fn for_crate_and_deps(db: &dyn HirDatabase, krate: Crate) -> Box<[Arc<Self>]> {
+        db.transitive_deps(krate).iter().map(|&dep| Self::for_crate(db, dep).clone()).collect()
+    }
+}
+
+impl TraitImpls {
+    fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
+        let mut map = FxHashMap::default();
+        collect(db, def_map, &mut map);
+        let mut map = map
+            .into_iter()
+            .map(|(trait_id, trait_map)| (trait_id, trait_map.finish()))
+            .collect::<FxHashMap<_, _>>();
+        map.shrink_to_fit();
+        return Self { map };
+
+        fn collect(
+            db: &dyn HirDatabase,
+            def_map: &DefMap,
+            map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
+        ) {
+            for (_module_id, module_data) in def_map.modules() {
+                for impl_id in module_data.scope.impls() {
+                    // Reservation impls should be ignored during trait resolution, so we never need
+                    // them during type analysis. See rust-lang/rust#64631 for details.
+                    //
+                    // FIXME: Reservation impls should be considered during coherence checks. If we are
+                    // (ever) to implement coherence checks, this filtering should be done by the trait
+                    // solver.
+                    if db.attrs(impl_id.into()).by_key(sym::rustc_reservation_impl).exists() {
+                        continue;
+                    }
+                    let trait_ref = match db.impl_trait(impl_id) {
+                        Some(tr) => tr.instantiate_identity(),
+                        None => continue,
                     };
-                    let struct_signature = db.struct_signature(s);
-                    if struct_signature.flags.contains(StructFlags::FUNDAMENTAL) {
-                        let next = subs.types().next();
-                        match next {
-                            Some(it) => ty = it,
-                            None => break ty,
+                    let self_ty = trait_ref.self_ty();
+                    let interner = DbInterner::new_with(db, None, None);
+                    let entry = map.entry(trait_ref.def_id.0).or_default();
+                    match simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) {
+                        Some(self_ty) => {
+                            entry.non_blanket_impls.entry(self_ty).or_default().push(impl_id)
                         }
-                    } else {
-                        break ty;
+                        None => entry.blanket_impls.push(impl_id),
                     }
                 }
-                _ => break ty,
+
+                // To better support custom derives, collect impls in all unnamed const items.
+                // const _: () = { ... };
+                for konst in module_data.scope.unnamed_consts() {
+                    let body = db.body(konst.into());
+                    for (_, block_def_map) in body.blocks(db) {
+                        collect(db, block_def_map, map);
+                    }
+                }
             }
         }
-    };
-    //   - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
+    }
 
-    // FIXME: param coverage
-    //   - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
-    let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() {
-        TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate()),
-        TyKind::Error(_) => true,
-        TyKind::Dynamic(it, _) => {
-            it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate()))
+    pub fn blanket_impls(&self, for_trait: TraitId) -> &[ImplId] {
+        self.map.get(&for_trait).map(|it| &*it.blanket_impls).unwrap_or_default()
+    }
+
+    /// Queries whether `self_ty` has potentially applicable implementations of `trait_`.
+    pub fn has_impls_for_trait_and_self_ty(
+        &self,
+        trait_: TraitId,
+        self_ty: &SimplifiedType,
+    ) -> bool {
+        self.map.get(&trait_).is_some_and(|trait_impls| {
+            trait_impls.non_blanket_impls.contains_key(self_ty)
+                || !trait_impls.blanket_impls.is_empty()
+        })
+    }
+
+    pub fn for_trait_and_self_ty(&self, trait_: TraitId, self_ty: &SimplifiedType) -> &[ImplId] {
+        self.map
+            .get(&trait_)
+            .and_then(|map| map.non_blanket_impls.get(self_ty))
+            .map(|it| &**it)
+            .unwrap_or_default()
+    }
+
+    pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
+        if let Some(impls) = self.map.get(&trait_) {
+            callback(&impls.blanket_impls);
+            for impls in impls.non_blanket_impls.values() {
+                callback(impls);
+            }
         }
-        _ => false,
-    });
-    #[allow(clippy::let_and_return)]
-    is_not_orphan
-}
+    }
 
-/// To be used from `hir` only.
-pub fn iterate_path_candidates<'db>(
-    ty: &Canonical<'db, Ty<'db>>,
-    db: &'db dyn HirDatabase,
-    env: Arc<TraitEnvironment<'db>>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    iterate_method_candidates_dyn(
-        ty,
-        db,
-        env,
-        traits_in_scope,
-        visible_from_module,
-        name,
-        LookupMode::Path,
-        // the adjustments are not relevant for path lookup
-        callback,
-    )
-}
+    pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
+        for for_trait in self.map.values() {
+            if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
+                callback(for_ty);
+            }
+        }
+    }
 
-/// To be used from `hir` only.
-pub fn iterate_method_candidates_dyn<'db>(
-    ty: &Canonical<'db, Ty<'db>>,
-    db: &'db dyn HirDatabase,
-    env: Arc<TraitEnvironment<'db>>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    mode: LookupMode,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    iterate_method_candidates_dyn_impl(
-        ty,
-        &mut InferenceTable::new(db, env, None),
-        traits_in_scope,
-        visible_from_module,
-        name,
-        mode,
-        callback,
-    )
-}
+    pub fn for_each_crate_and_block(
+        db: &dyn HirDatabase,
+        krate: Crate,
+        block: Option<BlockId>,
+        for_each: &mut dyn FnMut(&TraitImpls),
+    ) {
+        let blocks = std::iter::successors(block, |block| block.loc(db).module.containing_block());
+        blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
+        Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each);
+    }
 
-fn iterate_method_candidates_dyn_impl<'db>(
-    ty: &Canonical<'db, Ty<'db>>,
-    table: &mut InferenceTable<'db>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    mode: LookupMode,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    let _p = tracing::info_span!(
-        "iterate_method_candidates_dyn",
-        ?mode,
-        ?name,
-        traits_in_scope_len = traits_in_scope.len()
-    )
-    .entered();
+    /// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type.
+    pub fn for_each_crate_and_block_trait_and_type(
+        db: &dyn HirDatabase,
+        krate: Crate,
+        type_block: Option<BlockId>,
+        trait_block: Option<BlockId>,
+        for_each: &mut dyn FnMut(&TraitImpls),
+    ) {
+        let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate);
+        in_self_and_deps.iter().for_each(|impls| for_each(impls));
 
-    match mode {
-        LookupMode::MethodCall => {
-            // For method calls, rust first does any number of autoderef, and
-            // then one autoref (i.e. when the method takes &self or &mut self).
-            // Note that when we've got a receiver like &S, even if the method
-            // we find in the end takes &self, we still do the autoderef step
-            // (just as rustc does an autoderef and then autoref again).
-
-            // We have to be careful about the order we're looking at candidates
-            // in here. Consider the case where we're resolving `it.clone()`
-            // where `it: &Vec<_>`. This resolves to the clone method with self
-            // type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where
-            // the receiver type exactly matches before cases where we have to
-            // do autoref. But in the autoderef steps, the `&_` self type comes
-            // up *before* the `Vec<_>` self type.
-            //
-            // On the other hand, we don't want to just pick any by-value method
-            // before any by-autoref method; it's just that we need to consider
-            // the methods by autoderef order of *receiver types*, not *self
-            // types*.
-
-            table.run_in_snapshot(|table| {
-                let ty = table.instantiate_canonical(*ty);
-                let deref_chain = autoderef_method_receiver(table, ty);
-
-                deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
-                    iterate_method_candidates_with_autoref(
-                        table,
-                        receiver_ty,
-                        adj,
-                        traits_in_scope,
-                        visible_from_module,
-                        name,
-                        callback,
-                    )
+        // We must not provide duplicate impls to the solver. Therefore we work with the following strategy:
+        // start from each block, and walk ancestors until you meet the other block. If they never meet,
+        // that means there can't be duplicate impls; if they meet, we stop the search of the deeper block.
+        // This breaks when they are equal (both will stop immediately), therefore we handle this case
+        // specifically.
+        let blocks_iter = |block: Option<BlockId>| {
+            std::iter::successors(block, |block| block.loc(db).module.containing_block())
+        };
+        let for_each_block = |current_block: Option<BlockId>, other_block: Option<BlockId>| {
+            blocks_iter(current_block)
+                .take_while(move |&block| {
+                    other_block.is_none_or(|other_block| other_block != block)
                 })
-            })
-        }
-        LookupMode::Path => {
-            // No autoderef for path lookups
-            iterate_method_candidates_for_self_ty(
-                ty,
-                table,
-                traits_in_scope,
-                visible_from_module,
-                name,
-                callback,
-            )
-        }
-    }
-}
-
-#[tracing::instrument(skip_all, fields(name = ?name))]
-fn iterate_method_candidates_with_autoref<'db>(
-    table: &mut InferenceTable<'db>,
-    receiver_ty: Canonical<'db, Ty<'db>>,
-    first_adjustment: ReceiverAdjustments,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    let interner = table.interner();
-
-    let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| {
-        iterate_method_candidates_by_receiver(
-            table,
-            receiver_ty,
-            first_adjustment,
-            traits_in_scope,
-            visible_from_module,
-            name,
-            callback,
-        )
-    };
-
-    let mut maybe_reborrowed = first_adjustment.clone();
-    if let TyKind::Ref(_, _, m) = receiver_ty.value.kind() {
-        // Prefer reborrow of references to move
-        maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m));
-        maybe_reborrowed.autoderefs += 1;
-    }
-
-    iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
-
-    let refed = Canonical {
-        max_universe: receiver_ty.max_universe,
-        variables: receiver_ty.variables,
-        value: Ty::new_ref(interner, Region::error(interner), receiver_ty.value, Mutability::Not),
-    };
-
-    iterate_method_candidates_by_receiver(
-        refed,
-        first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)),
-    )?;
-
-    let ref_muted = Canonical {
-        max_universe: receiver_ty.max_universe,
-        variables: receiver_ty.variables,
-        value: Ty::new_ref(interner, Region::error(interner), receiver_ty.value, Mutability::Mut),
-    };
-
-    iterate_method_candidates_by_receiver(
-        ref_muted,
-        first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)),
-    )?;
-
-    if let TyKind::RawPtr(ty, Mutability::Mut) = receiver_ty.value.kind() {
-        let const_ptr_ty = rustc_type_ir::Canonical {
-            max_universe: rustc_type_ir::UniverseIndex::ZERO,
-            value: Ty::new_ptr(interner, ty, Mutability::Not),
-            variables: receiver_ty.variables,
+                .filter_map(move |block| TraitImpls::for_block(db, block).as_deref())
         };
-        iterate_method_candidates_by_receiver(
-            const_ptr_ty,
-            first_adjustment.with_autoref(AutorefOrPtrAdjustment::ToConstPtr),
-        )?;
-    }
-
-    ControlFlow::Continue(())
-}
-
-pub trait MethodCandidateCallback {
-    fn on_inherent_method(
-        &mut self,
-        adjustments: ReceiverAdjustments,
-        item: AssocItemId,
-        is_visible: bool,
-    ) -> ControlFlow<()>;
-
-    fn on_trait_method(
-        &mut self,
-        adjustments: ReceiverAdjustments,
-        item: AssocItemId,
-        is_visible: bool,
-    ) -> ControlFlow<()>;
-}
-
-impl<F> MethodCandidateCallback for F
-where
-    F: FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
-{
-    fn on_inherent_method(
-        &mut self,
-        adjustments: ReceiverAdjustments,
-        item: AssocItemId,
-        is_visible: bool,
-    ) -> ControlFlow<()> {
-        self(adjustments, item, is_visible)
-    }
-
-    fn on_trait_method(
-        &mut self,
-        adjustments: ReceiverAdjustments,
-        item: AssocItemId,
-        is_visible: bool,
-    ) -> ControlFlow<()> {
-        self(adjustments, item, is_visible)
-    }
-}
-
-#[tracing::instrument(skip_all, fields(name = ?name))]
-fn iterate_method_candidates_by_receiver<'db>(
-    table: &mut InferenceTable<'db>,
-    receiver_ty: Canonical<'db, Ty<'db>>,
-    receiver_adjustments: ReceiverAdjustments,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    let receiver_ty = table.instantiate_canonical(receiver_ty);
-    // We're looking for methods with *receiver* type receiver_ty. These could
-    // be found in any of the derefs of receiver_ty, so we have to go through
-    // that, including raw derefs.
-    table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty)
-            .include_raw_pointers()
-            .use_receiver_trait();
-        while let Some((self_ty, _)) = autoderef.next() {
-            iterate_inherent_methods(
-                self_ty,
-                autoderef.table,
-                name,
-                Some(receiver_ty),
-                Some(receiver_adjustments.clone()),
-                visible_from_module,
-                LookupMode::MethodCall,
-                &mut |adjustments, item, is_visible| {
-                    callback.on_inherent_method(adjustments, item, is_visible)
-                },
-            )?
-        }
-        ControlFlow::Continue(())
-    })?;
-    table.run_in_snapshot(|table| {
-        let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty)
-            .include_raw_pointers()
-            .use_receiver_trait();
-        while let Some((self_ty, _)) = autoderef.next() {
-            if matches!(self_ty.kind(), TyKind::Infer(rustc_type_ir::TyVar(_))) {
-                // don't try to resolve methods on unknown types
-                return ControlFlow::Continue(());
-            }
-
-            iterate_trait_method_candidates(
-                self_ty,
-                autoderef.table,
-                traits_in_scope,
-                name,
-                Some(receiver_ty),
-                Some(receiver_adjustments.clone()),
-                LookupMode::MethodCall,
-                &mut |adjustments, item, is_visible| {
-                    callback.on_trait_method(adjustments, item, is_visible)
-                },
-            )?
-        }
-        ControlFlow::Continue(())
-    })
-}
-
-#[tracing::instrument(skip_all, fields(name = ?name))]
-fn iterate_method_candidates_for_self_ty<'db>(
-    self_ty: &Canonical<'db, Ty<'db>>,
-    table: &mut InferenceTable<'db>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    visible_from_module: VisibleFromModule,
-    name: Option<&Name>,
-    callback: &mut dyn MethodCandidateCallback,
-) -> ControlFlow<()> {
-    table.run_in_snapshot(|table| {
-        let self_ty = table.instantiate_canonical(*self_ty);
-        iterate_inherent_methods(
-            self_ty,
-            table,
-            name,
-            None,
-            None,
-            visible_from_module,
-            LookupMode::Path,
-            &mut |adjustments, item, is_visible| {
-                callback.on_inherent_method(adjustments, item, is_visible)
-            },
-        )?;
-        iterate_trait_method_candidates(
-            self_ty,
-            table,
-            traits_in_scope,
-            name,
-            None,
-            None,
-            LookupMode::Path,
-            &mut |adjustments, item, is_visible| {
-                callback.on_trait_method(adjustments, item, is_visible)
-            },
-        )
-    })
-}
-
-#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
-fn iterate_trait_method_candidates<'db>(
-    self_ty: Ty<'db>,
-    table: &mut InferenceTable<'db>,
-    traits_in_scope: &FxHashSet<TraitId>,
-    name: Option<&Name>,
-    receiver_ty: Option<Ty<'db>>,
-    receiver_adjustments: Option<ReceiverAdjustments>,
-    mode: LookupMode,
-    callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
-) -> ControlFlow<()> {
-    let db = table.db;
-
-    let canonical_self_ty = table.canonicalize(self_ty);
-    let krate = table.trait_env.krate;
-
-    'traits: for &t in traits_in_scope {
-        let data = db.trait_signature(t);
-
-        // Traits annotated with `#[rustc_skip_during_method_dispatch]` are skipped during
-        // method resolution, if the receiver is an array, and we're compiling for editions before
-        // 2021.
-        // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
-        // arrays.
-        if data.flags.contains(TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH)
-            && matches!(self_ty.kind(), TyKind::Array(..))
-        {
-            // FIXME: this should really be using the edition of the method name's span, in case it
-            // comes from a macro
-            if !krate.data(db).edition.at_least_2021() {
-                continue;
-            }
-        }
-        if data.flags.contains(TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH)
-            && matches!(
-                self_ty.kind(), TyKind::Adt(adt_def, subst)
-                if is_box(table.db, adt_def.def_id().0)
-                    && matches!(subst.type_at(0).kind(), TyKind::Slice(..))
-            )
-        {
-            // FIXME: this should really be using the edition of the method name's span, in case it
-            // comes from a macro
-            if !krate.data(db).edition.at_least_2024() {
-                continue;
-            }
-        }
-
-        // we'll be lazy about checking whether the type implements the
-        // trait, but if we find out it doesn't, we'll skip the rest of the
-        // iteration
-        let mut known_implemented = false;
-        for &(_, item) in t.trait_items(db).items.iter() {
-            // Don't pass a `visible_from_module` down to `is_valid_candidate`,
-            // since only inherent methods should be included into visibility checking.
-            let visible = match is_valid_trait_method_candidate(
-                table,
-                t,
-                name,
-                receiver_ty,
-                item,
-                self_ty,
-                mode,
-            ) {
-                IsValidCandidate::Yes => true,
-                IsValidCandidate::NotVisible => false,
-                IsValidCandidate::No => continue,
-            };
-            if !known_implemented {
-                let goal = generic_implements_goal_ns(table, t, canonical_self_ty);
-                if next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() {
-                    continue 'traits;
-                }
-            }
-            known_implemented = true;
-            callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
-        }
-    }
-    ControlFlow::Continue(())
-}
-
-#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
-fn iterate_inherent_methods<'db>(
-    self_ty: Ty<'db>,
-    table: &mut InferenceTable<'db>,
-    name: Option<&Name>,
-    receiver_ty: Option<Ty<'db>>,
-    receiver_adjustments: Option<ReceiverAdjustments>,
-    visible_from_module: VisibleFromModule,
-    mode: LookupMode,
-    callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
-) -> ControlFlow<()> {
-    let db = table.db;
-    let env = table.trait_env.clone();
-
-    // For trait object types and placeholder types with trait bounds, the methods of the trait and
-    // its super traits are considered inherent methods. This matters because these methods have
-    // higher priority than the other traits' methods, which would be considered in
-    // `iterate_trait_method_candidates()` only after this function.
-    match self_ty.kind() {
-        TyKind::Param(_) => {
-            let env = table.trait_env.clone();
-            let traits =
-                env.traits_in_scope_from_clauses(self_ty).flat_map(|t| all_super_traits(db, t));
-            iterate_inherent_trait_methods(
-                self_ty,
-                table,
-                name,
-                receiver_ty,
-                receiver_adjustments.clone(),
-                callback,
-                traits,
-                mode,
-            )?;
-        }
-        TyKind::Dynamic(bounds, _) => {
-            if let Some(principal_trait) = bounds.principal_def_id() {
-                let traits = all_super_traits(db, principal_trait.0);
-                iterate_inherent_trait_methods(
-                    self_ty,
-                    table,
-                    name,
-                    receiver_ty,
-                    receiver_adjustments.clone(),
-                    callback,
-                    traits.into_iter(),
-                    mode,
-                )?;
-            }
-        }
-        _ => {}
-    }
-
-    let def_crates = match def_crates(db, self_ty, env.krate) {
-        Some(k) => k,
-        None => return ControlFlow::Continue(()),
-    };
-
-    let (module, mut block) = match visible_from_module {
-        VisibleFromModule::Filter(module) => (Some(module), module.containing_block()),
-        VisibleFromModule::IncludeBlock(block) => (None, Some(block)),
-        VisibleFromModule::None => (None, None),
-    };
-
-    while let Some(block_id) = block {
-        if let Some(impls) = db.inherent_impls_in_block(block_id) {
-            impls_for_self_ty(
-                &impls,
-                self_ty,
-                table,
-                name,
-                receiver_ty,
-                receiver_adjustments.clone(),
-                module,
-                callback,
-            )?;
-        }
-
-        block = block_def_map(db, block_id).parent().and_then(|module| module.containing_block());
-    }
-
-    for krate in def_crates {
-        let impls = db.inherent_impls_in_crate(krate);
-        impls_for_self_ty(
-            &impls,
-            self_ty,
-            table,
-            name,
-            receiver_ty,
-            receiver_adjustments.clone(),
-            module,
-            callback,
-        )?;
-    }
-    return ControlFlow::Continue(());
-
-    #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
-    fn iterate_inherent_trait_methods<'db>(
-        self_ty: Ty<'db>,
-        table: &mut InferenceTable<'db>,
-        name: Option<&Name>,
-        receiver_ty: Option<Ty<'db>>,
-        receiver_adjustments: Option<ReceiverAdjustments>,
-        callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
-        traits: impl Iterator<Item = TraitId>,
-        mode: LookupMode,
-    ) -> ControlFlow<()> {
-        let db = table.db;
-        for t in traits {
-            let data = t.trait_items(db);
-            for &(_, item) in data.items.iter() {
-                // We don't pass `visible_from_module` as all trait items should be visible.
-                let visible = match is_valid_trait_method_candidate(
-                    table,
-                    t,
-                    name,
-                    receiver_ty,
-                    item,
-                    self_ty,
-                    mode,
-                ) {
-                    IsValidCandidate::Yes => true,
-                    IsValidCandidate::NotVisible => false,
-                    IsValidCandidate::No => continue,
-                };
-                callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
-            }
-        }
-        ControlFlow::Continue(())
-    }
-
-    #[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
-    fn impls_for_self_ty<'db>(
-        impls: &InherentImpls,
-        self_ty: Ty<'db>,
-        table: &mut InferenceTable<'db>,
-        name: Option<&Name>,
-        receiver_ty: Option<Ty<'db>>,
-        receiver_adjustments: Option<ReceiverAdjustments>,
-        visible_from_module: Option<ModuleId>,
-        callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
-    ) -> ControlFlow<()> {
-        for &impl_id in impls.for_self_ty(self_ty) {
-            for &(ref item_name, item) in impl_id.impl_items(table.db).items.iter() {
-                let visible = match is_valid_impl_method_candidate(
-                    table,
-                    self_ty,
-                    receiver_ty,
-                    visible_from_module,
-                    name,
-                    impl_id,
-                    item,
-                    item_name,
-                ) {
-                    IsValidCandidate::Yes => true,
-                    IsValidCandidate::NotVisible => false,
-                    IsValidCandidate::No => continue,
-                };
-                callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
-            }
-        }
-        ControlFlow::Continue(())
-    }
-}
-
-/// Returns the receiver type for the index trait call.
-pub(crate) fn resolve_indexing_op<'db>(
-    table: &mut InferenceTable<'db>,
-    ty: Canonical<'db, Ty<'db>>,
-    index_trait: TraitId,
-) -> Option<ReceiverAdjustments> {
-    let ty = table.instantiate_canonical(ty);
-    let deref_chain = autoderef_method_receiver(table, ty);
-    for (ty, adj) in deref_chain {
-        let goal = generic_implements_goal_ns(table, index_trait, ty);
-        if !next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() {
-            return Some(adj);
-        }
-    }
-    None
-}
-
-// FIXME: Replace this with a `Try` impl once stable
-macro_rules! check_that {
-    ($cond:expr) => {
-        if !$cond {
-            return IsValidCandidate::No;
-        }
-    };
-}
-
-#[derive(Debug)]
-enum IsValidCandidate {
-    Yes,
-    No,
-    NotVisible,
-}
-
-#[tracing::instrument(skip_all, fields(name))]
-fn is_valid_impl_method_candidate<'db>(
-    table: &mut InferenceTable<'db>,
-    self_ty: Ty<'db>,
-    receiver_ty: Option<Ty<'db>>,
-    visible_from_module: Option<ModuleId>,
-    name: Option<&Name>,
-    impl_id: ImplId,
-    item: AssocItemId,
-    item_name: &Name,
-) -> IsValidCandidate {
-    match item {
-        AssocItemId::FunctionId(f) => is_valid_impl_fn_candidate(
-            table,
-            impl_id,
-            f,
-            name,
-            receiver_ty,
-            self_ty,
-            visible_from_module,
-            item_name,
-        ),
-        AssocItemId::ConstId(c) => {
-            let db = table.db;
-            check_that!(receiver_ty.is_none());
-            check_that!(name.is_none_or(|n| n == item_name));
-
-            if let Some(from_module) = visible_from_module
-                && !db.assoc_visibility(c.into()).is_visible_from(db, from_module)
-            {
-                cov_mark::hit!(const_candidate_not_visible);
-                return IsValidCandidate::NotVisible;
-            }
-            let self_ty_matches = table.run_in_snapshot(|table| {
-                let impl_args = table.fresh_args_for_item(impl_id.into());
-                let expected_self_ty =
-                    db.impl_self_ty(impl_id).instantiate(table.interner(), impl_args);
-                table.unify(expected_self_ty, self_ty)
-            });
-            if !self_ty_matches {
-                cov_mark::hit!(const_candidate_self_type_mismatch);
-                return IsValidCandidate::No;
-            }
-            IsValidCandidate::Yes
-        }
-        _ => IsValidCandidate::No,
-    }
-}
-
-/// Checks whether a given `AssocItemId` is applicable for `receiver_ty`.
-#[tracing::instrument(skip_all, fields(name))]
-fn is_valid_trait_method_candidate<'db>(
-    table: &mut InferenceTable<'db>,
-    trait_id: TraitId,
-    name: Option<&Name>,
-    receiver_ty: Option<Ty<'db>>,
-    item: AssocItemId,
-    self_ty: Ty<'db>,
-    mode: LookupMode,
-) -> IsValidCandidate {
-    let db = table.db;
-    match item {
-        AssocItemId::FunctionId(fn_id) => {
-            let data = db.function_signature(fn_id);
-
-            check_that!(name.is_none_or(|n| n == &data.name));
-
-            table.run_in_snapshot(|table| {
-                let impl_subst = table.fresh_args_for_item(trait_id.into());
-                let expect_self_ty = impl_subst.type_at(0);
-
-                check_that!(table.unify(expect_self_ty, self_ty));
-
-                if let Some(receiver_ty) = receiver_ty {
-                    check_that!(data.has_self_param());
-
-                    let args = table.fill_rest_fresh_args(fn_id.into(), impl_subst);
-
-                    let sig = db.callable_item_signature(fn_id.into());
-                    let expected_receiver = sig
-                        .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0])
-                        .instantiate(table.interner(), args);
-
-                    // FIXME: Clean up this mess with some context struct like rustc's `ProbeContext`
-                    let variance = match mode {
-                        LookupMode::MethodCall => rustc_type_ir::Variance::Covariant,
-                        LookupMode::Path => rustc_type_ir::Variance::Invariant,
-                    };
-                    let res = table
-                        .infer_ctxt
-                        .at(&ObligationCause::dummy(), table.trait_env.env)
-                        .relate(expected_receiver, variance, receiver_ty);
-                    let Ok(infer_ok) = res else {
-                        return IsValidCandidate::No;
-                    };
-
-                    if !infer_ok.obligations.is_empty() {
-                        let mut ctxt = ObligationCtxt::new(&table.infer_ctxt);
-                        ctxt.register_obligations(infer_ok.into_obligations());
-                        // FIXME: Are we doing this correctly? Probably better to follow rustc more closely.
-                        check_that!(ctxt.try_evaluate_obligations().is_empty());
-                    }
-
-                    check_that!(table.unify(receiver_ty, expected_receiver));
-                }
-
-                IsValidCandidate::Yes
-            })
-        }
-        AssocItemId::ConstId(c) => {
-            check_that!(receiver_ty.is_none());
-            check_that!(name.is_none_or(|n| db.const_signature(c).name.as_ref() == Some(n)));
-
-            IsValidCandidate::Yes
-        }
-        _ => IsValidCandidate::No,
-    }
-}
-
-#[tracing::instrument(skip_all, fields(name))]
-fn is_valid_impl_fn_candidate<'db>(
-    table: &mut InferenceTable<'db>,
-    impl_id: ImplId,
-    fn_id: FunctionId,
-    name: Option<&Name>,
-    receiver_ty: Option<Ty<'db>>,
-    self_ty: Ty<'db>,
-    visible_from_module: Option<ModuleId>,
-    item_name: &Name,
-) -> IsValidCandidate {
-    check_that!(name.is_none_or(|n| n == item_name));
-
-    let db = table.db;
-    let data = db.function_signature(fn_id);
-
-    if let Some(from_module) = visible_from_module
-        && !db.assoc_visibility(fn_id.into()).is_visible_from(db, from_module)
-    {
-        cov_mark::hit!(autoderef_candidate_not_visible);
-        return IsValidCandidate::NotVisible;
-    }
-    table.run_in_snapshot(|table| {
-        let _p = tracing::info_span!("subst_for_def").entered();
-        let impl_subst = table.infer_ctxt.fresh_args_for_item(impl_id.into());
-        let expect_self_ty = db.impl_self_ty(impl_id).instantiate(table.interner(), &impl_subst);
-
-        check_that!(table.unify(expect_self_ty, self_ty));
-
-        if let Some(receiver_ty) = receiver_ty {
-            let _p = tracing::info_span!("check_receiver_ty").entered();
-            check_that!(data.has_self_param());
-
-            let args = table.infer_ctxt.fresh_args_for_item(fn_id.into());
-
-            let sig = db.callable_item_signature(fn_id.into());
-            let expected_receiver = sig
-                .map_bound(|s| s.skip_binder().inputs_and_output.as_slice()[0])
-                .instantiate(table.interner(), args);
-
-            check_that!(table.unify(receiver_ty, expected_receiver));
-        }
-
-        // We need to consider the bounds on the impl to distinguish functions of the same name
-        // for a type.
-        let predicates = db.generic_predicates(impl_id.into());
-        let Some(predicates) = predicates.instantiate(table.interner(), impl_subst) else {
-            return IsValidCandidate::Yes;
-        };
-
-        let mut ctxt = ObligationCtxt::new(&table.infer_ctxt);
-
-        ctxt.register_obligations(predicates.into_iter().map(|p| {
-            PredicateObligation::new(
-                table.interner(),
-                ObligationCause::new(),
-                table.trait_env.env,
-                p.0,
-            )
-        }));
-
-        if ctxt.try_evaluate_obligations().is_empty() {
-            IsValidCandidate::Yes
+        if trait_block == type_block {
+            blocks_iter(trait_block)
+                .filter_map(|block| TraitImpls::for_block(db, block).as_deref())
+                .for_each(for_each);
         } else {
-            IsValidCandidate::No
+            for_each_block(trait_block, type_block).for_each(&mut *for_each);
+            for_each_block(type_block, trait_block).for_each(for_each);
         }
-    })
-}
-
-/// This creates Substs for a trait with the given Self type and type variables
-/// for all other parameters, to query the trait solver with it.
-#[tracing::instrument(skip_all)]
-fn generic_implements_goal_ns<'db>(
-    table: &mut InferenceTable<'db>,
-    trait_: TraitId,
-    self_ty: Canonical<'db, Ty<'db>>,
-) -> Canonical<'db, Goal<'db, Predicate<'db>>> {
-    let args = table.infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_));
-    let self_ty = table.instantiate_canonical(self_ty);
-    let trait_ref =
-        rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args)
-            .with_replaced_self_ty(table.infer_ctxt.interner, self_ty);
-    let goal = Goal::new(table.infer_ctxt.interner, table.trait_env.env, trait_ref);
-
-    table.canonicalize(goal)
-}
-
-fn autoderef_method_receiver<'db>(
-    table: &mut InferenceTable<'db>,
-    ty: Ty<'db>,
-) -> Vec<(Canonical<'db, Ty<'db>>, ReceiverAdjustments)> {
-    let interner = table.interner();
-    let mut deref_chain = Vec::new();
-    let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty).use_receiver_trait();
-    while let Some((ty, derefs)) = autoderef.next() {
-        deref_chain.push((
-            autoderef.table.canonicalize(ty),
-            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((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) =
-        deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables, d.0.max_universe, d.1.clone()))
-    {
-        let unsized_ty = Ty::new_slice(interner, parameters);
-        deref_chain.push((
-            Canonical { max_universe, value: unsized_ty, variables },
-            ReceiverAdjustments { unsize_array: true, ..adj.clone() },
-        ));
-    }
-    deref_chain
 }
diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs
new file mode 100644
index 0000000..9e8791e
--- /dev/null
+++ b/crates/hir-ty/src/method_resolution/confirm.rs
@@ -0,0 +1,616 @@
+//! Confirmation step of method selection, meaning ensuring the selected candidate
+//! is valid and registering all obligations.
+
+use hir_def::{
+    FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId,
+    expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs},
+    hir::{ExprId, generics::GenericParamDataRef},
+    lang_item::LangItem,
+};
+use rustc_type_ir::{
+    TypeFoldable,
+    elaborate::elaborate,
+    inherent::{BoundExistentialPredicates, IntoKind, SliceLike, Ty as _},
+};
+use tracing::debug;
+
+use crate::{
+    Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic,
+    LifetimeElisionKind, PointerCast,
+    db::HirDatabase,
+    infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch},
+    lower::{
+        GenericPredicates,
+        path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
+    },
+    method_resolution::{CandidateId, MethodCallee, probe},
+    next_solver::{
+        Binder, Clause, ClauseKind, Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, FnSig,
+        GenericArg, GenericArgs, ParamConst, PolyExistentialTraitRef, PolyTraitRef, Region,
+        TraitRef, Ty, TyKind,
+        infer::{
+            BoundRegionConversionTime, InferCtxt,
+            traits::{ObligationCause, PredicateObligation},
+        },
+        util::{clauses_as_obligations, upcast_choices},
+    },
+};
+
+struct ConfirmContext<'a, 'b, 'db> {
+    ctx: &'a mut InferenceContext<'b, 'db>,
+    candidate: FunctionId,
+    expr: ExprId,
+}
+
+#[derive(Debug)]
+pub(crate) struct ConfirmResult<'db> {
+    pub(crate) callee: MethodCallee<'db>,
+    pub(crate) illegal_sized_bound: bool,
+    pub(crate) adjustments: Box<[Adjustment<'db>]>,
+}
+
+impl<'a, 'db> InferenceContext<'a, 'db> {
+    pub(crate) fn confirm_method(
+        &mut self,
+        pick: &probe::Pick<'db>,
+        unadjusted_self_ty: Ty<'db>,
+        expr: ExprId,
+        generic_args: Option<&HirGenericArgs>,
+    ) -> ConfirmResult<'db> {
+        debug!(
+            "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
+            unadjusted_self_ty, pick, generic_args,
+        );
+
+        let CandidateId::FunctionId(candidate) = pick.item else {
+            panic!("confirmation is only done for method calls, not path lookups");
+        };
+        let mut confirm_cx = ConfirmContext::new(self, candidate, expr);
+        confirm_cx.confirm(unadjusted_self_ty, pick, generic_args)
+    }
+}
+
+impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
+    fn new(
+        ctx: &'a mut InferenceContext<'b, 'db>,
+        candidate: FunctionId,
+        expr: ExprId,
+    ) -> ConfirmContext<'a, 'b, 'db> {
+        ConfirmContext { ctx, candidate, expr }
+    }
+
+    #[inline]
+    fn db(&self) -> &'db dyn HirDatabase {
+        self.ctx.table.infer_ctxt.interner.db
+    }
+
+    #[inline]
+    fn interner(&self) -> DbInterner<'db> {
+        self.ctx.table.infer_ctxt.interner
+    }
+
+    #[inline]
+    fn infcx(&self) -> &InferCtxt<'db> {
+        &self.ctx.table.infer_ctxt
+    }
+
+    fn confirm(
+        &mut self,
+        unadjusted_self_ty: Ty<'db>,
+        pick: &probe::Pick<'db>,
+        generic_args: Option<&HirGenericArgs>,
+    ) -> ConfirmResult<'db> {
+        // Adjust the self expression the user provided and obtain the adjusted type.
+        let (self_ty, adjustments) = self.adjust_self_ty(unadjusted_self_ty, pick);
+
+        // Create generic args for the method's type parameters.
+        let rcvr_args = self.fresh_receiver_args(self_ty, pick);
+        let all_args = self.instantiate_method_args(generic_args, rcvr_args);
+
+        debug!("rcvr_args={rcvr_args:?}, all_args={all_args:?}");
+
+        // Create the final signature for the method, replacing late-bound regions.
+        let (method_sig, method_predicates) =
+            self.instantiate_method_sig(pick, all_args.as_slice());
+
+        // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
+        // something which derefs to `Self` actually implements the trait and the caller
+        // wanted to make a static dispatch on it but forgot to import the trait.
+        // See test `tests/ui/issues/issue-35976.rs`.
+        //
+        // In that case, we'll error anyway, but we'll also re-run the search with all traits
+        // in scope, and if we find another method which can be used, we'll output an
+        // appropriate hint suggesting to import the trait.
+        let filler_args = GenericArgs::fill_rest(
+            self.interner(),
+            self.candidate.into(),
+            rcvr_args,
+            |index, id, _| match id {
+                GenericParamId::TypeParamId(id) => Ty::new_param(self.interner(), id, index).into(),
+                GenericParamId::ConstParamId(id) => {
+                    Const::new_param(self.interner(), ParamConst { id, index }).into()
+                }
+                GenericParamId::LifetimeParamId(id) => {
+                    Region::new_early_param(self.interner(), EarlyParamRegion { id, index }).into()
+                }
+            },
+        );
+        let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
+            GenericPredicates::query_all(self.db(), self.candidate.into())
+                .iter_instantiated_copied(self.interner(), filler_args.as_slice()),
+        );
+
+        // Unify the (adjusted) self type with what the method expects.
+        //
+        // SUBTLE: if we want good error messages, because of "guessing" while matching
+        // traits, no trait system method can be called before this point because they
+        // could alter our Self-type, except for normalizing the receiver from the
+        // signature (which is also done during probing).
+        let method_sig_rcvr = method_sig.inputs().as_slice()[0];
+        debug!(
+            "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?}",
+            self_ty, method_sig_rcvr, method_sig
+        );
+        self.unify_receivers(self_ty, method_sig_rcvr, pick);
+
+        // Make sure nobody calls `drop()` explicitly.
+        self.check_for_illegal_method_calls();
+
+        // Lint when an item is shadowing a supertrait item.
+        self.lint_shadowed_supertrait_items(pick);
+
+        // Add any trait/regions obligations specified on the method's type parameters.
+        // We won't add these if we encountered an illegal sized bound, so that we can use
+        // a custom error in that case.
+        if !illegal_sized_bound {
+            self.add_obligations(method_sig, all_args, method_predicates);
+        }
+
+        // Create the final `MethodCallee`.
+        let callee = MethodCallee { def_id: self.candidate, args: all_args, sig: method_sig };
+        ConfirmResult { callee, illegal_sized_bound, adjustments }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // ADJUSTMENTS
+
+    fn adjust_self_ty(
+        &mut self,
+        unadjusted_self_ty: Ty<'db>,
+        pick: &probe::Pick<'db>,
+    ) -> (Ty<'db>, Box<[Adjustment<'db>]>) {
+        // Commit the autoderefs by calling `autoderef` again, but this
+        // time writing the results into the various typeck results.
+        let mut autoderef = self.ctx.table.autoderef_with_tracking(unadjusted_self_ty);
+        let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else {
+            return (Ty::new_error(self.interner(), ErrorGuaranteed), Box::new([]));
+        };
+        assert_eq!(n, pick.autoderefs);
+
+        let mut adjustments =
+            self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
+        match pick.autoref_or_ptr_adjustment {
+            Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
+                let region = self.infcx().next_region_var();
+                // Type we're wrapping in a reference, used later for unsizing
+                let base_ty = target;
+
+                target = Ty::new_ref(self.interner(), region, target, mutbl);
+
+                // Method call receivers are the primary use case
+                // for two-phase borrows.
+                let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes);
+
+                adjustments
+                    .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target });
+
+                if unsize {
+                    let unsized_ty = if let TyKind::Array(elem_ty, _) = base_ty.kind() {
+                        Ty::new_slice(self.interner(), elem_ty)
+                    } else {
+                        panic!(
+                            "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {:?}",
+                            base_ty
+                        )
+                    };
+                    target = Ty::new_ref(self.interner(), region, unsized_ty, mutbl.into());
+                    adjustments
+                        .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
+                }
+            }
+            Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
+                target = match target.kind() {
+                    TyKind::RawPtr(ty, mutbl) => {
+                        assert!(mutbl.is_mut());
+                        Ty::new_imm_ptr(self.interner(), ty)
+                    }
+                    other => panic!("Cannot adjust receiver type {other:?} to const ptr"),
+                };
+
+                adjustments.push(Adjustment {
+                    kind: Adjust::Pointer(PointerCast::MutToConstPointer),
+                    target,
+                });
+            }
+            None => {}
+        }
+
+        (target, adjustments.into_boxed_slice())
+    }
+
+    /// Returns a set of generic parameters for the method *receiver* where all type and region
+    /// parameters are instantiated with fresh variables. This generic parameters does not include any
+    /// parameters declared on the method itself.
+    ///
+    /// Note that this generic parameters may include late-bound regions from the impl level. If so,
+    /// these are instantiated later in the `instantiate_method_sig` routine.
+    fn fresh_receiver_args(
+        &mut self,
+        self_ty: Ty<'db>,
+        pick: &probe::Pick<'db>,
+    ) -> GenericArgs<'db> {
+        match pick.kind {
+            probe::InherentImplPick(impl_def_id) => {
+                self.infcx().fresh_args_for_item(impl_def_id.into())
+            }
+
+            probe::ObjectPick(trait_def_id) => {
+                // If the trait is not object safe (specifically, we care about when
+                // the receiver is not valid), then there's a chance that we will not
+                // actually be able to recover the object by derefing the receiver like
+                // we should if it were valid.
+                if self.db().dyn_compatibility_of_trait(trait_def_id).is_some() {
+                    return GenericArgs::error_for_item(self.interner(), trait_def_id.into());
+                }
+
+                self.extract_existential_trait_ref(self_ty, |this, object_ty, principal| {
+                    // The object data has no entry for the Self
+                    // Type. For the purposes of this method call, we
+                    // instantiate the object type itself. This
+                    // wouldn't be a sound instantiation in all cases,
+                    // since each instance of the object type is a
+                    // different existential and hence could match
+                    // distinct types (e.g., if `Self` appeared as an
+                    // argument type), but those cases have already
+                    // been ruled out when we deemed the trait to be
+                    // "dyn-compatible".
+                    let original_poly_trait_ref =
+                        principal.with_self_ty(this.interner(), object_ty);
+                    let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id);
+                    let upcast_trait_ref =
+                        this.instantiate_binder_with_fresh_vars(upcast_poly_trait_ref);
+                    debug!(
+                        "original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}",
+                        original_poly_trait_ref, upcast_trait_ref, trait_def_id
+                    );
+                    upcast_trait_ref.args
+                })
+            }
+
+            probe::TraitPick(trait_def_id) => {
+                // Make a trait reference `$0 : Trait<$1...$n>`
+                // consisting entirely of type variables. Later on in
+                // the process we will unify the transformed-self-type
+                // of the method with the actual type in order to
+                // unify some of these variables.
+                self.infcx().fresh_args_for_item(trait_def_id.into())
+            }
+
+            probe::WhereClausePick(poly_trait_ref) => {
+                // Where clauses can have bound regions in them. We need to instantiate
+                // those to convert from a poly-trait-ref to a trait-ref.
+                self.instantiate_binder_with_fresh_vars(poly_trait_ref).args
+            }
+        }
+    }
+
+    fn extract_existential_trait_ref<R, F>(&self, self_ty: Ty<'db>, mut closure: F) -> R
+    where
+        F: FnMut(&ConfirmContext<'a, 'b, 'db>, Ty<'db>, PolyExistentialTraitRef<'db>) -> R,
+    {
+        // If we specified that this is an object method, then the
+        // self-type ought to be something that can be dereferenced to
+        // yield an object-type (e.g., `&Object` or `Box<Object>`
+        // etc).
+
+        let mut autoderef = self.ctx.table.autoderef(self_ty);
+
+        // We don't need to gate this behind arbitrary self types
+        // per se, but it does make things a bit more gated.
+        if self.ctx.unstable_features.arbitrary_self_types
+            || self.ctx.unstable_features.arbitrary_self_types_pointers
+        {
+            autoderef = autoderef.use_receiver_trait();
+        }
+
+        autoderef
+            .include_raw_pointers()
+            .find_map(|(ty, _)| match ty.kind() {
+                TyKind::Dynamic(data, ..) => Some(closure(
+                    self,
+                    ty,
+                    data.principal().expect("calling trait method on empty object?"),
+                )),
+                _ => None,
+            })
+            .unwrap_or_else(|| {
+                panic!("self-type `{:?}` for ObjectPick never dereferenced to an object", self_ty)
+            })
+    }
+
+    fn instantiate_method_args(
+        &mut self,
+        generic_args: Option<&HirGenericArgs>,
+        parent_args: GenericArgs<'db>,
+    ) -> GenericArgs<'db> {
+        struct LowererCtx<'a, 'b, 'db> {
+            ctx: &'a mut InferenceContext<'b, 'db>,
+            expr: ExprId,
+            parent_args: &'a [GenericArg<'db>],
+        }
+
+        impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, 'db> {
+            fn report_len_mismatch(
+                &mut self,
+                def: GenericDefId,
+                provided_count: u32,
+                expected_count: u32,
+                kind: IncorrectGenericsLenKind,
+            ) {
+                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsLen {
+                    expr: self.expr,
+                    provided_count,
+                    expected_count,
+                    kind,
+                    def,
+                });
+            }
+
+            fn report_arg_mismatch(
+                &mut self,
+                param_id: GenericParamId,
+                arg_idx: u32,
+                has_self_arg: bool,
+            ) {
+                self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
+                    expr: self.expr,
+                    param_id,
+                    arg_idx,
+                    has_self_arg,
+                });
+            }
+
+            fn provided_kind(
+                &mut self,
+                param_id: GenericParamId,
+                param: GenericParamDataRef<'_>,
+                arg: &HirGenericArg,
+            ) -> GenericArg<'db> {
+                match (param, arg) {
+                    (
+                        GenericParamDataRef::LifetimeParamData(_),
+                        HirGenericArg::Lifetime(lifetime),
+                    ) => self.ctx.make_body_lifetime(*lifetime).into(),
+                    (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => {
+                        self.ctx.make_body_ty(*type_ref).into()
+                    }
+                    (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => {
+                        let GenericParamId::ConstParamId(const_id) = param_id else {
+                            unreachable!("non-const param ID for const param");
+                        };
+                        let const_ty = self.ctx.db.const_param_ty_ns(const_id);
+                        self.ctx.make_body_const(*konst, const_ty).into()
+                    }
+                    _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
+                }
+            }
+
+            fn provided_type_like_const(
+                &mut self,
+                const_ty: Ty<'db>,
+                arg: TypeLikeConst<'_>,
+            ) -> Const<'db> {
+                match arg {
+                    TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
+                    TypeLikeConst::Infer => self.ctx.table.next_const_var(),
+                }
+            }
+
+            fn inferred_kind(
+                &mut self,
+                _def: GenericDefId,
+                param_id: GenericParamId,
+                _param: GenericParamDataRef<'_>,
+                _infer_args: bool,
+                _preceding_args: &[GenericArg<'db>],
+            ) -> GenericArg<'db> {
+                // Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
+                // and I think it's also required in the presence of `impl Trait` (that must be inferred).
+                self.ctx.table.next_var_for_param(param_id)
+            }
+
+            fn parent_arg(&mut self, param_idx: u32, _param_id: GenericParamId) -> GenericArg<'db> {
+                self.parent_args[param_idx as usize]
+            }
+
+            fn report_elided_lifetimes_in_path(
+                &mut self,
+                _def: GenericDefId,
+                _expected_count: u32,
+                _hard_error: bool,
+            ) {
+                unreachable!("we set `LifetimeElisionKind::Infer`")
+            }
+
+            fn report_elision_failure(&mut self, _def: GenericDefId, _expected_count: u32) {
+                unreachable!("we set `LifetimeElisionKind::Infer`")
+            }
+
+            fn report_missing_lifetime(&mut self, _def: GenericDefId, _expected_count: u32) {
+                unreachable!("we set `LifetimeElisionKind::Infer`")
+            }
+        }
+
+        substs_from_args_and_bindings(
+            self.db(),
+            self.ctx.body,
+            generic_args,
+            self.candidate.into(),
+            true,
+            LifetimeElisionKind::Infer,
+            false,
+            None,
+            &mut LowererCtx { ctx: self.ctx, expr: self.expr, parent_args: parent_args.as_slice() },
+        )
+    }
+
+    fn unify_receivers(
+        &mut self,
+        self_ty: Ty<'db>,
+        method_self_ty: Ty<'db>,
+        pick: &probe::Pick<'db>,
+    ) {
+        debug!(
+            "unify_receivers: self_ty={:?} method_self_ty={:?} pick={:?}",
+            self_ty, method_self_ty, pick
+        );
+        let cause = ObligationCause::new();
+        match self.ctx.table.at(&cause).sup(method_self_ty, self_ty) {
+            Ok(infer_ok) => {
+                self.ctx.table.register_infer_ok(infer_ok);
+            }
+            Err(_) => {
+                if self.ctx.unstable_features.arbitrary_self_types {
+                    self.ctx.result.type_mismatches.insert(
+                        self.expr.into(),
+                        TypeMismatch { expected: method_self_ty, actual: self_ty },
+                    );
+                }
+            }
+        }
+    }
+
+    // NOTE: this returns the *unnormalized* predicates and method sig. Because of
+    // inference guessing, the predicates and method signature can't be normalized
+    // until we unify the `Self` type.
+    fn instantiate_method_sig<'c>(
+        &mut self,
+        pick: &probe::Pick<'db>,
+        all_args: &'c [GenericArg<'db>],
+    ) -> (FnSig<'db>, impl Iterator<Item = PredicateObligation<'db>> + use<'c, 'db>) {
+        debug!("instantiate_method_sig(pick={:?}, all_args={:?})", pick, all_args);
+
+        // Instantiate the bounds on the method with the
+        // type/early-bound-regions instantiations performed. There can
+        // be no late-bound regions appearing here.
+        let def_id = self.candidate;
+        let method_predicates = clauses_as_obligations(
+            GenericPredicates::query_all(self.db(), def_id.into())
+                .iter_instantiated_copied(self.interner(), all_args),
+            ObligationCause::new(),
+            self.ctx.table.trait_env.env,
+        );
+
+        let sig =
+            self.db().callable_item_signature(def_id.into()).instantiate(self.interner(), all_args);
+        debug!("type scheme instantiated, sig={:?}", sig);
+
+        let sig = self.instantiate_binder_with_fresh_vars(sig);
+        debug!("late-bound lifetimes from method instantiated, sig={:?}", sig);
+
+        (sig, method_predicates)
+    }
+
+    fn add_obligations(
+        &mut self,
+        sig: FnSig<'db>,
+        all_args: GenericArgs<'db>,
+        method_predicates: impl Iterator<Item = PredicateObligation<'db>>,
+    ) {
+        debug!("add_obligations: sig={:?} all_args={:?}", sig, all_args);
+
+        self.ctx.table.register_predicates(method_predicates);
+
+        // this is a projection from a trait reference, so we have to
+        // make sure that the trait reference inputs are well-formed.
+        self.ctx.table.add_wf_bounds(all_args);
+
+        // the function type must also be well-formed (this is not
+        // implied by the args being well-formed because of inherent
+        // impls and late-bound regions - see issue #28609).
+        for ty in sig.inputs_and_output {
+            self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new());
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // MISCELLANY
+
+    fn predicates_require_illegal_sized_bound(
+        &self,
+        predicates: impl Iterator<Item = Clause<'db>>,
+    ) -> bool {
+        let Some(sized_def_id) =
+            LangItem::Sized.resolve_trait(self.db(), self.ctx.resolver.krate())
+        else {
+            return false;
+        };
+
+        elaborate(self.interner(), predicates)
+            // We don't care about regions here.
+            .filter_map(|pred| match pred.kind().skip_binder() {
+                ClauseKind::Trait(trait_pred) if trait_pred.def_id().0 == sized_def_id => {
+                    Some(trait_pred)
+                }
+                _ => None,
+            })
+            .any(|trait_pred| matches!(trait_pred.self_ty().kind(), TyKind::Dynamic(..)))
+    }
+
+    fn check_for_illegal_method_calls(&self) {
+        // Disallow calls to the method `drop` defined in the `Drop` trait.
+        if let ItemContainerId::TraitId(trait_def_id) = self.candidate.loc(self.db()).container
+            && LangItem::Drop
+                .resolve_trait(self.db(), self.ctx.resolver.krate())
+                .is_some_and(|drop_trait| drop_trait == trait_def_id)
+        {
+            // FIXME: Report an error.
+        }
+    }
+
+    #[expect(clippy::needless_return)]
+    fn lint_shadowed_supertrait_items(&self, pick: &probe::Pick<'_>) {
+        if pick.shadowed_candidates.is_empty() {
+            return;
+        }
+
+        // FIXME: Emit the lint.
+    }
+
+    fn upcast(
+        &self,
+        source_trait_ref: PolyTraitRef<'db>,
+        target_trait_def_id: TraitId,
+    ) -> PolyTraitRef<'db> {
+        let upcast_trait_refs =
+            upcast_choices(self.interner(), source_trait_ref, target_trait_def_id);
+
+        // must be exactly one trait ref or we'd get an ambig error etc
+        if let &[upcast_trait_ref] = upcast_trait_refs.as_slice() {
+            upcast_trait_ref
+        } else {
+            Binder::dummy(TraitRef::new_from_args(
+                self.interner(),
+                target_trait_def_id.into(),
+                GenericArgs::error_for_item(self.interner(), target_trait_def_id.into()),
+            ))
+        }
+    }
+
+    fn instantiate_binder_with_fresh_vars<T>(&self, value: Binder<'db, T>) -> T
+    where
+        T: TypeFoldable<DbInterner<'db>> + Copy,
+    {
+        self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, value)
+    }
+}
diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs
new file mode 100644
index 0000000..adc144c
--- /dev/null
+++ b/crates/hir-ty/src/method_resolution/probe.rs
@@ -0,0 +1,2077 @@
+//! Candidate assembly and selection in method resolution - where we enumerate all candidates
+//! and choose the best one (or, in some IDE scenarios, just enumerate them all).
+
+use std::{cell::RefCell, convert::Infallible, ops::ControlFlow};
+
+use hir_def::{
+    AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId,
+    signatures::TraitFlags,
+};
+use hir_expand::name::Name;
+use rustc_ast_ir::Mutability;
+use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_type_ir::{
+    InferTy, TypeVisitableExt, Upcast, Variance,
+    elaborate::{self, supertrait_def_ids},
+    fast_reject::{DeepRejectCtxt, TreatParams, simplify_type},
+    inherent::{AdtDef as _, BoundExistentialPredicates as _, IntoKind, SliceLike, Ty as _},
+};
+use smallvec::{SmallVec, smallvec};
+use tracing::{debug, instrument};
+
+use self::CandidateKind::*;
+pub(super) use self::PickKind::*;
+use crate::{
+    autoderef::Autoderef,
+    db::HirDatabase,
+    lower::GenericPredicates,
+    method_resolution::{
+        CandidateId, CandidateSource, InherentImpls, MethodError, MethodResolutionContext,
+        incoherent_inherent_impls, simplified_type_module,
+    },
+    next_solver::{
+        Binder, Canonical, ClauseKind, DbInterner, FnSig, GenericArg, GenericArgs, Goal, ParamEnv,
+        PolyTraitRef, Predicate, Region, SimplifiedType, TraitRef, Ty, TyKind,
+        infer::{
+            BoundRegionConversionTime, InferCtxt, InferOk,
+            canonical::{QueryResponse, canonicalizer::OriginalQueryValues},
+            select::{ImplSource, Selection, SelectionResult},
+            traits::{Obligation, ObligationCause, PredicateObligation},
+        },
+        obligation_ctxt::ObligationCtxt,
+        util::clauses_as_obligations,
+    },
+};
+
+struct ProbeContext<'a, 'db, Choice> {
+    ctx: &'a MethodResolutionContext<'a, 'db>,
+    mode: Mode,
+
+    /// This is the OriginalQueryValues for the steps queries
+    /// that are answered in steps.
+    orig_steps_var_values: &'a OriginalQueryValues<'db>,
+    steps: &'a [CandidateStep<'db>],
+
+    inherent_candidates: Vec<Candidate<'db>>,
+    extension_candidates: Vec<Candidate<'db>>,
+    impl_dups: FxHashSet<ImplId>,
+
+    /// List of potential private candidates. Will be trimmed to ones that
+    /// actually apply and then the result inserted into `private_candidate`
+    private_candidates: Vec<Candidate<'db>>,
+
+    /// Collects near misses when the candidate functions are missing a `self` keyword and is only
+    /// used for error reporting
+    static_candidates: Vec<CandidateSource>,
+
+    choice: Choice,
+}
+
+#[derive(Debug)]
+pub struct CandidateWithPrivate<'db> {
+    pub candidate: Candidate<'db>,
+    pub is_visible: bool,
+}
+
+#[derive(Debug, Clone)]
+pub struct Candidate<'db> {
+    pub item: CandidateId,
+    pub kind: CandidateKind<'db>,
+}
+
+#[derive(Debug, Clone)]
+pub enum CandidateKind<'db> {
+    InherentImplCandidate { impl_def_id: ImplId, receiver_steps: usize },
+    ObjectCandidate(PolyTraitRef<'db>),
+    TraitCandidate(PolyTraitRef<'db>),
+    WhereClauseCandidate(PolyTraitRef<'db>),
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum ProbeResult {
+    NoMatch,
+    Match,
+}
+
+/// When adjusting a receiver we often want to do one of
+///
+/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`)
+/// - If the receiver has type `*mut T`, convert it to `*const T`
+///
+/// This type tells us which one to do.
+///
+/// Note that in principle we could do both at the same time. For example, when the receiver has
+/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut
+/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do
+/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with
+/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum AutorefOrPtrAdjustment {
+    /// Receiver has type `T`, add `&` or `&mut` (if `T` is `mut`), and maybe also "unsize" it.
+    /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
+    Autoref {
+        mutbl: Mutability,
+
+        /// Indicates that the source expression should be "unsized" to a target type.
+        /// This is special-cased for just arrays unsizing to slices.
+        unsize: bool,
+    },
+    /// Receiver has type `*mut T`, convert to `*const T`
+    ToConstPtr,
+}
+
+impl AutorefOrPtrAdjustment {
+    fn get_unsize(&self) -> bool {
+        match self {
+            AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
+            AutorefOrPtrAdjustment::ToConstPtr => false,
+        }
+    }
+}
+
+/// Criteria to apply when searching for a given Pick. This is used during
+/// the search for potentially shadowed methods to ensure we don't search
+/// more candidates than strictly necessary.
+#[derive(Debug)]
+struct PickConstraintsForShadowed {
+    autoderefs: usize,
+    receiver_steps: Option<usize>,
+    def_id: CandidateId,
+}
+
+impl PickConstraintsForShadowed {
+    fn may_shadow_based_on_autoderefs(&self, autoderefs: usize) -> bool {
+        autoderefs == self.autoderefs
+    }
+
+    fn candidate_may_shadow(&self, candidate: &Candidate<'_>) -> bool {
+        // An item never shadows itself
+        candidate.item != self.def_id
+            // and we're only concerned about inherent impls doing the shadowing.
+            // Shadowing can only occur if the shadowed is further along
+            // the Receiver dereferencing chain than the shadowed.
+            && match candidate.kind {
+                CandidateKind::InherentImplCandidate { receiver_steps, .. } => match self.receiver_steps {
+                    Some(shadowed_receiver_steps) => receiver_steps > shadowed_receiver_steps,
+                    _ => false
+                },
+                _ => false
+            }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Pick<'db> {
+    pub item: CandidateId,
+    pub kind: PickKind<'db>,
+
+    /// Indicates that the source expression should be autoderef'd N times
+    /// ```ignore (not-rust)
+    /// A = expr | *expr | **expr | ...
+    /// ```
+    pub autoderefs: usize,
+
+    /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is
+    /// `*mut T`, convert it to `*const T`.
+    pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>,
+    pub self_ty: Ty<'db>,
+
+    /// Number of jumps along the `Receiver::Target` chain we followed
+    /// to identify this method. Used only for deshadowing errors.
+    /// Only applies for inherent impls.
+    pub receiver_steps: Option<usize>,
+
+    /// Candidates that were shadowed by supertraits.
+    pub shadowed_candidates: Vec<CandidateId>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum PickKind<'db> {
+    InherentImplPick(ImplId),
+    ObjectPick(TraitId),
+    TraitPick(TraitId),
+    WhereClausePick(
+        // Trait
+        PolyTraitRef<'db>,
+    ),
+}
+
+pub(crate) type PickResult<'db> = Result<Pick<'db>, MethodError<'db>>;
+
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+pub enum Mode {
+    // An expression of the form `receiver.method_name(...)`.
+    // Autoderefs are performed on `receiver`, lookup is done based on the
+    // `self` argument of the method, and static methods aren't considered.
+    MethodCall,
+    // An expression of the form `Type::item` or `<T>::item`.
+    // No autoderefs are performed, lookup is done based on the type each
+    // implementation is for, and static methods are included.
+    Path,
+}
+
+#[derive(Debug, Clone)]
+pub struct CandidateStep<'db> {
+    pub self_ty: Canonical<'db, QueryResponse<'db, Ty<'db>>>,
+    pub self_ty_is_opaque: bool,
+    pub autoderefs: usize,
+    /// `true` if the type results from a dereference of a raw pointer.
+    /// when assembling candidates, we include these steps, but not when
+    /// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
+    /// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
+    /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
+    pub from_unsafe_deref: bool,
+    pub unsize: bool,
+    /// We will generate CandidateSteps which are reachable via a chain
+    /// of following `Receiver`. The first 'n' of those will be reachable
+    /// by following a chain of 'Deref' instead (since there's a blanket
+    /// implementation of Receiver for Deref).
+    /// We use the entire set of steps when identifying method candidates
+    /// (e.g. identifying relevant `impl` blocks) but only those that are
+    /// reachable via Deref when examining what the receiver type can
+    /// be converted into by autodereffing.
+    pub reachable_via_deref: bool,
+}
+
+#[derive(Clone, Debug)]
+struct MethodAutoderefStepsResult<'db> {
+    /// The valid autoderef steps that could be found by following a chain
+    /// of `Receiver<Target=T>` or `Deref<Target=T>` trait implementations.
+    pub steps: SmallVec<[CandidateStep<'db>; 3]>,
+    /// If Some(T), a type autoderef reported an error on.
+    pub opt_bad_ty: Option<MethodAutoderefBadTy<'db>>,
+    /// If `true`, `steps` has been truncated due to reaching the
+    /// recursion limit.
+    pub reached_recursion_limit: bool,
+}
+
+#[derive(Debug, Clone)]
+struct MethodAutoderefBadTy<'db> {
+    pub reached_raw_pointer: bool,
+    pub ty: Canonical<'db, QueryResponse<'db, Ty<'db>>>,
+}
+
+impl<'a, 'db> MethodResolutionContext<'a, 'db> {
+    #[instrument(level = "debug", skip(self))]
+    pub fn probe_for_name(&self, mode: Mode, item_name: Name, self_ty: Ty<'db>) -> PickResult<'db> {
+        self.probe_op(mode, self_ty, ProbeForNameChoice { private_candidate: None, item_name })
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub fn probe_all(
+        &self,
+        mode: Mode,
+        self_ty: Ty<'db>,
+    ) -> impl Iterator<Item = CandidateWithPrivate<'db>> {
+        self.probe_op(mode, self_ty, ProbeAllChoice::new()).candidates.into_inner().into_values()
+    }
+
+    fn probe_op<Choice: ProbeChoice<'db>>(
+        &self,
+        mode: Mode,
+        self_ty: Ty<'db>,
+        choice: Choice,
+    ) -> Choice::FinalChoice {
+        let mut orig_values = OriginalQueryValues::default();
+        let query_input = self.infcx.canonicalize_query(self_ty, &mut orig_values);
+        let steps = match mode {
+            Mode::MethodCall => self.method_autoderef_steps(&query_input),
+            Mode::Path => self.infcx.probe(|_| {
+                // Mode::Path - the deref steps is "trivial". This turns
+                // our CanonicalQuery into a "trivial" QueryResponse. This
+                // is a bit inefficient, but I don't think that writing
+                // special handling for this "trivial case" is a good idea.
+
+                let infcx = self.infcx;
+                let (self_ty, var_values) = infcx.instantiate_canonical(&query_input);
+                debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
+                MethodAutoderefStepsResult {
+                    steps: smallvec![CandidateStep {
+                        self_ty: self
+                            .infcx
+                            .make_query_response_ignoring_pending_obligations(var_values, self_ty),
+                        self_ty_is_opaque: false,
+                        autoderefs: 0,
+                        from_unsafe_deref: false,
+                        unsize: false,
+                        reachable_via_deref: true,
+                    }],
+                    opt_bad_ty: None,
+                    reached_recursion_limit: false,
+                }
+            }),
+        };
+
+        if steps.reached_recursion_limit {
+            // FIXME: Report an error.
+        }
+
+        // If we encountered an `_` type or an error type during autoderef, this is
+        // ambiguous.
+        if let Some(bad_ty) = &steps.opt_bad_ty {
+            if bad_ty.reached_raw_pointer
+                && !self.unstable_features.arbitrary_self_types_pointers
+                && self.edition.at_least_2018()
+            {
+                // this case used to be allowed by the compiler,
+                // so we do a future-compat lint here for the 2015 edition
+                // (see https://github.com/rust-lang/rust/issues/46906)
+                // FIXME: Emit the lint.
+                // self.tcx.node_span_lint(
+                //     lint::builtin::TYVAR_BEHIND_RAW_POINTER,
+                //     scope_expr_id,
+                //     span,
+                //     |lint| {
+                //         lint.primary_message("type annotations needed");
+                //     },
+                // );
+            } else {
+                // Ended up encountering a type variable when doing autoderef,
+                // but it may not be a type variable after processing obligations
+                // in our local `FnCtxt`, so don't call `structurally_resolve_type`.
+                let ty = &bad_ty.ty;
+                let ty = self
+                    .infcx
+                    .instantiate_query_response_and_region_obligations(
+                        &ObligationCause::new(),
+                        self.env.env,
+                        &orig_values,
+                        ty,
+                    )
+                    .unwrap_or_else(|_| panic!("instantiating {:?} failed?", ty));
+                let ty = self.infcx.resolve_vars_if_possible(ty.value);
+                match ty.kind() {
+                    TyKind::Infer(InferTy::TyVar(_)) => {
+                        // FIXME: Report "type annotations needed" error.
+                    }
+                    TyKind::Error(_) => {}
+                    _ => panic!("unexpected bad final type in method autoderef"),
+                };
+                return Choice::final_choice_from_err(MethodError::ErrorReported);
+            }
+        }
+
+        debug!("ProbeContext: steps for self_ty={:?} are {:?}", self_ty, steps);
+
+        // this creates one big transaction so that all type variables etc
+        // that we create during the probe process are removed later
+        self.infcx.probe(|_| {
+            let mut probe_cx = ProbeContext::new(self, mode, &orig_values, &steps.steps, choice);
+
+            probe_cx.assemble_inherent_candidates();
+            probe_cx.assemble_extension_candidates_for_traits_in_scope();
+            Choice::choose(probe_cx)
+        })
+    }
+
+    fn method_autoderef_steps(
+        &self,
+        self_ty: &Canonical<'db, Ty<'db>>,
+    ) -> MethodAutoderefStepsResult<'db> {
+        self.infcx.probe(|_| {
+            debug!("method_autoderef_steps({:?})", self_ty);
+
+            // We accept not-yet-defined opaque types in the autoderef
+            // chain to support recursive calls. We do error if the final
+            // infer var is not an opaque.
+            let infcx = self.infcx;
+            let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty);
+            let self_ty_is_opaque = |ty: Ty<'_>| {
+                if let TyKind::Infer(InferTy::TyVar(vid)) = ty.kind() {
+                    infcx.has_opaques_with_sub_unified_hidden_type(vid)
+                } else {
+                    false
+                }
+            };
+
+            // If arbitrary self types is not enabled, we follow the chain of
+            // `Deref<Target=T>`. If arbitrary self types is enabled, we instead
+            // follow the chain of `Receiver<Target=T>`, but we also record whether
+            // such types are reachable by following the (potentially shorter)
+            // chain of `Deref<Target=T>`. We will use the first list when finding
+            // potentially relevant function implementations (e.g. relevant impl blocks)
+            // but the second list when determining types that the receiver may be
+            // converted to, in order to find out which of those methods might actually
+            // be callable.
+            let mut autoderef_via_deref =
+                Autoderef::new(infcx, self.env, self_ty).include_raw_pointers();
+
+            let mut reached_raw_pointer = false;
+            let arbitrary_self_types_enabled = self.unstable_features.arbitrary_self_types
+                || self.unstable_features.arbitrary_self_types_pointers;
+            let (mut steps, reached_recursion_limit) = if arbitrary_self_types_enabled {
+                let reachable_via_deref =
+                    autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false));
+
+                let mut autoderef_via_receiver = Autoderef::new(infcx, self.env, self_ty)
+                    .include_raw_pointers()
+                    .use_receiver_trait();
+                let steps = autoderef_via_receiver
+                    .by_ref()
+                    .zip(reachable_via_deref)
+                    .map(|((ty, d), reachable_via_deref)| {
+                        let step = CandidateStep {
+                            self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                                inference_vars,
+                                ty,
+                            ),
+                            self_ty_is_opaque: self_ty_is_opaque(ty),
+                            autoderefs: d,
+                            from_unsafe_deref: reached_raw_pointer,
+                            unsize: false,
+                            reachable_via_deref,
+                        };
+                        if ty.is_raw_ptr() {
+                            // all the subsequent steps will be from_unsafe_deref
+                            reached_raw_pointer = true;
+                        }
+                        step
+                    })
+                    .collect::<SmallVec<[_; _]>>();
+                (steps, autoderef_via_receiver.reached_recursion_limit())
+            } else {
+                let steps = autoderef_via_deref
+                    .by_ref()
+                    .map(|(ty, d)| {
+                        let step = CandidateStep {
+                            self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                                inference_vars,
+                                ty,
+                            ),
+                            self_ty_is_opaque: self_ty_is_opaque(ty),
+                            autoderefs: d,
+                            from_unsafe_deref: reached_raw_pointer,
+                            unsize: false,
+                            reachable_via_deref: true,
+                        };
+                        if ty.is_raw_ptr() {
+                            // all the subsequent steps will be from_unsafe_deref
+                            reached_raw_pointer = true;
+                        }
+                        step
+                    })
+                    .collect();
+                (steps, autoderef_via_deref.reached_recursion_limit())
+            };
+            let final_ty = autoderef_via_deref.final_ty();
+            let opt_bad_ty = match final_ty.kind() {
+                TyKind::Infer(InferTy::TyVar(_)) if !self_ty_is_opaque(final_ty) => {
+                    Some(MethodAutoderefBadTy {
+                        reached_raw_pointer,
+                        ty: infcx.make_query_response_ignoring_pending_obligations(
+                            inference_vars,
+                            final_ty,
+                        ),
+                    })
+                }
+                TyKind::Error(_) => Some(MethodAutoderefBadTy {
+                    reached_raw_pointer,
+                    ty: infcx
+                        .make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+                }),
+                TyKind::Array(elem_ty, _) => {
+                    let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1;
+                    steps.push(CandidateStep {
+                        self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                            inference_vars,
+                            Ty::new_slice(infcx.interner, elem_ty),
+                        ),
+                        self_ty_is_opaque: false,
+                        autoderefs,
+                        // this could be from an unsafe deref if we had
+                        // a *mut/const [T; N]
+                        from_unsafe_deref: reached_raw_pointer,
+                        unsize: true,
+                        reachable_via_deref: true, // this is always the final type from
+                                                   // autoderef_via_deref
+                    });
+
+                    None
+                }
+                _ => None,
+            };
+
+            debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+            MethodAutoderefStepsResult { steps, opt_bad_ty, reached_recursion_limit }
+        })
+    }
+}
+
+trait ProbeChoice<'db>: Sized {
+    type Choice;
+    type FinalChoice;
+
+    /// Finds the method with the appropriate name (or return type, as the case may be).
+    // The length of the returned iterator is nearly always 0 or 1 and this
+    // method is fairly hot.
+    fn with_impl_or_trait_item<'a>(
+        this: &mut ProbeContext<'a, 'db, Self>,
+        items: &[(Name, AssocItemId)],
+        callback: impl FnMut(&mut ProbeContext<'a, 'db, Self>, CandidateId),
+    );
+
+    fn consider_candidates(
+        this: &ProbeContext<'_, 'db, Self>,
+        self_ty: Ty<'db>,
+        candidates: Vec<&Candidate<'db>>,
+    ) -> ControlFlow<Self::Choice>;
+
+    fn consider_private_candidates(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    );
+
+    fn map_choice_pick(
+        choice: Self::Choice,
+        f: impl FnOnce(Pick<'db>) -> Pick<'db>,
+    ) -> Self::Choice;
+
+    fn check_by_value_method_shadowing(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        by_value_pick: &Self::Choice,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice>;
+
+    fn check_autorefed_method_shadowing(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        autoref_pick: &Self::Choice,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice>;
+
+    fn final_choice_from_err(err: MethodError<'db>) -> Self::FinalChoice;
+
+    fn choose(this: ProbeContext<'_, 'db, Self>) -> Self::FinalChoice;
+}
+
+#[derive(Debug)]
+struct ProbeForNameChoice<'db> {
+    item_name: Name,
+
+    /// Some(candidate) if there is a private candidate
+    private_candidate: Option<Pick<'db>>,
+}
+
+impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> {
+    type Choice = PickResult<'db>;
+    type FinalChoice = PickResult<'db>;
+
+    fn with_impl_or_trait_item<'a>(
+        this: &mut ProbeContext<'a, 'db, Self>,
+        items: &[(Name, AssocItemId)],
+        mut callback: impl FnMut(&mut ProbeContext<'a, 'db, Self>, CandidateId),
+    ) {
+        let item = items
+            .iter()
+            .filter_map(|(name, id)| {
+                let id = match *id {
+                    AssocItemId::FunctionId(id) => id.into(),
+                    AssocItemId::ConstId(id) => id.into(),
+                    AssocItemId::TypeAliasId(_) => return None,
+                };
+                Some((name, id))
+            })
+            .find(|(name, _)| **name == this.choice.item_name)
+            .map(|(_, id)| id)
+            .filter(|id| this.mode == Mode::Path || matches!(id, CandidateId::FunctionId(_)));
+        if let Some(item) = item {
+            callback(this, item);
+        }
+    }
+
+    fn consider_candidates(
+        this: &ProbeContext<'_, 'db, Self>,
+        self_ty: Ty<'db>,
+        mut applicable_candidates: Vec<&Candidate<'db>>,
+    ) -> ControlFlow<Self::Choice> {
+        if applicable_candidates.len() > 1
+            && let Some(pick) =
+                this.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
+        {
+            return ControlFlow::Break(Ok(pick));
+        }
+
+        if applicable_candidates.len() > 1 {
+            // We collapse to a subtrait pick *after* filtering unstable candidates
+            // to make sure we don't prefer a unstable subtrait method over a stable
+            // supertrait method.
+            if this.ctx.unstable_features.supertrait_item_shadowing
+                && let Some(pick) =
+                    this.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
+            {
+                return ControlFlow::Break(Ok(pick));
+            }
+
+            let sources =
+                applicable_candidates.iter().map(|p| this.candidate_source(p, self_ty)).collect();
+            return ControlFlow::Break(Err(MethodError::Ambiguity(sources)));
+        }
+
+        match applicable_candidates.pop() {
+            Some(probe) => ControlFlow::Break(Ok(probe.to_unadjusted_pick(self_ty))),
+            None => ControlFlow::Continue(()),
+        }
+    }
+
+    fn consider_private_candidates(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) {
+        if this.choice.private_candidate.is_none()
+            && let ControlFlow::Break(Ok(pick)) = this.consider_candidates(
+                self_ty,
+                instantiate_self_ty_obligations,
+                &this.private_candidates,
+                None,
+            )
+        {
+            this.choice.private_candidate = Some(pick);
+        }
+    }
+
+    fn map_choice_pick(
+        choice: Self::Choice,
+        f: impl FnOnce(Pick<'db>) -> Pick<'db>,
+    ) -> Self::Choice {
+        choice.map(f)
+    }
+
+    fn check_by_value_method_shadowing(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        by_value_pick: &Self::Choice,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice> {
+        if let Ok(by_value_pick) = by_value_pick
+            && matches!(by_value_pick.kind, PickKind::InherentImplPick(_))
+        {
+            for mutbl in [Mutability::Not, Mutability::Mut] {
+                if let Err(e) = this.check_for_shadowed_autorefd_method(
+                    by_value_pick,
+                    step,
+                    self_ty,
+                    instantiate_self_ty_obligations,
+                    mutbl,
+                ) {
+                    return ControlFlow::Break(Err(e));
+                }
+            }
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn check_autorefed_method_shadowing(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        autoref_pick: &Self::Choice,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice> {
+        if let Ok(autoref_pick) = autoref_pick.as_ref() {
+            // Check we're not shadowing others
+            if matches!(autoref_pick.kind, PickKind::InherentImplPick(_))
+                && let Err(e) = this.check_for_shadowed_autorefd_method(
+                    autoref_pick,
+                    step,
+                    self_ty,
+                    instantiate_self_ty_obligations,
+                    Mutability::Mut,
+                )
+            {
+                return ControlFlow::Break(Err(e));
+            }
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn final_choice_from_err(err: MethodError<'db>) -> Self::FinalChoice {
+        Err(err)
+    }
+
+    fn choose(this: ProbeContext<'_, 'db, Self>) -> Self::FinalChoice {
+        this.pick()
+    }
+}
+
+#[derive(Debug)]
+struct ProbeAllChoice<'db> {
+    candidates: RefCell<FxHashMap<CandidateId, CandidateWithPrivate<'db>>>,
+    considering_visible_candidates: bool,
+}
+
+impl ProbeAllChoice<'_> {
+    fn new() -> Self {
+        Self { candidates: RefCell::default(), considering_visible_candidates: true }
+    }
+}
+
+impl<'db> ProbeChoice<'db> for ProbeAllChoice<'db> {
+    type Choice = Infallible;
+    type FinalChoice = Self;
+
+    fn with_impl_or_trait_item<'a>(
+        this: &mut ProbeContext<'a, 'db, Self>,
+        items: &[(Name, AssocItemId)],
+        mut callback: impl FnMut(&mut ProbeContext<'a, 'db, Self>, CandidateId),
+    ) {
+        let mode = this.mode;
+        items
+            .iter()
+            .filter_map(|(_, id)| is_relevant_kind_for_mode(mode, *id))
+            .for_each(|id| callback(this, id));
+    }
+
+    fn consider_candidates(
+        this: &ProbeContext<'_, 'db, Self>,
+        _self_ty: Ty<'db>,
+        candidates: Vec<&Candidate<'db>>,
+    ) -> ControlFlow<Self::Choice> {
+        let is_visible = this.choice.considering_visible_candidates;
+        let mut all_candidates = this.choice.candidates.borrow_mut();
+        for candidate in candidates {
+            // We should not override existing entries, because inherent methods of trait objects (from the principal)
+            // are also visited as trait methods, and we want to consider them inherent.
+            all_candidates
+                .entry(candidate.item)
+                .or_insert(CandidateWithPrivate { candidate: candidate.clone(), is_visible });
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn consider_private_candidates(
+        this: &mut ProbeContext<'_, 'db, Self>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) {
+        this.choice.considering_visible_candidates = false;
+        let ControlFlow::Continue(()) = this.consider_candidates(
+            self_ty,
+            instantiate_self_ty_obligations,
+            &this.private_candidates,
+            None,
+        );
+        this.choice.considering_visible_candidates = true;
+    }
+
+    fn map_choice_pick(
+        choice: Self::Choice,
+        _f: impl FnOnce(Pick<'db>) -> Pick<'db>,
+    ) -> Self::Choice {
+        choice
+    }
+
+    fn check_by_value_method_shadowing(
+        _this: &mut ProbeContext<'_, 'db, Self>,
+        _by_value_pick: &Self::Choice,
+        _step: &CandidateStep<'db>,
+        _self_ty: Ty<'db>,
+        _instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice> {
+        ControlFlow::Continue(())
+    }
+
+    fn check_autorefed_method_shadowing(
+        _this: &mut ProbeContext<'_, 'db, Self>,
+        _autoref_pick: &Self::Choice,
+        _step: &CandidateStep<'db>,
+        _self_ty: Ty<'db>,
+        _instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Self::Choice> {
+        ControlFlow::Continue(())
+    }
+
+    fn final_choice_from_err(_err: MethodError<'db>) -> Self::FinalChoice {
+        Self::new()
+    }
+
+    fn choose(mut this: ProbeContext<'_, 'db, Self>) -> Self::FinalChoice {
+        let ControlFlow::Continue(()) = this.pick_all_method();
+        this.choice
+    }
+}
+
+impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
+    fn new(
+        ctx: &'a MethodResolutionContext<'a, 'db>,
+        mode: Mode,
+        orig_steps_var_values: &'a OriginalQueryValues<'db>,
+        steps: &'a [CandidateStep<'db>],
+        choice: Choice,
+    ) -> ProbeContext<'a, 'db, Choice> {
+        ProbeContext {
+            ctx,
+            mode,
+            inherent_candidates: Vec::new(),
+            extension_candidates: Vec::new(),
+            impl_dups: FxHashSet::default(),
+            orig_steps_var_values,
+            steps,
+            private_candidates: Vec::new(),
+            static_candidates: Vec::new(),
+            choice,
+        }
+    }
+
+    #[inline]
+    fn db(&self) -> &'db dyn HirDatabase {
+        self.ctx.infcx.interner.db
+    }
+
+    #[inline]
+    fn interner(&self) -> DbInterner<'db> {
+        self.ctx.infcx.interner
+    }
+
+    #[inline]
+    fn infcx(&self) -> &'a InferCtxt<'db> {
+        self.ctx.infcx
+    }
+
+    #[inline]
+    fn param_env(&self) -> ParamEnv<'db> {
+        self.ctx.env.env
+    }
+
+    /// When we're looking up a method by path (UFCS), we relate the receiver
+    /// types invariantly. When we are looking up a method by the `.` operator,
+    /// we relate them covariantly.
+    fn variance(&self) -> Variance {
+        match self.mode {
+            Mode::MethodCall => Variance::Covariant,
+            Mode::Path => Variance::Invariant,
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // CANDIDATE ASSEMBLY
+
+    fn push_candidate(&mut self, candidate: Candidate<'db>, is_inherent: bool) {
+        let is_accessible = if is_inherent {
+            let candidate_id = match candidate.item {
+                CandidateId::FunctionId(id) => id.into(),
+                CandidateId::ConstId(id) => id.into(),
+            };
+            let visibility = self.db().assoc_visibility(candidate_id);
+            self.ctx.resolver.is_visible(self.db(), visibility)
+        } else {
+            true
+        };
+        if is_accessible {
+            if is_inherent {
+                self.inherent_candidates.push(candidate);
+            } else {
+                self.extension_candidates.push(candidate);
+            }
+        } else {
+            self.private_candidates.push(candidate);
+        }
+    }
+
+    fn assemble_inherent_candidates(&mut self) {
+        for step in self.steps.iter() {
+            self.assemble_probe(&step.self_ty, step.autoderefs);
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_probe(
+        &mut self,
+        self_ty: &Canonical<'db, QueryResponse<'db, Ty<'db>>>,
+        receiver_steps: usize,
+    ) {
+        let raw_self_ty = self_ty.value.value;
+        match raw_self_ty.kind() {
+            TyKind::Dynamic(data, ..) => {
+                if let Some(p) = data.principal() {
+                    // Subtle: we can't use `instantiate_query_response` here: using it will
+                    // commit to all of the type equalities assumed by inference going through
+                    // autoderef (see the `method-probe-no-guessing` test).
+                    //
+                    // However, in this code, it is OK if we end up with an object type that is
+                    // "more general" than the object type that we are evaluating. For *every*
+                    // object type `MY_OBJECT`, a function call that goes through a trait-ref
+                    // of the form `<MY_OBJECT as SuperTraitOf(MY_OBJECT)>::func` is a valid
+                    // `ObjectCandidate`, and it should be discoverable "exactly" through one
+                    // of the iterations in the autoderef loop, so there is no problem with it
+                    // being discoverable in another one of these iterations.
+                    //
+                    // Using `instantiate_canonical` on our
+                    // `Canonical<QueryResponse<Ty<'db>>>` and then *throwing away* the
+                    // `CanonicalVarValues` will exactly give us such a generalization - it
+                    // will still match the original object type, but it won't pollute our
+                    // type variables in any form, so just do that!
+                    let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) =
+                        self.infcx().instantiate_canonical(self_ty);
+
+                    self.assemble_inherent_candidates_from_object(generalized_self_ty);
+                    self.assemble_inherent_impl_candidates_for_type(
+                        &SimplifiedType::Trait(p.def_id().0.into()),
+                        receiver_steps,
+                    );
+                    self.assemble_inherent_candidates_for_incoherent_ty(
+                        raw_self_ty,
+                        receiver_steps,
+                    );
+                }
+            }
+            TyKind::Adt(def, _) => {
+                let def_id = def.def_id().0;
+                self.assemble_inherent_impl_candidates_for_type(
+                    &SimplifiedType::Adt(def_id.into()),
+                    receiver_steps,
+                );
+                self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps);
+            }
+            TyKind::Foreign(did) => {
+                self.assemble_inherent_impl_candidates_for_type(
+                    &SimplifiedType::Foreign(did.0.into()),
+                    receiver_steps,
+                );
+                self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps);
+            }
+            TyKind::Param(_) => {
+                self.assemble_inherent_candidates_from_param(raw_self_ty);
+            }
+            TyKind::Bool
+            | TyKind::Char
+            | TyKind::Int(_)
+            | TyKind::Uint(_)
+            | TyKind::Float(_)
+            | TyKind::Str
+            | TyKind::Array(..)
+            | TyKind::Slice(_)
+            | TyKind::RawPtr(_, _)
+            | TyKind::Ref(..)
+            | TyKind::Never
+            | TyKind::Tuple(..) => {
+                self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps)
+            }
+            _ => {}
+        }
+    }
+
+    fn assemble_inherent_candidates_for_incoherent_ty(
+        &mut self,
+        self_ty: Ty<'db>,
+        receiver_steps: usize,
+    ) {
+        let Some(simp) = simplify_type(self.interner(), self_ty, TreatParams::InstantiateWithInfer)
+        else {
+            panic!("unexpected incoherent type: {:?}", self_ty)
+        };
+        for &impl_def_id in incoherent_inherent_impls(self.db(), simp) {
+            self.assemble_inherent_impl_probe(impl_def_id, receiver_steps);
+        }
+    }
+
+    fn assemble_inherent_impl_candidates_for_type(
+        &mut self,
+        self_ty: &SimplifiedType,
+        receiver_steps: usize,
+    ) {
+        let Some(module) = simplified_type_module(self.db(), self_ty) else {
+            return;
+        };
+        InherentImpls::for_each_crate_and_block(
+            self.db(),
+            module.krate(),
+            module.containing_block(),
+            &mut |impls| {
+                for &impl_def_id in impls.for_self_ty(self_ty) {
+                    self.assemble_inherent_impl_probe(impl_def_id, receiver_steps);
+                }
+            },
+        );
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_inherent_impl_probe(&mut self, impl_def_id: ImplId, receiver_steps: usize) {
+        if !self.impl_dups.insert(impl_def_id) {
+            return; // already visited
+        }
+
+        self.with_impl_item(impl_def_id, |this, item| {
+            if !this.has_applicable_self(item) {
+                // No receiver declared. Not a candidate.
+                this.record_static_candidate(CandidateSource::Impl(impl_def_id));
+                return;
+            }
+            this.push_candidate(
+                Candidate { item, kind: InherentImplCandidate { impl_def_id, receiver_steps } },
+                true,
+            );
+        });
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'db>) {
+        let principal = match self_ty.kind() {
+            TyKind::Dynamic(data, ..) => Some(data),
+            _ => None,
+        }
+        .and_then(|data| data.principal())
+        .unwrap_or_else(|| {
+            panic!("non-object {:?} in assemble_inherent_candidates_from_object", self_ty)
+        });
+
+        // It is illegal to invoke a method on a trait instance that refers to
+        // the `Self` type. An [`DynCompatibilityViolation::SupertraitSelf`] error
+        // will be reported by `dyn_compatibility.rs` if the method refers to the
+        // `Self` type anywhere other than the receiver. Here, we use a
+        // instantiation that replaces `Self` with the object type itself. Hence,
+        // a `&self` method will wind up with an argument type like `&dyn Trait`.
+        let trait_ref = principal.with_self_ty(self.interner(), self_ty);
+        self.assemble_candidates_for_bounds(
+            elaborate::supertraits(self.interner(), trait_ref),
+            |this, new_trait_ref, item| {
+                this.push_candidate(Candidate { item, kind: ObjectCandidate(new_trait_ref) }, true);
+            },
+        );
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_inherent_candidates_from_param(&mut self, param_ty: Ty<'db>) {
+        debug_assert!(matches!(param_ty.kind(), TyKind::Param(_)));
+
+        let interner = self.interner();
+
+        // We use `DeepRejectCtxt` here which may return false positive on where clauses
+        // with alias self types. We need to later on reject these as inherent candidates
+        // in `consider_probe`.
+        let bounds = self.param_env().clauses.iter().filter_map(|predicate| {
+            let bound_predicate = predicate.kind();
+            match bound_predicate.skip_binder() {
+                ClauseKind::Trait(trait_predicate) => DeepRejectCtxt::relate_rigid_rigid(interner)
+                    .types_may_unify(param_ty, trait_predicate.trait_ref.self_ty())
+                    .then(|| bound_predicate.rebind(trait_predicate.trait_ref)),
+                ClauseKind::RegionOutlives(_)
+                | ClauseKind::TypeOutlives(_)
+                | ClauseKind::Projection(_)
+                | ClauseKind::ConstArgHasType(_, _)
+                | ClauseKind::WellFormed(_)
+                | ClauseKind::ConstEvaluatable(_)
+                | ClauseKind::UnstableFeature(_)
+                | ClauseKind::HostEffect(..) => None,
+            }
+        });
+
+        self.assemble_candidates_for_bounds(bounds, |this, poly_trait_ref, item| {
+            this.push_candidate(
+                Candidate { item, kind: WhereClauseCandidate(poly_trait_ref) },
+                true,
+            );
+        });
+    }
+
+    // Do a search through a list of bounds, using a callback to actually
+    // create the candidates.
+    fn assemble_candidates_for_bounds<F>(
+        &mut self,
+        bounds: impl Iterator<Item = PolyTraitRef<'db>>,
+        mut mk_cand: F,
+    ) where
+        F: for<'b> FnMut(&mut ProbeContext<'b, 'db, Choice>, PolyTraitRef<'db>, CandidateId),
+    {
+        for bound_trait_ref in bounds {
+            debug!("elaborate_bounds(bound_trait_ref={:?})", bound_trait_ref);
+            self.with_trait_item(bound_trait_ref.def_id().0, |this, item| {
+                if !this.has_applicable_self(item) {
+                    this.record_static_candidate(CandidateSource::Trait(
+                        bound_trait_ref.def_id().0,
+                    ));
+                } else {
+                    mk_cand(this, bound_trait_ref, item);
+                }
+            });
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
+        for &trait_did in self.ctx.traits_in_scope {
+            self.assemble_extension_candidates_for_trait(trait_did);
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: TraitId) {
+        let trait_args = self.infcx().fresh_args_for_item(trait_def_id.into());
+        let trait_ref = TraitRef::new_from_args(self.interner(), trait_def_id.into(), trait_args);
+
+        self.with_trait_item(trait_def_id, |this, item| {
+            // Check whether `trait_def_id` defines a method with suitable name.
+            if !this.has_applicable_self(item) {
+                debug!("method has inapplicable self");
+                this.record_static_candidate(CandidateSource::Trait(trait_def_id));
+                return;
+            }
+            this.push_candidate(
+                Candidate { item, kind: TraitCandidate(Binder::dummy(trait_ref)) },
+                false,
+            );
+        });
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// THE ACTUAL SEARCH
+impl<'a, 'db> ProbeContext<'a, 'db, ProbeForNameChoice<'db>> {
+    #[instrument(level = "debug", skip(self))]
+    fn pick(mut self) -> PickResult<'db> {
+        if let Some(r) = self.pick_core() {
+            return r;
+        }
+
+        debug!("pick: actual search failed, assemble diagnostics");
+
+        if let Some(candidate) = self.choice.private_candidate {
+            return Err(MethodError::PrivateMatch(candidate));
+        }
+
+        Err(MethodError::NoMatch)
+    }
+
+    fn pick_core(&mut self) -> Option<PickResult<'db>> {
+        self.pick_all_method().break_value()
+    }
+
+    /// Check for cases where arbitrary self types allows shadowing
+    /// of methods that might be a compatibility break. Specifically,
+    /// we have something like:
+    /// ```ignore (illustrative)
+    /// struct A;
+    /// impl A {
+    ///   fn foo(self: &NonNull<A>) {}
+    ///      // note this is by reference
+    /// }
+    /// ```
+    /// then we've come along and added this method to `NonNull`:
+    /// ```ignore (illustrative)
+    ///   fn foo(self)  // note this is by value
+    /// ```
+    /// Report an error in this case.
+    fn check_for_shadowed_autorefd_method(
+        &mut self,
+        possible_shadower: &Pick<'db>,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+        mutbl: Mutability,
+    ) -> Result<(), MethodError<'db>> {
+        // The errors emitted by this function are part of
+        // the arbitrary self types work, and should not impact
+        // other users.
+        if !self.ctx.unstable_features.arbitrary_self_types
+            && !self.ctx.unstable_features.arbitrary_self_types_pointers
+        {
+            return Ok(());
+        }
+
+        // Set criteria for how we find methods possibly shadowed by 'possible_shadower'
+        let pick_constraints = PickConstraintsForShadowed {
+            // It's the same `self` type...
+            autoderefs: possible_shadower.autoderefs,
+            // ... but the method was found in an impl block determined
+            // by searching further along the Receiver chain than the other,
+            // showing that it's a smart pointer type causing the problem...
+            receiver_steps: possible_shadower.receiver_steps,
+            // ... and they don't end up pointing to the same item in the
+            // first place (could happen with things like blanket impls for T)
+            def_id: possible_shadower.item,
+        };
+        // A note on the autoderefs above. Within pick_by_value_method, an extra
+        // autoderef may be applied in order to reborrow a reference with
+        // a different lifetime. That seems as though it would break the
+        // logic of these constraints, since the number of autoderefs could
+        // no longer be used to identify the fundamental type of the receiver.
+        // However, this extra autoderef is applied only to by-value calls
+        // where the receiver is already a reference. So this situation would
+        // only occur in cases where the shadowing looks like this:
+        // ```
+        // struct A;
+        // impl A {
+        //   fn foo(self: &&NonNull<A>) {}
+        //      // note this is by DOUBLE reference
+        // }
+        // ```
+        // then we've come along and added this method to `NonNull`:
+        // ```
+        //   fn foo(&self)  // note this is by single reference
+        // ```
+        // and the call is:
+        // ```
+        // let bar = NonNull<Foo>;
+        // let bar = &foo;
+        // bar.foo();
+        // ```
+        // In these circumstances, the logic is wrong, and we wouldn't spot
+        // the shadowing, because the autoderef-based maths wouldn't line up.
+        // This is a niche case and we can live without generating an error
+        // in the case of such shadowing.
+        let potentially_shadowed_pick = self.pick_autorefd_method(
+            step,
+            self_ty,
+            instantiate_self_ty_obligations,
+            mutbl,
+            Some(&pick_constraints),
+        );
+        // Look for actual pairs of shadower/shadowed which are
+        // the sort of shadowing case we want to avoid. Specifically...
+        if let ControlFlow::Break(Ok(possible_shadowed)) = &potentially_shadowed_pick {
+            let sources = [possible_shadower, possible_shadowed]
+                .into_iter()
+                .map(|p| self.candidate_source_from_pick(p))
+                .collect();
+            return Err(MethodError::Ambiguity(sources));
+        }
+        Ok(())
+    }
+}
+
+impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
+    fn pick_all_method(&mut self) -> ControlFlow<Choice::Choice> {
+        self.steps
+            .iter()
+            // At this point we're considering the types to which the receiver can be converted,
+            // so we want to follow the `Deref` chain not the `Receiver` chain. Filter out
+            // steps which can only be reached by following the (longer) `Receiver` chain.
+            .filter(|step| step.reachable_via_deref)
+            .filter(|step| {
+                debug!("pick_all_method: step={:?}", step);
+                // skip types that are from a type error or that would require dereferencing
+                // a raw pointer
+                !step.self_ty.value.value.references_non_lt_error() && !step.from_unsafe_deref
+            })
+            .try_for_each(|step| {
+                let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
+                    .infcx()
+                    .instantiate_query_response_and_region_obligations(
+                        &ObligationCause::new(),
+                        self.param_env(),
+                        self.orig_steps_var_values,
+                        &step.self_ty,
+                    )
+                    .unwrap_or_else(|_| panic!("{:?} was applicable but now isn't?", step.self_ty));
+
+                let by_value_pick =
+                    self.pick_by_value_method(step, self_ty, &instantiate_self_ty_obligations);
+
+                // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing)
+                if let ControlFlow::Break(by_value_pick) = by_value_pick {
+                    Choice::check_by_value_method_shadowing(
+                        self,
+                        &by_value_pick,
+                        step,
+                        self_ty,
+                        &instantiate_self_ty_obligations,
+                    )?;
+                    return ControlFlow::Break(by_value_pick);
+                }
+
+                let autoref_pick = self.pick_autorefd_method(
+                    step,
+                    self_ty,
+                    &instantiate_self_ty_obligations,
+                    Mutability::Not,
+                    None,
+                );
+                // Check for shadowing of a by-mut-ref method by a by-reference method (see comments on check_for_shadowing)
+                if let ControlFlow::Break(autoref_pick) = autoref_pick {
+                    Choice::check_autorefed_method_shadowing(
+                        self,
+                        &autoref_pick,
+                        step,
+                        self_ty,
+                        &instantiate_self_ty_obligations,
+                    )?;
+                    return ControlFlow::Break(autoref_pick);
+                }
+
+                // Note that no shadowing errors are produced from here on,
+                // as we consider const ptr methods.
+                // We allow new methods that take *mut T to shadow
+                // methods which took *const T, so there is no entry in
+                // this list for the results of `pick_const_ptr_method`.
+                // The reason is that the standard pointer cast method
+                // (on a mutable pointer) always already shadows the
+                // cast method (on a const pointer). So, if we added
+                // `pick_const_ptr_method` to this method, the anti-
+                // shadowing algorithm would always complain about
+                // the conflict between *const::cast and *mut::cast.
+                // In practice therefore this does constrain us:
+                // we cannot add new
+                //   self: *mut Self
+                // methods to types such as NonNull or anything else
+                // which implements Receiver, because this might in future
+                // shadow existing methods taking
+                //   self: *const NonNull<Self>
+                // in the pointee. In practice, methods taking raw pointers
+                // are rare, and it seems that it should be easily possible
+                // to avoid such compatibility breaks.
+                // We also don't check for reborrowed pin methods which
+                // may be shadowed; these also seem unlikely to occur.
+                self.pick_autorefd_method(
+                    step,
+                    self_ty,
+                    &instantiate_self_ty_obligations,
+                    Mutability::Mut,
+                    None,
+                )?;
+                self.pick_const_ptr_method(step, self_ty, &instantiate_self_ty_obligations)
+            })
+    }
+
+    /// For each type `T` in the step list, this attempts to find a method where
+    /// the (transformed) self type is exactly `T`. We do however do one
+    /// transformation on the adjustment: if we are passing a region pointer in,
+    /// we will potentially *reborrow* it to a shorter lifetime. This allows us
+    /// to transparently pass `&mut` pointers, in particular, without consuming
+    /// them for their entire lifetime.
+    fn pick_by_value_method(
+        &mut self,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Choice::Choice> {
+        if step.unsize {
+            return ControlFlow::Continue(());
+        }
+
+        self.pick_method(self_ty, instantiate_self_ty_obligations, None).map_break(|r| {
+            Choice::map_choice_pick(r, |mut pick| {
+                pick.autoderefs = step.autoderefs;
+
+                match step.self_ty.value.value.kind() {
+                    // Insert a `&*` or `&mut *` if this is a reference type:
+                    TyKind::Ref(_, _, mutbl) => {
+                        pick.autoderefs += 1;
+                        pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
+                            mutbl,
+                            unsize: pick.autoref_or_ptr_adjustment.is_some_and(|a| a.get_unsize()),
+                        })
+                    }
+
+                    _ => (),
+                }
+
+                pick
+            })
+        })
+    }
+
+    fn pick_autorefd_method(
+        &mut self,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+        mutbl: Mutability,
+        pick_constraints: Option<&PickConstraintsForShadowed>,
+    ) -> ControlFlow<Choice::Choice> {
+        let interner = self.interner();
+
+        if let Some(pick_constraints) = pick_constraints
+            && !pick_constraints.may_shadow_based_on_autoderefs(step.autoderefs)
+        {
+            return ControlFlow::Continue(());
+        }
+
+        // In general, during probing we erase regions.
+        let region = Region::new_erased(interner);
+
+        let autoref_ty = Ty::new_ref(interner, region, self_ty, mutbl);
+        self.pick_method(autoref_ty, instantiate_self_ty_obligations, pick_constraints).map_break(
+            |r| {
+                Choice::map_choice_pick(r, |mut pick| {
+                    pick.autoderefs = step.autoderefs;
+                    pick.autoref_or_ptr_adjustment =
+                        Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize });
+                    pick
+                })
+            },
+        )
+    }
+
+    /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
+    /// special case for this is because going from `*mut T` to `*const T` with autoderefs and
+    /// autorefs would require dereferencing the pointer, which is not safe.
+    fn pick_const_ptr_method(
+        &mut self,
+        step: &CandidateStep<'db>,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+    ) -> ControlFlow<Choice::Choice> {
+        // Don't convert an unsized reference to ptr
+        if step.unsize {
+            return ControlFlow::Continue(());
+        }
+
+        let TyKind::RawPtr(ty, Mutability::Mut) = self_ty.kind() else {
+            return ControlFlow::Continue(());
+        };
+
+        let const_ptr_ty = Ty::new_ptr(self.interner(), ty, Mutability::Not);
+        self.pick_method(const_ptr_ty, instantiate_self_ty_obligations, None).map_break(|r| {
+            Choice::map_choice_pick(r, |mut pick| {
+                pick.autoderefs = step.autoderefs;
+                pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
+                pick
+            })
+        })
+    }
+
+    fn pick_method(
+        &mut self,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+        pick_constraints: Option<&PickConstraintsForShadowed>,
+    ) -> ControlFlow<Choice::Choice> {
+        debug!("pick_method(self_ty={:?})", self_ty);
+
+        for (kind, candidates) in
+            [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
+        {
+            debug!("searching {} candidates", kind);
+            self.consider_candidates(
+                self_ty,
+                instantiate_self_ty_obligations,
+                candidates,
+                pick_constraints,
+            )?;
+        }
+
+        Choice::consider_private_candidates(self, self_ty, instantiate_self_ty_obligations);
+
+        ControlFlow::Continue(())
+    }
+
+    fn consider_candidates(
+        &self,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+        candidates: &[Candidate<'db>],
+        pick_constraints: Option<&PickConstraintsForShadowed>,
+    ) -> ControlFlow<Choice::Choice> {
+        let applicable_candidates: Vec<_> = candidates
+            .iter()
+            .filter(|candidate| {
+                pick_constraints
+                    .map(|pick_constraints| pick_constraints.candidate_may_shadow(candidate))
+                    .unwrap_or(true)
+            })
+            .filter(|probe| {
+                self.consider_probe(self_ty, instantiate_self_ty_obligations, probe)
+                    != ProbeResult::NoMatch
+            })
+            .collect();
+
+        debug!("applicable_candidates: {:?}", applicable_candidates);
+
+        Choice::consider_candidates(self, self_ty, applicable_candidates)
+    }
+
+    fn select_trait_candidate(
+        &self,
+        trait_ref: TraitRef<'db>,
+    ) -> SelectionResult<'db, Selection<'db>> {
+        let obligation =
+            Obligation::new(self.interner(), ObligationCause::new(), self.param_env(), trait_ref);
+        self.infcx().select(&obligation)
+    }
+
+    /// Used for ambiguous method call error reporting. Uses probing that throws away the result internally,
+    /// so do not use to make a decision that may lead to a successful compilation.
+    fn candidate_source(&self, candidate: &Candidate<'db>, self_ty: Ty<'db>) -> CandidateSource {
+        match candidate.kind {
+            InherentImplCandidate { impl_def_id, .. } => CandidateSource::Impl(impl_def_id),
+            ObjectCandidate(trait_ref) | WhereClauseCandidate(trait_ref) => {
+                CandidateSource::Trait(trait_ref.def_id().0)
+            }
+            TraitCandidate(trait_ref) => self.infcx().probe(|_| {
+                let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+                    BoundRegionConversionTime::FnCall,
+                    trait_ref,
+                );
+                let (xform_self_ty, _) = self.xform_self_ty(
+                    candidate.item,
+                    trait_ref.self_ty(),
+                    trait_ref.args.as_slice(),
+                );
+                // Guide the trait selection to show impls that have methods whose type matches
+                // up with the `self` parameter of the method.
+                let _ = self
+                    .infcx()
+                    .at(&ObligationCause::dummy(), self.param_env())
+                    .sup(xform_self_ty, self_ty);
+                match self.select_trait_candidate(trait_ref) {
+                    Ok(Some(ImplSource::UserDefined(ref impl_data))) => {
+                        // If only a single impl matches, make the error message point
+                        // to that impl.
+                        CandidateSource::Impl(impl_data.impl_def_id)
+                    }
+                    _ => CandidateSource::Trait(trait_ref.def_id.0),
+                }
+            }),
+        }
+    }
+
+    fn candidate_source_from_pick(&self, pick: &Pick<'db>) -> CandidateSource {
+        match pick.kind {
+            InherentImplPick(impl_) => CandidateSource::Impl(impl_),
+            ObjectPick(trait_) | TraitPick(trait_) => CandidateSource::Trait(trait_),
+            WhereClausePick(trait_ref) => CandidateSource::Trait(trait_ref.skip_binder().def_id.0),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn consider_probe(
+        &self,
+        self_ty: Ty<'db>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'db>],
+        probe: &Candidate<'db>,
+    ) -> ProbeResult {
+        self.infcx().probe(|_| {
+            let mut result = ProbeResult::Match;
+            let cause = &ObligationCause::new();
+            let mut ocx = ObligationCtxt::new(self.infcx());
+
+            // Subtle: we're not *really* instantiating the current self type while
+            // probing, but instead fully recompute the autoderef steps once we've got
+            // a final `Pick`. We can't nicely handle these obligations outside of a probe.
+            //
+            // We simply handle them for each candidate here for now. That's kinda scuffed
+            // and ideally we just put them into the `FnCtxt` right away. We need to consider
+            // them to deal with defining uses in `method_autoderef_steps`.
+            ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned());
+            let errors = ocx.try_evaluate_obligations();
+            if !errors.is_empty() {
+                unreachable!("unexpected autoderef error {errors:?}");
+            }
+
+            let mut trait_predicate = None;
+            let (xform_self_ty, xform_ret_ty);
+
+            match probe.kind {
+                InherentImplCandidate { impl_def_id, .. } => {
+                    let impl_args = self.infcx().fresh_args_for_item(impl_def_id.into());
+                    let impl_ty =
+                        self.db().impl_self_ty(impl_def_id).instantiate(self.interner(), impl_args);
+                    (xform_self_ty, xform_ret_ty) =
+                        self.xform_self_ty(probe.item, impl_ty, impl_args.as_slice());
+                    match ocx.relate(
+                        cause,
+                        self.param_env(),
+                        self.variance(),
+                        self_ty,
+                        xform_self_ty,
+                    ) {
+                        Ok(()) => {}
+                        Err(err) => {
+                            debug!("--> cannot relate self-types {:?}", err);
+                            return ProbeResult::NoMatch;
+                        }
+                    }
+                    // Check whether the impl imposes obligations we have to worry about.
+                    let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into());
+                    let impl_bounds = clauses_as_obligations(
+                        impl_bounds.iter_instantiated_copied(self.interner(), impl_args.as_slice()),
+                        ObligationCause::new(),
+                        self.param_env(),
+                    );
+                    // Convert the bounds into obligations.
+                    ocx.register_obligations(impl_bounds);
+                }
+                TraitCandidate(poly_trait_ref) => {
+                    // Some trait methods are excluded for arrays before 2021.
+                    // (`array.into_iter()` wants a slice iterator for compatibility.)
+                    if self_ty.is_array() && !self.ctx.edition.at_least_2021() {
+                        let trait_signature = self.db().trait_signature(poly_trait_ref.def_id().0);
+                        if trait_signature
+                            .flags
+                            .contains(TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH)
+                        {
+                            return ProbeResult::NoMatch;
+                        }
+                    }
+
+                    // Some trait methods are excluded for boxed slices before 2024.
+                    // (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
+                    if self_ty.boxed_ty().is_some_and(Ty::is_slice)
+                        && !self.ctx.edition.at_least_2024()
+                    {
+                        let trait_signature = self.db().trait_signature(poly_trait_ref.def_id().0);
+                        if trait_signature
+                            .flags
+                            .contains(TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH)
+                        {
+                            return ProbeResult::NoMatch;
+                        }
+                    }
+
+                    let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+                        BoundRegionConversionTime::FnCall,
+                        poly_trait_ref,
+                    );
+                    (xform_self_ty, xform_ret_ty) = self.xform_self_ty(
+                        probe.item,
+                        trait_ref.self_ty(),
+                        trait_ref.args.as_slice(),
+                    );
+                    match ocx.relate(
+                        cause,
+                        self.param_env(),
+                        self.variance(),
+                        self_ty,
+                        xform_self_ty,
+                    ) {
+                        Ok(()) => {}
+                        Err(err) => {
+                            debug!("--> cannot relate self-types {:?}", err);
+                            return ProbeResult::NoMatch;
+                        }
+                    }
+                    let obligation = Obligation::new(
+                        self.interner(),
+                        cause.clone(),
+                        self.param_env(),
+                        Binder::dummy(trait_ref),
+                    );
+
+                    // We only need this hack to deal with fatal overflow in the old solver.
+                    ocx.register_obligation(obligation);
+
+                    trait_predicate = Some(trait_ref.upcast(self.interner()));
+                }
+                ObjectCandidate(poly_trait_ref) | WhereClauseCandidate(poly_trait_ref) => {
+                    let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+                        BoundRegionConversionTime::FnCall,
+                        poly_trait_ref,
+                    );
+                    (xform_self_ty, xform_ret_ty) = self.xform_self_ty(
+                        probe.item,
+                        trait_ref.self_ty(),
+                        trait_ref.args.as_slice(),
+                    );
+
+                    if matches!(probe.kind, WhereClauseCandidate(_)) {
+                        // `WhereClauseCandidate` requires that the self type is a param,
+                        // because it has special behavior with candidate preference as an
+                        // inherent pick.
+                        match ocx.structurally_normalize_ty(
+                            cause,
+                            self.param_env(),
+                            trait_ref.self_ty(),
+                        ) {
+                            Ok(ty) => {
+                                if !matches!(ty.kind(), TyKind::Param(_)) {
+                                    debug!("--> not a param ty: {xform_self_ty:?}");
+                                    return ProbeResult::NoMatch;
+                                }
+                            }
+                            Err(errors) => {
+                                debug!("--> cannot relate self-types {:?}", errors);
+                                return ProbeResult::NoMatch;
+                            }
+                        }
+                    }
+
+                    match ocx.relate(
+                        cause,
+                        self.param_env(),
+                        self.variance(),
+                        self_ty,
+                        xform_self_ty,
+                    ) {
+                        Ok(()) => {}
+                        Err(err) => {
+                            debug!("--> cannot relate self-types {:?}", err);
+                            return ProbeResult::NoMatch;
+                        }
+                    }
+                }
+            }
+
+            // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/134>.
+            //
+            // In the new solver, check the well-formedness of the return type.
+            // This emulates, in a way, the predicates that fall out of
+            // normalizing the return type in the old solver.
+            //
+            // FIXME(-Znext-solver): We alternatively could check the predicates of
+            // the method itself hold, but we intentionally do not do this in the old
+            // solver b/c of cycles, and doing it in the new solver would be stronger.
+            // This should be fixed in the future, since it likely leads to much better
+            // method winnowing.
+            if let Some(xform_ret_ty) = xform_ret_ty {
+                ocx.register_obligation(Obligation::new(
+                    self.interner(),
+                    cause.clone(),
+                    self.param_env(),
+                    ClauseKind::WellFormed(xform_ret_ty.into()),
+                ));
+            }
+
+            if !ocx.try_evaluate_obligations().is_empty() {
+                result = ProbeResult::NoMatch;
+            }
+
+            if self.should_reject_candidate_due_to_opaque_treated_as_rigid(trait_predicate) {
+                result = ProbeResult::NoMatch;
+            }
+
+            // FIXME: Need to leak-check here.
+            // if let Err(_) = self.leak_check(outer_universe, Some(snapshot)) {
+            //     result = ProbeResult::NoMatch;
+            // }
+
+            result
+        })
+    }
+
+    /// Trait candidates for not-yet-defined opaque types are a somewhat hacky.
+    ///
+    /// We want to only accept trait methods if they were hold even if the
+    /// opaque types were rigid. To handle this, we both check that for trait
+    /// candidates the goal were to hold even when treating opaques as rigid,
+    /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank).
+    ///
+    /// We also check that all opaque types encountered as self types in the
+    /// autoderef chain don't get constrained when applying the candidate.
+    /// Importantly, this also handles calling methods taking `&self` on
+    /// `impl Trait` to reject the "by-self" candidate.
+    ///
+    /// This needs to happen at the end of `consider_probe` as we need to take
+    /// all the constraints from that into account.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn should_reject_candidate_due_to_opaque_treated_as_rigid(
+        &self,
+        trait_predicate: Option<Predicate<'db>>,
+    ) -> bool {
+        // This function is what hacky and doesn't perfectly do what we want it to.
+        // It's not soundness critical and we should be able to freely improve this
+        // in the future.
+        //
+        // Some concrete edge cases include the fact that `goal_may_hold_opaque_types_jank`
+        // also fails if there are any constraints opaques which are never used as a self
+        // type. We also allow where-bounds which are currently ambiguous but end up
+        // constraining an opaque later on.
+
+        // Check whether the trait candidate would not be applicable if the
+        // opaque type were rigid.
+        if let Some(predicate) = trait_predicate {
+            let goal = Goal { param_env: self.param_env(), predicate };
+            if !self.infcx().goal_may_hold_opaque_types_jank(goal) {
+                return true;
+            }
+        }
+
+        // Check whether any opaque types in the autoderef chain have been
+        // constrained.
+        for step in self.steps {
+            if step.self_ty_is_opaque {
+                debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque");
+                let constrained_opaque = self.infcx().probe(|_| {
+                    // If we fail to instantiate the self type of this
+                    // step, this part of the deref-chain is no longer
+                    // reachable. In this case we don't care about opaque
+                    // types there.
+                    let Ok(ok) = self.infcx().instantiate_query_response_and_region_obligations(
+                        &ObligationCause::new(),
+                        self.param_env(),
+                        self.orig_steps_var_values,
+                        &step.self_ty,
+                    ) else {
+                        debug!("failed to instantiate self_ty");
+                        return false;
+                    };
+                    let mut ocx = ObligationCtxt::new(self.infcx());
+                    let self_ty = ocx.register_infer_ok_obligations(ok);
+                    if !ocx.try_evaluate_obligations().is_empty() {
+                        debug!("failed to prove instantiate self_ty obligations");
+                        return false;
+                    }
+
+                    !self.infcx().resolve_vars_if_possible(self_ty).is_ty_var()
+                });
+                if constrained_opaque {
+                    debug!("opaque type has been constrained");
+                    return true;
+                }
+            }
+        }
+
+        false
+    }
+
+    /// Sometimes we get in a situation where we have multiple probes that are all impls of the
+    /// same trait, but we don't know which impl to use. In this case, since in all cases the
+    /// external interface of the method can be determined from the trait, it's ok not to decide.
+    /// We can basically just collapse all of the probes for various impls into one where-clause
+    /// probe. This will result in a pending obligation so when more type-info is available we can
+    /// make the final decision.
+    ///
+    /// Example (`tests/ui/methods/method-two-trait-defer-resolution-1.rs`):
+    ///
+    /// ```ignore (illustrative)
+    /// trait Foo { ... }
+    /// impl Foo for Vec<i32> { ... }
+    /// impl Foo for Vec<usize> { ... }
+    /// ```
+    ///
+    /// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we
+    /// use, so it's ok to just commit to "using the method from the trait Foo".
+    fn collapse_candidates_to_trait_pick(
+        &self,
+        self_ty: Ty<'db>,
+        probes: &[&Candidate<'db>],
+    ) -> Option<Pick<'db>> {
+        // Do all probes correspond to the same trait?
+        let ItemContainerId::TraitId(container) = probes[0].item.container(self.db()) else {
+            return None;
+        };
+        for p in &probes[1..] {
+            let ItemContainerId::TraitId(p_container) = p.item.container(self.db()) else {
+                return None;
+            };
+            if p_container != container {
+                return None;
+            }
+        }
+
+        // FIXME: check the return type here somehow.
+        // If so, just use this trait and call it a day.
+        Some(Pick {
+            item: probes[0].item,
+            kind: TraitPick(container),
+            autoderefs: 0,
+            autoref_or_ptr_adjustment: None,
+            self_ty,
+            receiver_steps: None,
+            shadowed_candidates: vec![],
+        })
+    }
+
+    /// Much like `collapse_candidates_to_trait_pick`, this method allows us to collapse
+    /// multiple conflicting picks if there is one pick whose trait container is a subtrait
+    /// of the trait containers of all of the other picks.
+    ///
+    /// This implements RFC #3624.
+    fn collapse_candidates_to_subtrait_pick(
+        &self,
+        self_ty: Ty<'db>,
+        probes: &[&Candidate<'db>],
+    ) -> Option<Pick<'db>> {
+        let mut child_candidate = probes[0];
+        let ItemContainerId::TraitId(mut child_trait) = child_candidate.item.container(self.db())
+        else {
+            return None;
+        };
+        let mut supertraits: FxHashSet<_> =
+            supertrait_def_ids(self.interner(), child_trait.into()).collect();
+
+        let mut remaining_candidates: Vec<_> = probes[1..].to_vec();
+        while !remaining_candidates.is_empty() {
+            let mut made_progress = false;
+            let mut next_round = vec![];
+
+            for remaining_candidate in remaining_candidates {
+                let ItemContainerId::TraitId(remaining_trait) =
+                    remaining_candidate.item.container(self.db())
+                else {
+                    return None;
+                };
+                if supertraits.contains(&remaining_trait.into()) {
+                    made_progress = true;
+                    continue;
+                }
+
+                // This pick is not a supertrait of the `child_pick`.
+                // Check if it's a subtrait of the `child_pick`, instead.
+                // If it is, then it must have been a subtrait of every
+                // other pick we've eliminated at this point. It will
+                // take over at this point.
+                let remaining_trait_supertraits: FxHashSet<_> =
+                    supertrait_def_ids(self.interner(), remaining_trait.into()).collect();
+                if remaining_trait_supertraits.contains(&child_trait.into()) {
+                    child_candidate = remaining_candidate;
+                    child_trait = remaining_trait;
+                    supertraits = remaining_trait_supertraits;
+                    made_progress = true;
+                    continue;
+                }
+
+                // `child_pick` is not a supertrait of this pick.
+                // Don't bail here, since we may be comparing two supertraits
+                // of a common subtrait. These two supertraits won't be related
+                // at all, but we will pick them up next round when we find their
+                // child as we continue iterating in this round.
+                next_round.push(remaining_candidate);
+            }
+
+            if made_progress {
+                // If we've made progress, iterate again.
+                remaining_candidates = next_round;
+            } else {
+                // Otherwise, we must have at least two candidates which
+                // are not related to each other at all.
+                return None;
+            }
+        }
+
+        Some(Pick {
+            item: child_candidate.item,
+            kind: TraitPick(child_trait),
+            autoderefs: 0,
+            autoref_or_ptr_adjustment: None,
+            self_ty,
+            shadowed_candidates: probes
+                .iter()
+                .map(|c| c.item)
+                .filter(|item| *item != child_candidate.item)
+                .collect(),
+            receiver_steps: None,
+        })
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // MISCELLANY
+    fn has_applicable_self(&self, item: CandidateId) -> bool {
+        // "Fast track" -- check for usage of sugar when in method call
+        // mode.
+        //
+        // In Path mode (i.e., resolving a value like `T::next`), consider any
+        // associated value (i.e., methods, constants).
+        match item {
+            CandidateId::FunctionId(id) if self.mode == Mode::MethodCall => {
+                self.db().function_signature(id).has_self_param()
+            }
+            _ => true,
+        }
+        // FIXME -- check for types that deref to `Self`,
+        // like `Rc<Self>` and so on.
+        //
+        // Note also that the current code will break if this type
+        // includes any of the type parameters defined on the method
+        // -- but this could be overcome.
+    }
+
+    fn record_static_candidate(&mut self, source: CandidateSource) {
+        self.static_candidates.push(source);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn xform_self_ty(
+        &self,
+        item: CandidateId,
+        impl_ty: Ty<'db>,
+        args: &[GenericArg<'db>],
+    ) -> (Ty<'db>, Option<Ty<'db>>) {
+        if let CandidateId::FunctionId(item) = item
+            && self.mode == Mode::MethodCall
+        {
+            let sig = self.xform_method_sig(item, args);
+            (sig.inputs().as_slice()[0], Some(sig.output()))
+        } else {
+            (impl_ty, None)
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn xform_method_sig(&self, method: FunctionId, args: &[GenericArg<'db>]) -> FnSig<'db> {
+        let fn_sig = self.db().callable_item_signature(method.into());
+        debug!(?fn_sig);
+
+        assert!(!args.has_escaping_bound_vars());
+
+        // It is possible for type parameters or early-bound lifetimes
+        // to appear in the signature of `self`. The generic parameters
+        // we are given do not include type/lifetime parameters for the
+        // method yet. So create fresh variables here for those too,
+        // if there are any.
+        let generics = self.db().generic_params(method.into());
+
+        let xform_fn_sig = if generics.is_empty() {
+            fn_sig.instantiate(self.interner(), args)
+        } else {
+            let args = GenericArgs::for_item(
+                self.interner(),
+                method.into(),
+                |param_index, param_id, _| {
+                    let i = param_index as usize;
+                    if i < args.len() {
+                        args[i]
+                    } else {
+                        match param_id {
+                            GenericParamId::LifetimeParamId(_) => {
+                                // In general, during probe we erase regions.
+                                Region::new_erased(self.interner()).into()
+                            }
+                            GenericParamId::TypeParamId(_) => self.infcx().next_ty_var().into(),
+                            GenericParamId::ConstParamId(_) => self.infcx().next_const_var().into(),
+                        }
+                    }
+                },
+            );
+            fn_sig.instantiate(self.interner(), args)
+        };
+
+        self.interner().instantiate_bound_regions_with_erased(xform_fn_sig)
+    }
+
+    fn with_impl_item(&mut self, def_id: ImplId, callback: impl FnMut(&mut Self, CandidateId)) {
+        Choice::with_impl_or_trait_item(self, &def_id.impl_items(self.db()).items, callback)
+    }
+
+    fn with_trait_item(&mut self, def_id: TraitId, callback: impl FnMut(&mut Self, CandidateId)) {
+        Choice::with_impl_or_trait_item(self, &def_id.trait_items(self.db()).items, callback)
+    }
+}
+
+/// Determine if the given associated item type is relevant in the current context.
+fn is_relevant_kind_for_mode(mode: Mode, kind: AssocItemId) -> Option<CandidateId> {
+    Some(match (mode, kind) {
+        (Mode::MethodCall, AssocItemId::FunctionId(id)) => id.into(),
+        (Mode::Path, AssocItemId::ConstId(id)) => id.into(),
+        (Mode::Path, AssocItemId::FunctionId(id)) => id.into(),
+        _ => return None,
+    })
+}
+
+impl<'db> Candidate<'db> {
+    fn to_unadjusted_pick(&self, self_ty: Ty<'db>) -> Pick<'db> {
+        Pick {
+            item: self.item,
+            kind: match self.kind {
+                InherentImplCandidate { impl_def_id, .. } => InherentImplPick(impl_def_id),
+                ObjectCandidate(trait_ref) => ObjectPick(trait_ref.skip_binder().def_id.0),
+                TraitCandidate(trait_ref) => TraitPick(trait_ref.skip_binder().def_id.0),
+                WhereClauseCandidate(trait_ref) => {
+                    // Only trait derived from where-clauses should
+                    // appear here, so they should not contain any
+                    // inference variables or other artifacts. This
+                    // means they are safe to put into the
+                    // `WhereClausePick`.
+                    assert!(
+                        !trait_ref.skip_binder().args.has_infer()
+                            && !trait_ref.skip_binder().args.has_placeholders()
+                    );
+
+                    WhereClausePick(trait_ref)
+                }
+            },
+            autoderefs: 0,
+            autoref_or_ptr_adjustment: None,
+            self_ty,
+            receiver_steps: match self.kind {
+                InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps),
+                _ => None,
+            },
+            shadowed_candidates: vec![],
+        }
+    }
+}
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 7aebe17..b5b691d 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -12,7 +12,7 @@
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
 use rustc_ast_ir::Mutability;
 use rustc_hash::FxHashMap;
-use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _};
+use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _};
 use smallvec::{SmallVec, smallvec};
 use stdx::{impl_from, never};
 
@@ -22,7 +22,6 @@
     db::{HirDatabase, InternedClosureId},
     display::{DisplayTarget, HirDisplay},
     infer::PointerCast,
-    lang_items::is_box,
     next_solver::{
         Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind,
         infer::{InferCtxt, traits::ObligationCause},
@@ -185,7 +184,7 @@
         match self {
             ProjectionElem::Deref => match base.kind() {
                 TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
-                TyKind::Adt(adt_def, subst) if is_box(db, adt_def.def_id().0) => subst.type_at(0),
+                TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0),
                 _ => {
                     never!(
                         "Overloaded deref on type {} is not a projection",
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index f242115..88acd49 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -544,7 +544,7 @@
 fn for_loop() {
     check_pass(
         r#"
-//- minicore: iterator, add
+//- minicore: iterator, add, builtin_impls
 fn should_not_reach() {
     _ // FIXME: replace this function with panic when that works
 }
@@ -706,7 +706,7 @@
 fn closure_state() {
     check_pass(
         r#"
-//- minicore: fn, add, copy
+//- minicore: fn, add, copy, builtin_impls
 fn should_not_reach() {
     _ // FIXME: replace this function with panic when that works
 }
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 1439c43..6704012 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -33,6 +33,7 @@
     infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy},
     inhabitedness::is_ty_uninhabited_from,
     layout::LayoutError,
+    method_resolution::CandidateId,
     mir::{
         AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr,
         FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan,
@@ -388,15 +389,15 @@
                     );
                     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_rustc(*m);
-                    self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
-                    Ok(Some(current))
+                Adjust::Borrow(AutoBorrow::Ref(m)) => self.lower_expr_to_place_with_borrow_adjust(
+                    expr_id,
+                    place,
+                    current,
+                    rest,
+                    (*m).into(),
+                ),
+                Adjust::Borrow(AutoBorrow::RawPtr(m)) => {
+                    self.lower_expr_to_place_with_borrow_adjust(expr_id, place, current, rest, *m)
                 }
                 Adjust::Pointer(cast) => {
                     let Some((p, current)) =
@@ -421,6 +422,24 @@
         }
     }
 
+    fn lower_expr_to_place_with_borrow_adjust(
+        &mut self,
+        expr_id: ExprId,
+        place: Place<'db>,
+        current: BasicBlockId<'db>,
+        rest: &[Adjustment<'db>],
+        m: Mutability,
+    ) -> Result<'db, Option<BasicBlockId<'db>>> {
+        let Some((p, current)) =
+            self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)?
+        else {
+            return Ok(None);
+        };
+        let bk = BorrowKind::from_rustc(m);
+        self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
+        Ok(Some(current))
+    }
+
     fn lower_expr_to_place(
         &mut self,
         expr_id: ExprId,
@@ -460,18 +479,14 @@
                 let pr =
                     if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
                         match assoc {
-                            hir_def::AssocItemId::ConstId(c) => {
+                            CandidateId::ConstId(c) => {
                                 self.lower_const(c.into(), current, place, subst, expr_id.into())?;
                                 return Ok(Some(current));
                             }
-                            hir_def::AssocItemId::FunctionId(_) => {
+                            CandidateId::FunctionId(_) => {
                                 // FnDefs are zero sized, no action is needed.
                                 return Ok(Some(current));
                             }
-                            hir_def::AssocItemId::TypeAliasId(_) => {
-                                // FIXME: If it is unreachable, use proper error instead of `not_supported`.
-                                not_supported!("associated functions and types")
-                            }
                         }
                     } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
                         match variant {
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 52f1412..bceafae 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -193,7 +193,7 @@
                     return self.lower_overloaded_deref(
                         current,
                         p,
-                        self.expr_ty_after_adjustments(*expr),
+                        self.expr_ty_without_adjust(*expr),
                         self.expr_ty_without_adjust(expr_id),
                         expr_id.into(),
                         'b: {
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index b1b86ab..c722a80 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,9 +1,8 @@
 //! MIR lowering for patterns
 
-use hir_def::{AssocItemId, hir::ExprId, signatures::VariantFields};
+use hir_def::{hir::ExprId, signatures::VariantFields};
 use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _};
 
-use crate::next_solver::GenericArgs;
 use crate::{
     BindingMode,
     mir::{
@@ -16,6 +15,7 @@
         },
     },
 };
+use crate::{method_resolution::CandidateId, next_solver::GenericArgs};
 
 macro_rules! not_supported {
     ($x: expr) => {
@@ -393,7 +393,7 @@
                     }
                     let (c, subst) = 'b: {
                         if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern)
-                            && let AssocItemId::ConstId(c) = x.0
+                            && let CandidateId::ConstId(c) = x.0
                         {
                             break 'b (c, x.1);
                         }
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index 77f2106..2c30922 100644
--- a/crates/hir-ty/src/next_solver/def_id.rs
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -1,8 +1,9 @@
 //! Definition of `SolverDefId`
 
 use hir_def::{
-    AdtId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
-    GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+    AdtId, AttrDefId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
+    GeneralConstId, GenericDefId, HasModule, ImplId, ModuleId, StaticId, StructId, TraitId,
+    TypeAliasId, UnionId, db::DefDatabase,
 };
 use rustc_type_ir::inherent;
 use stdx::impl_from;
@@ -154,6 +155,28 @@
     }
 }
 
+impl TryFrom<SolverDefId> for AttrDefId {
+    type Error = ();
+    #[inline]
+    fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
+        match value {
+            SolverDefId::AdtId(it) => Ok(it.into()),
+            SolverDefId::ConstId(it) => Ok(it.into()),
+            SolverDefId::FunctionId(it) => Ok(it.into()),
+            SolverDefId::ImplId(it) => Ok(it.into()),
+            SolverDefId::StaticId(it) => Ok(it.into()),
+            SolverDefId::TraitId(it) => Ok(it.into()),
+            SolverDefId::TypeAliasId(it) => Ok(it.into()),
+            SolverDefId::EnumVariantId(it) => Ok(it.into()),
+            SolverDefId::Ctor(Ctor::Struct(it)) => Ok(it.into()),
+            SolverDefId::Ctor(Ctor::Enum(it)) => Ok(it.into()),
+            SolverDefId::InternedClosureId(_)
+            | SolverDefId::InternedCoroutineId(_)
+            | SolverDefId::InternedOpaqueTyId(_) => Err(()),
+        }
+    }
+}
+
 impl TryFrom<SolverDefId> for DefWithBodyId {
     type Error = ();
 
@@ -218,6 +241,28 @@
     }
 }
 
+impl HasModule for SolverDefId {
+    fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+        match *self {
+            SolverDefId::AdtId(id) => id.module(db),
+            SolverDefId::ConstId(id) => id.module(db),
+            SolverDefId::FunctionId(id) => id.module(db),
+            SolverDefId::ImplId(id) => id.module(db),
+            SolverDefId::StaticId(id) => id.module(db),
+            SolverDefId::TraitId(id) => id.module(db),
+            SolverDefId::TypeAliasId(id) => id.module(db),
+            SolverDefId::InternedClosureId(id) => id.loc(db).0.module(db),
+            SolverDefId::InternedCoroutineId(id) => id.loc(db).0.module(db),
+            SolverDefId::InternedOpaqueTyId(id) => match id.loc(db) {
+                crate::ImplTraitId::ReturnTypeImplTrait(owner, _) => owner.module(db),
+                crate::ImplTraitId::TypeAliasImplTrait(owner, _) => owner.module(db),
+            },
+            SolverDefId::Ctor(Ctor::Enum(id)) | SolverDefId::EnumVariantId(id) => id.module(db),
+            SolverDefId::Ctor(Ctor::Struct(id)) => id.module(db),
+        }
+    }
+}
+
 impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
     fn as_local(self) -> Option<SolverDefId> {
         Some(self)
diff --git a/crates/hir-ty/src/next_solver/fold.rs b/crates/hir-ty/src/next_solver/fold.rs
index f776b6e..7836419 100644
--- a/crates/hir-ty/src/next_solver/fold.rs
+++ b/crates/hir-ty/src/next_solver/fold.rs
@@ -5,7 +5,7 @@
     TypeVisitableExt, inherent::IntoKind,
 };
 
-use crate::next_solver::BoundConst;
+use crate::next_solver::{BoundConst, FxIndexMap};
 
 use super::{
     Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind,
@@ -158,3 +158,65 @@
 
     t.fold_with(&mut Folder { interner, callback })
 }
+
+impl<'db> DbInterner<'db> {
+    /// Replaces all regions bound by the given `Binder` with the
+    /// results returned by the closure; the closure is expected to
+    /// return a free region (relative to this binder), and hence the
+    /// binder is removed in the return type. The closure is invoked
+    /// once for each unique `BoundRegionKind`; multiple references to the
+    /// same `BoundRegionKind` will reuse the previous result. A map is
+    /// returned at the end with each bound region and the free region
+    /// that replaced it.
+    ///
+    /// # Panics
+    ///
+    /// This method only replaces late bound regions. Any types or
+    /// constants bound by `value` will cause an ICE.
+    pub fn instantiate_bound_regions<T, F>(
+        self,
+        value: Binder<'db, T>,
+        mut fld_r: F,
+    ) -> (T, FxIndexMap<BoundRegion, Region<'db>>)
+    where
+        F: FnMut(BoundRegion) -> Region<'db>,
+        T: TypeFoldable<DbInterner<'db>>,
+    {
+        let mut region_map = FxIndexMap::default();
+        let real_fld_r = |br: BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+        let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
+        (value, region_map)
+    }
+
+    pub fn instantiate_bound_regions_uncached<T, F>(
+        self,
+        value: Binder<'db, T>,
+        mut replace_regions: F,
+    ) -> T
+    where
+        F: FnMut(BoundRegion) -> Region<'db>,
+        T: TypeFoldable<DbInterner<'db>>,
+    {
+        let value = value.skip_binder();
+        if !value.has_escaping_bound_vars() {
+            value
+        } else {
+            let delegate = FnMutDelegate {
+                regions: &mut replace_regions,
+                types: &mut |b| panic!("unexpected bound ty in binder: {b:?}"),
+                consts: &mut |b| panic!("unexpected bound ct in binder: {b:?}"),
+            };
+            let mut replacer = BoundVarReplacer::new(self, delegate);
+            value.fold_with(&mut replacer)
+        }
+    }
+
+    /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
+    /// method lookup and a few other places where precise region relationships are not required.
+    pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'db, T>) -> T
+    where
+        T: TypeFoldable<DbInterner<'db>>,
+    {
+        self.instantiate_bound_regions(value, |_| Region::new_erased(self)).0
+    }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
index 7995545..1029a7f 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
@@ -10,8 +10,8 @@
 use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar};
 use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _};
 use rustc_type_ir::{
-    BoundVar, BoundVarIndexKind, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind,
-    TyVid, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
+    BoundVar, BoundVarIndexKind, DebruijnIndex, Flags, InferConst, RegionKind, TyVid, TypeFlags,
+    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
 };
 use smallvec::SmallVec;
 use tracing::debug;
@@ -19,7 +19,7 @@
 use crate::next_solver::infer::InferCtxt;
 use crate::next_solver::{
     Binder, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg,
-    ParamEnvAnd, Placeholder, Region, Ty, TyKind,
+    Placeholder, Region, Ty, TyKind,
 };
 
 /// When we canonicalize a value to form a query, we wind up replacing
@@ -66,33 +66,19 @@
     /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
     pub fn canonicalize_query<V>(
         &self,
-        value: ParamEnvAnd<'db, V>,
+        value: V,
         query_state: &mut OriginalQueryValues<'db>,
-    ) -> CanonicalQueryInput<DbInterner<'db>, ParamEnvAnd<'db, V>>
+    ) -> Canonical<'db, V>
     where
         V: TypeFoldable<DbInterner<'db>>,
     {
-        let (param_env, value) = value.into_parts();
-        // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the
-        // `param_env` because they are treated differently by trait selection.
-        let canonical_param_env = Canonicalizer::canonicalize(
-            param_env,
-            self,
-            self.interner,
-            &CanonicalizeFreeRegionsOtherThanStatic,
-            query_state,
-        );
-
-        let canonical = Canonicalizer::canonicalize_with_base(
-            canonical_param_env,
+        Canonicalizer::canonicalize(
             value,
             self,
             self.interner,
             &CanonicalizeAllFreeRegions,
             query_state,
         )
-        .unchecked_map(|(param_env, value)| ParamEnvAnd { param_env, value });
-        CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }
     }
 
     /// Canonicalizes a query *response* `V`. When we canonicalize a
@@ -285,26 +271,6 @@
     }
 }
 
-struct CanonicalizeFreeRegionsOtherThanStatic;
-
-impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic {
-    fn canonicalize_free_region<'db>(
-        &self,
-        canonicalizer: &mut Canonicalizer<'_, 'db>,
-        r: Region<'db>,
-    ) -> Region<'db> {
-        if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) }
-    }
-
-    fn any(&self) -> bool {
-        true
-    }
-
-    fn preserve_universes(&self) -> bool {
-        false
-    }
-}
-
 struct Canonicalizer<'cx, 'db> {
     /// Set to `None` to disable the resolution of inference variables.
     infcx: &'cx InferCtxt<'db>,
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
index 6360291..13c620c 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -1,21 +1,31 @@
 //! This module contains code to instantiate new values into a
-//! `Canonical<'tcx, T>`.
+//! `Canonical<'db, T>`.
 //!
 //! For an overview of what canonicalization is and how it fits into
 //! rustc, check out the [chapter in the rustc dev guide][c].
 //!
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
+use std::{fmt::Debug, iter};
+
 use crate::next_solver::{
-    BoundConst, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Clauses, Const, ConstKind,
-    DbInterner, GenericArg, Predicate, Region, RegionKind, Ty, TyKind, fold::FnMutDelegate,
+    BoundConst, BoundRegion, BoundTy, Canonical, CanonicalVarKind, CanonicalVarValues, Clauses,
+    Const, ConstKind, DbInterner, GenericArg, ParamEnv, Predicate, Region, RegionKind, Ty, TyKind,
+    fold::FnMutDelegate,
+    infer::{
+        InferCtxt, InferOk, InferResult,
+        canonical::{QueryRegionConstraints, QueryResponse, canonicalizer::OriginalQueryValues},
+        traits::{ObligationCause, PredicateObligations},
+    },
 };
 use rustc_hash::FxHashMap;
+use rustc_index::{Idx as _, IndexVec};
 use rustc_type_ir::{
-    BoundVarIndexKind, GenericArgKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
-    TypeVisitableExt,
+    BoundVar, BoundVarIndexKind, GenericArgKind, TypeFlags, TypeFoldable, TypeFolder,
+    TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
     inherent::{GenericArg as _, IntoKind, SliceLike},
 };
+use tracing::{debug, instrument};
 
 pub trait CanonicalExt<'db, V> {
     fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V
@@ -169,3 +179,331 @@
         c.super_fold_with(self)
     }
 }
+
+impl<'db> InferCtxt<'db> {
+    /// A version of `make_canonicalized_query_response` that does
+    /// not pack in obligations, for contexts that want to drop
+    /// pending obligations instead of treating them as an ambiguity (e.g.
+    /// typeck "probing" contexts).
+    ///
+    /// If you DO want to keep track of pending obligations (which
+    /// include all region obligations, so this includes all cases
+    /// that care about regions) with this function, you have to
+    /// do it yourself, by e.g., having them be a part of the answer.
+    pub fn make_query_response_ignoring_pending_obligations<T>(
+        &self,
+        inference_vars: CanonicalVarValues<'db>,
+        answer: T,
+    ) -> Canonical<'db, QueryResponse<'db, T>>
+    where
+        T: TypeFoldable<DbInterner<'db>>,
+    {
+        // While we ignore region constraints and pending obligations,
+        // we do return constrained opaque types to avoid unconstrained
+        // inference variables in the response. This is important as we want
+        // to check that opaques in deref steps stay unconstrained.
+        //
+        // This doesn't handle the more general case for non-opaques as
+        // ambiguous `Projection` obligations have same the issue.
+        let opaque_types = self
+            .inner
+            .borrow_mut()
+            .opaque_type_storage
+            .iter_opaque_types()
+            .map(|(k, v)| (k, v.ty))
+            .collect();
+
+        self.canonicalize_response(QueryResponse {
+            var_values: inference_vars,
+            region_constraints: QueryRegionConstraints::default(),
+            opaque_types,
+            value: answer,
+        })
+    }
+
+    /// Given the (canonicalized) result to a canonical query,
+    /// instantiates the result so it can be used, plugging in the
+    /// values from the canonical query. (Note that the result may
+    /// have been ambiguous; you should check the certainty level of
+    /// the query before applying this function.)
+    ///
+    /// To get a good understanding of what is happening here, check
+    /// out the [chapter in the rustc dev guide][c].
+    ///
+    /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#processing-the-canonicalized-query-result
+    pub fn instantiate_query_response_and_region_obligations<R>(
+        &self,
+        cause: &ObligationCause,
+        param_env: ParamEnv<'db>,
+        original_values: &OriginalQueryValues<'db>,
+        query_response: &Canonical<'db, QueryResponse<'db, R>>,
+    ) -> InferResult<'db, R>
+    where
+        R: TypeFoldable<DbInterner<'db>>,
+    {
+        let InferOk { value: result_args, obligations } =
+            self.query_response_instantiation(cause, param_env, original_values, query_response)?;
+
+        for predicate in &query_response.value.region_constraints.outlives {
+            let predicate = instantiate_value(self.interner, &result_args, *predicate);
+            self.register_outlives_constraint(predicate);
+        }
+
+        for assumption in &query_response.value.region_constraints.assumptions {
+            let assumption = instantiate_value(self.interner, &result_args, *assumption);
+            self.register_region_assumption(assumption);
+        }
+
+        let user_result: R =
+            query_response
+                .instantiate_projected(self.interner, &result_args, |q_r| q_r.value.clone());
+
+        Ok(InferOk { value: user_result, obligations })
+    }
+
+    /// Given the original values and the (canonicalized) result from
+    /// computing a query, returns an instantiation that can be applied
+    /// to the query result to convert the result back into the
+    /// original namespace.
+    ///
+    /// The instantiation also comes accompanied with subobligations
+    /// that arose from unification; these might occur if (for
+    /// example) we are doing lazy normalization and the value
+    /// assigned to a type variable is unified with an unnormalized
+    /// projection.
+    fn query_response_instantiation<R>(
+        &self,
+        cause: &ObligationCause,
+        param_env: ParamEnv<'db>,
+        original_values: &OriginalQueryValues<'db>,
+        query_response: &Canonical<'db, QueryResponse<'db, R>>,
+    ) -> InferResult<'db, CanonicalVarValues<'db>>
+    where
+        R: Debug + TypeFoldable<DbInterner<'db>>,
+    {
+        debug!(
+            "query_response_instantiation(original_values={:#?}, query_response={:#?})",
+            original_values, query_response,
+        );
+
+        let mut value = self.query_response_instantiation_guess(
+            cause,
+            param_env,
+            original_values,
+            query_response,
+        )?;
+
+        value.obligations.extend(
+            self.unify_query_response_instantiation_guess(
+                cause,
+                param_env,
+                original_values,
+                &value.value,
+                query_response,
+            )?
+            .into_obligations(),
+        );
+
+        Ok(value)
+    }
+
+    /// Given the original values and the (canonicalized) result from
+    /// computing a query, returns a **guess** at an instantiation that
+    /// can be applied to the query result to convert the result back
+    /// into the original namespace. This is called a **guess**
+    /// because it uses a quick heuristic to find the values for each
+    /// canonical variable; if that quick heuristic fails, then we
+    /// will instantiate fresh inference variables for each canonical
+    /// variable instead. Therefore, the result of this method must be
+    /// properly unified
+    #[instrument(level = "debug", skip(self, param_env))]
+    fn query_response_instantiation_guess<R>(
+        &self,
+        cause: &ObligationCause,
+        param_env: ParamEnv<'db>,
+        original_values: &OriginalQueryValues<'db>,
+        query_response: &Canonical<'db, QueryResponse<'db, R>>,
+    ) -> InferResult<'db, CanonicalVarValues<'db>>
+    where
+        R: Debug + TypeFoldable<DbInterner<'db>>,
+    {
+        // For each new universe created in the query result that did
+        // not appear in the original query, create a local
+        // superuniverse.
+        let mut universe_map = original_values.universe_map.clone();
+        let num_universes_in_query = original_values.universe_map.len();
+        let num_universes_in_response = query_response.max_universe.as_usize() + 1;
+        for _ in num_universes_in_query..num_universes_in_response {
+            universe_map.push(self.create_next_universe());
+        }
+        assert!(!universe_map.is_empty()); // always have the root universe
+        assert_eq!(universe_map[UniverseIndex::ROOT.as_usize()], UniverseIndex::ROOT);
+
+        // Every canonical query result includes values for each of
+        // the inputs to the query. Therefore, we begin by unifying
+        // these values with the original inputs that were
+        // canonicalized.
+        let result_values = &query_response.value.var_values;
+        assert_eq!(original_values.var_values.len(), result_values.len());
+
+        // Quickly try to find initial values for the canonical
+        // variables in the result in terms of the query. We do this
+        // by iterating down the values that the query gave to each of
+        // the canonical inputs. If we find that one of those values
+        // is directly equal to one of the canonical variables in the
+        // result, then we can type the corresponding value from the
+        // input. See the example above.
+        let mut opt_values: IndexVec<BoundVar, Option<GenericArg<'db>>> =
+            IndexVec::from_elem_n(None, query_response.variables.len());
+
+        for (original_value, result_value) in iter::zip(&original_values.var_values, result_values)
+        {
+            match result_value.kind() {
+                GenericArgKind::Type(result_value) => {
+                    // We disable the instantiation guess for inference variables
+                    // and only use it for placeholders. We need to handle the
+                    // `sub_root` of type inference variables which would make this
+                    // more involved. They are also a lot rarer than region variables.
+                    if let TyKind::Bound(index_kind, b) = result_value.kind()
+                        && !matches!(
+                            query_response.variables.as_slice()[b.var.as_usize()],
+                            CanonicalVarKind::Ty { .. }
+                        )
+                    {
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, BoundVarIndexKind::Canonical));
+                        opt_values[b.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Lifetime(result_value) => {
+                    if let RegionKind::ReBound(index_kind, b) = result_value.kind() {
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, BoundVarIndexKind::Canonical));
+                        opt_values[b.var] = Some(*original_value);
+                    }
+                }
+                GenericArgKind::Const(result_value) => {
+                    if let ConstKind::Bound(index_kind, b) = result_value.kind() {
+                        // We only allow a `Canonical` index in generic parameters.
+                        assert!(matches!(index_kind, BoundVarIndexKind::Canonical));
+                        opt_values[b.var] = Some(*original_value);
+                    }
+                }
+            }
+        }
+
+        // Create result arguments: if we found a value for a
+        // given variable in the loop above, use that. Otherwise, use
+        // a fresh inference variable.
+        let interner = self.interner;
+        let variables = query_response.variables;
+        let var_values =
+            CanonicalVarValues::instantiate(interner, variables, |var_values, kind| {
+                if kind.universe() != UniverseIndex::ROOT {
+                    // A variable from inside a binder of the query. While ideally these shouldn't
+                    // exist at all, we have to deal with them for now.
+                    self.instantiate_canonical_var(kind, var_values, |u| universe_map[u.as_usize()])
+                } else if kind.is_existential() {
+                    match opt_values[BoundVar::new(var_values.len())] {
+                        Some(k) => k,
+                        None => self.instantiate_canonical_var(kind, var_values, |u| {
+                            universe_map[u.as_usize()]
+                        }),
+                    }
+                } else {
+                    // For placeholders which were already part of the input, we simply map this
+                    // universal bound variable back the placeholder of the input.
+                    opt_values[BoundVar::new(var_values.len())]
+                        .expect("expected placeholder to be unified with itself during response")
+                }
+            });
+
+        let mut obligations = PredicateObligations::new();
+
+        // Carry all newly resolved opaque types to the caller's scope
+        for &(a, b) in &query_response.value.opaque_types {
+            let a = instantiate_value(self.interner, &var_values, a);
+            let b = instantiate_value(self.interner, &var_values, b);
+            debug!(?a, ?b, "constrain opaque type");
+            // We use equate here instead of, for example, just registering the
+            // opaque type's hidden value directly, because the hidden type may have been an inference
+            // variable that got constrained to the opaque type itself. In that case we want to equate
+            // the generic args of the opaque with the generic params of its hidden type version.
+            obligations.extend(
+                self.at(cause, param_env)
+                    .eq(Ty::new_opaque(self.interner, a.def_id, a.args), b)?
+                    .obligations,
+            );
+        }
+
+        Ok(InferOk { value: var_values, obligations })
+    }
+
+    /// Given a "guess" at the values for the canonical variables in
+    /// the input, try to unify with the *actual* values found in the
+    /// query result. Often, but not always, this is a no-op, because
+    /// we already found the mapping in the "guessing" step.
+    ///
+    /// See also: [`Self::query_response_instantiation_guess`]
+    fn unify_query_response_instantiation_guess<R>(
+        &self,
+        cause: &ObligationCause,
+        param_env: ParamEnv<'db>,
+        original_values: &OriginalQueryValues<'db>,
+        result_args: &CanonicalVarValues<'db>,
+        query_response: &Canonical<'db, QueryResponse<'db, R>>,
+    ) -> InferResult<'db, ()>
+    where
+        R: Debug + TypeFoldable<DbInterner<'db>>,
+    {
+        // A closure that yields the result value for the given
+        // canonical variable; this is taken from
+        // `query_response.var_values` after applying the instantiation
+        // by `result_args`.
+        let instantiated_query_response = |index: BoundVar| -> GenericArg<'db> {
+            query_response
+                .instantiate_projected(self.interner, result_args, |v| v.var_values[index])
+        };
+
+        // Unify the original value for each variable with the value
+        // taken from `query_response` (after applying `result_args`).
+        self.unify_canonical_vars(cause, param_env, original_values, instantiated_query_response)
+    }
+
+    /// Given two sets of values for the same set of canonical variables, unify them.
+    /// The second set is produced lazily by supplying indices from the first set.
+    fn unify_canonical_vars(
+        &self,
+        cause: &ObligationCause,
+        param_env: ParamEnv<'db>,
+        variables1: &OriginalQueryValues<'db>,
+        variables2: impl Fn(BoundVar) -> GenericArg<'db>,
+    ) -> InferResult<'db, ()> {
+        let mut obligations = PredicateObligations::new();
+        for (index, value1) in variables1.var_values.iter().enumerate() {
+            let value2 = variables2(BoundVar::new(index));
+
+            match (value1.kind(), value2.kind()) {
+                (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
+                    obligations.extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
+                }
+                (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
+                    if re1.is_erased() && re2.is_erased() =>
+                {
+                    // no action needed
+                }
+                (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
+                    self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(v1, v2);
+                }
+                (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
+                    let ok = self.at(cause, param_env).eq(v1, v2)?;
+                    obligations.extend(ok.into_obligations());
+                }
+                _ => {
+                    panic!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
+                }
+            }
+        }
+        Ok(InferOk { value: (), obligations })
+    }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
index b3bd0a4..a0420a5 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
@@ -22,10 +22,12 @@
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
 use crate::next_solver::{
-    Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, PlaceholderConst,
-    PlaceholderRegion, PlaceholderTy, Region, Ty, TyKind, infer::InferCtxt,
+    ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg,
+    OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Region, Ty, TyKind,
+    infer::InferCtxt,
 };
 use instantiate::CanonicalExt;
+use macros::{TypeFoldable, TypeVisitable};
 use rustc_index::IndexVec;
 use rustc_type_ir::inherent::IntoKind;
 use rustc_type_ir::{CanonicalVarKind, InferTy, TypeFoldable, UniverseIndex, inherent::Ty as _};
@@ -135,3 +137,22 @@
         }
     }
 }
+
+/// After we execute a query with a canonicalized key, we get back a
+/// `Canonical<QueryResponse<..>>`. You can use
+/// `instantiate_query_result` to access the data in this result.
+#[derive(Clone, Debug, TypeVisitable, TypeFoldable)]
+pub struct QueryResponse<'db, R> {
+    pub var_values: CanonicalVarValues<'db>,
+    pub region_constraints: QueryRegionConstraints<'db>,
+    pub opaque_types: Vec<(OpaqueTypeKey<'db>, Ty<'db>)>,
+    pub value: R,
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
+pub struct QueryRegionConstraints<'db> {
+    pub outlives: Vec<QueryOutlivesConstraint<'db>>,
+    pub assumptions: Vec<ArgOutlivesPredicate<'db>>,
+}
+
+pub type QueryOutlivesConstraint<'tcx> = ArgOutlivesPredicate<'tcx>;
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
index 7b8f52b..fcce04f 100644
--- a/crates/hir-ty/src/next_solver/infer/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -29,9 +29,10 @@
 use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
 
 use crate::next_solver::{
-    BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext,
+    ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, Predicate,
+    SolverContext,
     fold::BoundVarReplacerDelegate,
-    infer::{select::EvaluationResult, traits::PredicateObligation},
+    infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation},
     obligation_ctxt::ObligationCtxt,
 };
 
@@ -47,6 +48,7 @@
 pub mod canonical;
 mod context;
 pub mod opaque_types;
+mod outlives;
 pub mod region_constraints;
 pub mod relate;
 pub mod resolve;
@@ -141,7 +143,14 @@
     /// for each body-id in this map, which will process the
     /// obligations within. This is expected to be done 'late enough'
     /// that all type inference variables have been bound and so forth.
-    pub(crate) region_obligations: Vec<RegionObligation<'db>>,
+    pub(crate) region_obligations: Vec<TypeOutlivesConstraint<'db>>,
+
+    /// The outlives bounds that we assume must hold about placeholders that
+    /// come from instantiating the binder of coroutine-witnesses. These bounds
+    /// are deduced from the well-formedness of the witness's types, and are
+    /// necessary because of the way we anonymize the regions in a coroutine,
+    /// which may cause types to no longer be considered well-formed.
+    region_assumptions: Vec<ArgOutlivesPredicate<'db>>,
 
     /// Caches for opaque type inference.
     pub(crate) opaque_type_storage: OpaqueTypeStorage<'db>,
@@ -158,12 +167,13 @@
             float_unification_storage: Default::default(),
             region_constraint_storage: Some(Default::default()),
             region_obligations: vec![],
+            region_assumptions: Default::default(),
             opaque_type_storage: Default::default(),
         }
     }
 
     #[inline]
-    pub fn region_obligations(&self) -> &[RegionObligation<'db>] {
+    pub fn region_obligations(&self) -> &[TypeOutlivesConstraint<'db>] {
         &self.region_obligations
     }
 
@@ -318,7 +328,7 @@
 
 /// See the `region_obligations` field for more information.
 #[derive(Clone, Debug)]
-pub struct RegionObligation<'db> {
+pub struct TypeOutlivesConstraint<'db> {
     pub sub_region: Region<'db>,
     pub sup_type: Ty<'db>,
 }
@@ -387,6 +397,12 @@
         self.typing_mode
     }
 
+    /// Evaluates whether the predicate can be satisfied (by any means)
+    /// in the given `ParamEnv`.
+    pub fn predicate_may_hold(&self, obligation: &PredicateObligation<'db>) -> bool {
+        self.evaluate_obligation(obligation).may_apply()
+    }
+
     /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank)
     /// for more details.
     pub fn predicate_may_hold_opaque_types_jank(
@@ -507,6 +523,22 @@
         })
     }
 
+    pub fn can_eq<T: ToTrace<'db>>(&self, param_env: ParamEnv<'db>, a: T, b: T) -> bool {
+        self.probe(|_| {
+            let mut ocx = ObligationCtxt::new(self);
+            let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else {
+                return false;
+            };
+            ocx.try_evaluate_obligations().is_empty()
+        })
+    }
+
+    /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank)
+    /// for more details.
+    pub fn goal_may_hold_opaque_types_jank(&self, goal: Goal<'db, Predicate<'db>>) -> bool {
+        <&SolverContext<'db>>::from(self).root_goal_may_hold_opaque_types_jank(goal)
+    }
+
     pub fn type_is_copy_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool {
         let ty = self.resolve_vars_if_possible(ty);
 
@@ -632,6 +664,14 @@
         self.inner.borrow_mut().type_variables().num_vars()
     }
 
+    pub fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> {
+        match id {
+            GenericParamId::TypeParamId(_) => self.next_ty_var().into(),
+            GenericParamId::ConstParamId(_) => self.next_const_var().into(),
+            GenericParamId::LifetimeParamId(_) => self.next_region_var().into(),
+        }
+    }
+
     pub fn next_ty_var(&self) -> Ty<'db> {
         self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None })
     }
@@ -846,6 +886,22 @@
         self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect()
     }
 
+    pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool {
+        let ty_sub_vid = self.sub_unification_table_root_var(ty_vid);
+        let inner = &mut *self.inner.borrow_mut();
+        let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log);
+        inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| {
+            if let TyKind::Infer(InferTy::TyVar(hidden_vid)) = hidden_ty.ty.kind() {
+                let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid);
+                if opaque_sub_vid == ty_sub_vid {
+                    return true;
+                }
+            }
+
+            false
+        })
+    }
+
     #[inline(always)]
     pub fn can_define_opaque_ty(&self, id: impl Into<SolverDefId>) -> bool {
         match self.typing_mode_unchecked() {
diff --git a/crates/hir-ty/src/next_solver/infer/outlives/mod.rs b/crates/hir-ty/src/next_solver/infer/outlives/mod.rs
new file mode 100644
index 0000000..321c4b8
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/outlives/mod.rs
@@ -0,0 +1 @@
+mod obligations;
diff --git a/crates/hir-ty/src/next_solver/infer/outlives/obligations.rs b/crates/hir-ty/src/next_solver/infer/outlives/obligations.rs
new file mode 100644
index 0000000..befb200
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/outlives/obligations.rs
@@ -0,0 +1,68 @@
+use ena::undo_log::UndoLogs;
+use rustc_type_ir::{OutlivesPredicate, TypeVisitableExt};
+use tracing::{debug, instrument};
+
+use crate::next_solver::{
+    ArgOutlivesPredicate, GenericArg, Region, RegionOutlivesPredicate, Ty,
+    infer::{InferCtxt, TypeOutlivesConstraint, snapshot::undo_log::UndoLog},
+};
+
+impl<'db> InferCtxt<'db> {
+    pub fn register_outlives_constraint(
+        &self,
+        OutlivesPredicate(arg, r2): ArgOutlivesPredicate<'db>,
+    ) {
+        match arg {
+            GenericArg::Lifetime(r1) => {
+                self.register_region_outlives_constraint(OutlivesPredicate(r1, r2));
+            }
+            GenericArg::Ty(ty1) => {
+                self.register_type_outlives_constraint(ty1, r2);
+            }
+            GenericArg::Const(_) => unreachable!(),
+        }
+    }
+
+    pub fn register_region_outlives_constraint(
+        &self,
+        OutlivesPredicate(r_a, r_b): RegionOutlivesPredicate<'db>,
+    ) {
+        // `'a: 'b` ==> `'b <= 'a`
+        self.sub_regions(r_b, r_a);
+    }
+
+    /// Registers that the given region obligation must be resolved
+    /// from within the scope of `body_id`. These regions are enqueued
+    /// and later processed by regionck, when full type information is
+    /// available (see `region_obligations` field for more
+    /// information).
+    #[instrument(level = "debug", skip(self))]
+    pub fn register_type_outlives_constraint_inner(&self, obligation: TypeOutlivesConstraint<'db>) {
+        let mut inner = self.inner.borrow_mut();
+        inner.undo_log.push(UndoLog::PushTypeOutlivesConstraint);
+        inner.region_obligations.push(obligation);
+    }
+
+    pub fn register_type_outlives_constraint(&self, sup_type: Ty<'db>, sub_region: Region<'db>) {
+        // `is_global` means the type has no params, infer, placeholder, or non-`'static`
+        // free regions. If the type has none of these things, then we can skip registering
+        // this outlives obligation since it has no components which affect lifetime
+        // checking in an interesting way.
+        if sup_type.is_global() {
+            return;
+        }
+
+        debug!(?sup_type, ?sub_region);
+
+        self.register_type_outlives_constraint_inner(TypeOutlivesConstraint {
+            sup_type,
+            sub_region,
+        });
+    }
+
+    pub fn register_region_assumption(&self, assumption: ArgOutlivesPredicate<'db>) {
+        let mut inner = self.inner.borrow_mut();
+        inner.undo_log.push(UndoLog::PushRegionAssumption);
+        inner.region_assumptions.push(assumption);
+    }
+}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
index c8ec8da..f246af1 100644
--- a/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
+++ b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
@@ -28,8 +28,8 @@
     FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>),
     RegionConstraintCollector(region_constraints::UndoLog<'db>),
     RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>),
-    #[expect(dead_code, reason = "this is used in rustc")]
-    PushRegionObligation,
+    PushTypeOutlivesConstraint,
+    PushRegionAssumption,
 }
 
 macro_rules! impl_from {
@@ -75,8 +75,13 @@
             UndoLog::RegionUnificationTable(undo) => {
                 self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
             }
-            UndoLog::PushRegionObligation => {
-                self.region_obligations.pop();
+            UndoLog::PushTypeOutlivesConstraint => {
+                let popped = self.region_obligations.pop();
+                assert!(popped.is_some(), "pushed region constraint but could not pop it");
+            }
+            UndoLog::PushRegionAssumption => {
+                let popped = self.region_assumptions.pop();
+                assert!(popped.is_some(), "pushed region assumption but could not pop it");
             }
         }
     }
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index b18e08b..39de17e 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -1,14 +1,15 @@
 //! Things related to the Interner in the next-trait-solver.
 
-use std::{fmt, ops::ControlFlow};
+use std::fmt;
 
+use rustc_ast_ir::{FloatTy, IntTy, UintTy};
 pub use tls_cache::clear_tls_solver_cache;
 pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db};
 
 use base_db::Crate;
 use hir_def::{
-    AdtId, AttrDefId, BlockId, CallableDefId, DefWithBodyId, EnumVariantId, ItemContainerId,
-    StructId, UnionId, VariantId,
+    AdtId, AttrDefId, BlockId, CallableDefId, DefWithBodyId, EnumVariantId, HasModule,
+    ItemContainerId, StructId, UnionId, VariantId,
     lang_item::LangItem,
     signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
 };
@@ -22,6 +23,7 @@
     TypeVisitableExt, UniverseIndex, Upcast, Variance,
     elaborate::elaborate,
     error::TypeError,
+    fast_reject,
     inherent::{self, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _},
     lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem},
     solve::SizedTraitKind,
@@ -30,12 +32,13 @@
 use crate::{
     FnAbi,
     db::{HirDatabase, InternedCoroutine, InternedCoroutineId},
-    method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
+    lower::GenericPredicates,
+    method_resolution::TraitImpls,
     next_solver::{
         AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
         CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, OpaqueTypeKey,
-        RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
-        util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls},
+        RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper,
+        TypeAliasIdWrapper, util::explicit_item_bounds,
     },
 };
 
@@ -583,6 +586,10 @@
         self.inner().flags.is_enum
     }
 
+    pub fn is_box(&self) -> bool {
+        self.inner().flags.is_box
+    }
+
     #[inline]
     pub fn repr(self) -> ReprOptions {
         self.inner().repr
@@ -1264,27 +1271,21 @@
         })
     }
 
-    #[tracing::instrument(skip(self), ret)]
+    #[tracing::instrument(skip(self))]
     fn item_bounds(
         self,
         def_id: Self::DefId,
     ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
-        explicit_item_bounds(self, def_id).map_bound(|bounds| {
-            Clauses::new_from_iter(self, elaborate(self, bounds).collect::<Vec<_>>())
-        })
+        explicit_item_bounds(self, def_id).map_bound(|bounds| elaborate(self, bounds))
     }
 
-    #[tracing::instrument(skip(self), ret)]
+    #[tracing::instrument(skip(self))]
     fn item_self_bounds(
         self,
         def_id: Self::DefId,
     ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
-        explicit_item_bounds(self, def_id).map_bound(|bounds| {
-            Clauses::new_from_iter(
-                self,
-                elaborate(self, bounds).filter_only_self().collect::<Vec<_>>(),
-            )
-        })
+        explicit_item_bounds(self, def_id)
+            .map_bound(|bounds| elaborate(self, bounds).filter_only_self())
     }
 
     fn item_non_self_bounds(
@@ -1309,9 +1310,8 @@
         self,
         def_id: Self::DefId,
     ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
-        let predicates = self.db().generic_predicates(def_id.try_into().unwrap());
-        let predicates: Vec<_> = predicates.iter().cloned().collect();
-        EarlyBinder::bind(predicates.into_iter())
+        GenericPredicates::query_all(self.db, def_id.try_into().unwrap())
+            .map_bound(|it| it.iter().copied())
     }
 
     #[tracing::instrument(level = "debug", skip(self), ret)]
@@ -1319,9 +1319,8 @@
         self,
         def_id: Self::DefId,
     ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
-        let predicates = self.db().generic_predicates_without_parent(def_id.try_into().unwrap());
-        let predicates: Vec<_> = predicates.iter().cloned().collect();
-        EarlyBinder::bind(predicates.into_iter())
+        GenericPredicates::query_own(self.db, def_id.try_into().unwrap())
+            .map_bound(|it| it.iter().copied())
     }
 
     #[tracing::instrument(skip(self), ret)]
@@ -1334,23 +1333,21 @@
             _ => false,
         };
 
-        let predicates: Vec<(Clause<'db>, Span)> = self
-            .db()
-            .generic_predicates(def_id.0.into())
-            .iter()
-            .filter(|p| match p.kind().skip_binder() {
-                // rustc has the following assertion:
-                // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608
-                rustc_type_ir::ClauseKind::Trait(it) => is_self(it.self_ty()),
-                rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self(it.0),
-                rustc_type_ir::ClauseKind::Projection(it) => is_self(it.self_ty()),
-                rustc_type_ir::ClauseKind::HostEffect(it) => is_self(it.self_ty()),
-                _ => false,
-            })
-            .cloned()
-            .map(|p| (p, Span::dummy()))
-            .collect();
-        EarlyBinder::bind(predicates)
+        GenericPredicates::query_explicit(self.db, def_id.0.into()).map_bound(move |predicates| {
+            predicates
+                .iter()
+                .copied()
+                .filter(move |p| match p.kind().skip_binder() {
+                    // rustc has the following assertion:
+                    // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608
+                    ClauseKind::Trait(it) => is_self(it.self_ty()),
+                    ClauseKind::TypeOutlives(it) => is_self(it.0),
+                    ClauseKind::Projection(it) => is_self(it.self_ty()),
+                    ClauseKind::HostEffect(it) => is_self(it.self_ty()),
+                    _ => false,
+                })
+                .map(|p| (p, Span::dummy()))
+        })
     }
 
     #[tracing::instrument(skip(self), ret)]
@@ -1368,25 +1365,25 @@
             }
         }
 
-        let predicates: Vec<(Clause<'db>, Span)> = self
-            .db()
-            .generic_predicates(def_id.try_into().unwrap())
-            .iter()
-            .filter(|p| match p.kind().skip_binder() {
-                rustc_type_ir::ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
-                rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
-                rustc_type_ir::ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
-                rustc_type_ir::ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
-                // FIXME: Not sure is this correct to allow other clauses but we might replace
-                // `generic_predicates_ns` query here with something closer to rustc's
-                // `implied_bounds_with_filter`, which is more granular lowering than this
-                // "lower at once and then filter" implementation.
-                _ => true,
-            })
-            .cloned()
-            .map(|p| (p, Span::dummy()))
-            .collect();
-        EarlyBinder::bind(predicates)
+        GenericPredicates::query_explicit(self.db, def_id.try_into().unwrap()).map_bound(
+            |predicates| {
+                predicates
+                    .iter()
+                    .copied()
+                    .filter(|p| match p.kind().skip_binder() {
+                        ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
+                        ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
+                        ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
+                        ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
+                        // FIXME: Not sure is this correct to allow other clauses but we might replace
+                        // `generic_predicates_ns` query here with something closer to rustc's
+                        // `implied_bounds_with_filter`, which is more granular lowering than this
+                        // "lower at once and then filter" implementation.
+                        _ => true,
+                    })
+                    .map(|p| (p, Span::dummy()))
+            },
+        )
     }
 
     fn impl_super_outlives(
@@ -1396,15 +1393,12 @@
         let trait_ref = self.db().impl_trait(impl_id.0).expect("expected an impl of trait");
         trait_ref.map_bound(|trait_ref| {
             let clause: Clause<'_> = trait_ref.upcast(self);
-            Clauses::new_from_iter(
-                self,
-                rustc_type_ir::elaborate::elaborate(self, [clause]).filter(|clause| {
-                    matches!(
-                        clause.kind().skip_binder(),
-                        ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_)
-                    )
-                }),
-            )
+            elaborate(self, [clause]).filter(|clause| {
+                matches!(
+                    clause.kind().skip_binder(),
+                    ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_)
+                )
+            })
         })
     }
 
@@ -1609,79 +1603,152 @@
 
     fn for_each_relevant_impl(
         self,
-        trait_: Self::TraitId,
+        trait_def_id: Self::TraitId,
         self_ty: Self::Ty,
         mut f: impl FnMut(Self::ImplId),
     ) {
-        let trait_ = trait_.0;
-        let self_ty_fp = TyFingerprint::for_trait_impl(self_ty);
-        let fps: &[TyFingerprint] = match self_ty.kind() {
-            TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS,
-            TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS,
-            _ => self_ty_fp.as_slice(),
+        let krate = self.krate.expect("trait solving requires setting `DbInterner::krate`");
+        let trait_block = trait_def_id.0.loc(self.db).container.containing_block();
+        let mut consider_impls_for_simplified_type = |simp: SimplifiedType| {
+            let type_block = simp.def().and_then(|def_id| {
+                let module = match def_id {
+                    SolverDefId::AdtId(AdtId::StructId(id)) => id.module(self.db),
+                    SolverDefId::AdtId(AdtId::EnumId(id)) => id.module(self.db),
+                    SolverDefId::AdtId(AdtId::UnionId(id)) => id.module(self.db),
+                    SolverDefId::TraitId(id) => id.module(self.db),
+                    SolverDefId::TypeAliasId(id) => id.module(self.db),
+                    SolverDefId::ConstId(_)
+                    | SolverDefId::FunctionId(_)
+                    | SolverDefId::ImplId(_)
+                    | SolverDefId::StaticId(_)
+                    | SolverDefId::InternedClosureId(_)
+                    | SolverDefId::InternedCoroutineId(_)
+                    | SolverDefId::InternedOpaqueTyId(_)
+                    | SolverDefId::EnumVariantId(_)
+                    | SolverDefId::Ctor(_) => return None,
+                };
+                module.containing_block()
+            });
+            TraitImpls::for_each_crate_and_block_trait_and_type(
+                self.db,
+                krate,
+                type_block,
+                trait_block,
+                &mut |impls| {
+                    for &impl_ in impls.for_trait_and_self_ty(trait_def_id.0, &simp) {
+                        f(impl_.into());
+                    }
+                },
+            );
         };
 
-        if fps.is_empty() {
-            _ = for_trait_impls(
-                self.db(),
-                self.krate.expect("Must have self.krate"),
-                self.block,
-                trait_,
-                self_ty_fp,
-                |impls| {
-                    for i in impls.for_trait(trait_) {
-                        use rustc_type_ir::TypeVisitable;
-                        let contains_errors = self.db().impl_trait(i).map_or(false, |b| {
-                            b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
-                        });
-                        if contains_errors {
-                            continue;
-                        }
+        match self_ty.kind() {
+            TyKind::Bool
+            | TyKind::Char
+            | TyKind::Int(_)
+            | TyKind::Uint(_)
+            | TyKind::Float(_)
+            | TyKind::Adt(_, _)
+            | TyKind::Foreign(_)
+            | TyKind::Str
+            | TyKind::Array(_, _)
+            | TyKind::Pat(_, _)
+            | TyKind::Slice(_)
+            | TyKind::RawPtr(_, _)
+            | TyKind::Ref(_, _, _)
+            | TyKind::FnDef(_, _)
+            | TyKind::FnPtr(..)
+            | TyKind::Dynamic(_, _)
+            | TyKind::Closure(..)
+            | TyKind::CoroutineClosure(..)
+            | TyKind::Coroutine(_, _)
+            | TyKind::Never
+            | TyKind::Tuple(_)
+            | TyKind::UnsafeBinder(_) => {
+                let simp =
+                    fast_reject::simplify_type(self, self_ty, fast_reject::TreatParams::AsRigid)
+                        .unwrap();
+                consider_impls_for_simplified_type(simp);
+            }
 
-                        f(i.into());
-                    }
-                    ControlFlow::Continue(())
-                },
-            );
-        } else {
-            _ = for_trait_impls(
-                self.db(),
-                self.krate.expect("Must have self.krate"),
-                self.block,
-                trait_,
-                self_ty_fp,
-                |impls| {
-                    for fp in fps {
-                        for i in impls.for_trait_and_self_ty(trait_, *fp) {
-                            use rustc_type_ir::TypeVisitable;
-                            let contains_errors = self.db().impl_trait(i).map_or(false, |b| {
-                                b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break()
-                            });
-                            if contains_errors {
-                                continue;
-                            }
+            // HACK: For integer and float variables we have to manually look at all impls
+            // which have some integer or float as a self type.
+            TyKind::Infer(InferTy::IntVar(_)) => {
+                use IntTy::*;
+                use UintTy::*;
+                // This causes a compiler error if any new integer kinds are added.
+                let (I8 | I16 | I32 | I64 | I128 | Isize): IntTy;
+                let (U8 | U16 | U32 | U64 | U128 | Usize): UintTy;
+                let possible_integers = [
+                    // signed integers
+                    SimplifiedType::Int(I8),
+                    SimplifiedType::Int(I16),
+                    SimplifiedType::Int(I32),
+                    SimplifiedType::Int(I64),
+                    SimplifiedType::Int(I128),
+                    SimplifiedType::Int(Isize),
+                    // unsigned integers
+                    SimplifiedType::Uint(U8),
+                    SimplifiedType::Uint(U16),
+                    SimplifiedType::Uint(U32),
+                    SimplifiedType::Uint(U64),
+                    SimplifiedType::Uint(U128),
+                    SimplifiedType::Uint(Usize),
+                ];
+                for simp in possible_integers {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
 
-                            f(i.into());
-                        }
-                    }
-                    ControlFlow::Continue(())
-                },
-            );
+            TyKind::Infer(InferTy::FloatVar(_)) => {
+                // This causes a compiler error if any new float kinds are added.
+                let (FloatTy::F16 | FloatTy::F32 | FloatTy::F64 | FloatTy::F128);
+                let possible_floats = [
+                    SimplifiedType::Float(FloatTy::F16),
+                    SimplifiedType::Float(FloatTy::F32),
+                    SimplifiedType::Float(FloatTy::F64),
+                    SimplifiedType::Float(FloatTy::F128),
+                ];
+
+                for simp in possible_floats {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            // The only traits applying to aliases and placeholders are blanket impls.
+            //
+            // Impls which apply to an alias after normalization are handled by
+            // `assemble_candidates_after_normalizing_self_ty`.
+            TyKind::Alias(_, _) | TyKind::Placeholder(..) | TyKind::Error(_) => (),
+
+            // FIXME: These should ideally not exist as a self type. It would be nice for
+            // the builtin auto trait impls of coroutines to instead directly recurse
+            // into the witness.
+            TyKind::CoroutineWitness(..) => (),
+
+            // These variants should not exist as a self type.
+            TyKind::Infer(
+                InferTy::TyVar(_)
+                | InferTy::FreshTy(_)
+                | InferTy::FreshIntTy(_)
+                | InferTy::FreshFloatTy(_),
+            )
+            | TyKind::Param(_)
+            | TyKind::Bound(_, _) => panic!("unexpected self type: {self_ty:?}"),
         }
+
+        self.for_each_blanket_impl(trait_def_id, f)
     }
 
     fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, mut f: impl FnMut(Self::ImplId)) {
         let Some(krate) = self.krate else { return };
+        let block = trait_def_id.0.loc(self.db).container.containing_block();
 
-        for impls in self.db.trait_impls_in_deps(krate).iter() {
-            for impl_id in impls.for_trait(trait_def_id.0) {
-                let impl_data = self.db.impl_signature(impl_id);
-                let self_ty_ref = &impl_data.store[impl_data.self_ty];
-                if matches!(self_ty_ref, hir_def::type_ref::TypeRef::TypeParam(_)) {
-                    f(impl_id.into());
-                }
+        TraitImpls::for_each_crate_and_block(self.db, krate, block, &mut |impls| {
+            for &impl_ in impls.blanket_impls(trait_def_id.0) {
+                f(impl_.into());
             }
-        }
+        });
     }
 
     fn has_item_definition(self, _def_id: Self::DefId) -> bool {
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs
index 3438b75..7cc3af7 100644
--- a/crates/hir-ty/src/next_solver/predicate.rs
+++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -13,7 +13,7 @@
 };
 use smallvec::SmallVec;
 
-use crate::next_solver::{InternedWrapperNoDebug, TraitIdWrapper};
+use crate::next_solver::{GenericArg, InternedWrapperNoDebug, TraitIdWrapper};
 
 use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db};
 
@@ -43,6 +43,7 @@
 pub type PolyTraitRef<'db> = Binder<'db, TraitRef<'db>>;
 pub type PolyExistentialTraitRef<'db> = Binder<'db, ExistentialTraitRef<'db>>;
 pub type PolyExistentialProjection<'db> = Binder<'db, ExistentialProjection<'db>>;
+pub type ArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>;
 
 /// Compares via an ordering that will not change if modules are reordered or other changes are
 /// made to the tree. In particular, this ordering is preserved across incremental compilations.
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs
index b5f0e6d..19f3c38 100644
--- a/crates/hir-ty/src/next_solver/region.rs
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -79,6 +79,10 @@
         matches!(self.inner(), RegionKind::ReStatic)
     }
 
+    pub fn is_erased(&self) -> bool {
+        matches!(self.inner(), RegionKind::ReErased)
+    }
+
     pub fn is_var(&self) -> bool {
         matches!(self.inner(), RegionKind::ReVar(_))
     }
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index 7b96b40..8fae340 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -11,13 +11,10 @@
 };
 use tracing::debug;
 
-use crate::{
-    ImplTraitId,
-    next_solver::{
-        AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
-        ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
-        util::sizedness_fast_path,
-    },
+use crate::next_solver::{
+    AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
+    ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
+    util::sizedness_fast_path,
 };
 
 use super::{
@@ -163,20 +160,7 @@
             })
         };
 
-        let db = interner.db;
-        let (opaques_table, opaque_idx) = match opaque_id.loc(db) {
-            ImplTraitId::ReturnTypeImplTrait(func, opaque_idx) => {
-                (db.return_type_impl_traits(func), opaque_idx)
-            }
-            ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx) => {
-                (db.type_alias_impl_traits(type_alias), opaque_idx)
-            }
-        };
-        let item_bounds = opaques_table
-            .as_deref()
-            .unwrap()
-            .as_ref()
-            .map_bound(|table| &table.impl_traits[opaque_idx].predicates);
+        let item_bounds = opaque_id.predicates(interner.db);
         for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) {
             let predicate = replace_opaques_in(predicate);
 
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index b8406fe..58849ce 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -25,8 +25,8 @@
 };
 
 use crate::{
-    ImplTraitId,
     db::{HirDatabase, InternedCoroutine},
+    lower::GenericPredicates,
     next_solver::{
         AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
         CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
@@ -41,6 +41,7 @@
     util::{FloatExt, IntegerExt},
 };
 
+pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<SolverDefId>;
 pub type TyKind<'db> = rustc_type_ir::TyKind<DbInterner<'db>>;
 pub type FnHeader<'db> = rustc_type_ir::FnHeader<DbInterner<'db>>;
 
@@ -127,6 +128,22 @@
         Ty::new_tup(interner, &[])
     }
 
+    pub fn new_imm_ptr(interner: DbInterner<'db>, ty: Ty<'db>) -> Self {
+        Ty::new_ptr(interner, ty, Mutability::Not)
+    }
+
+    pub fn new_imm_ref(interner: DbInterner<'db>, region: Region<'db>, ty: Ty<'db>) -> Self {
+        Ty::new_ref(interner, region, ty, Mutability::Not)
+    }
+
+    pub fn new_opaque(
+        interner: DbInterner<'db>,
+        def_id: SolverDefId,
+        args: GenericArgs<'db>,
+    ) -> Self {
+        Ty::new_alias(interner, AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args))
+    }
+
     /// Returns the `Size` for primitive types (bool, uint, int, char, float).
     pub fn primitive_size(self, interner: DbInterner<'db>) -> Size {
         match self.kind() {
@@ -327,11 +344,40 @@
     }
 
     #[inline]
+    pub fn is_bool(self) -> bool {
+        matches!(self.kind(), TyKind::Bool)
+    }
+
+    /// A scalar type is one that denotes an atomic datum, with no sub-components.
+    /// (A RawPtr is scalar because it represents a non-managed pointer, so its
+    /// contents are abstract to rustc.)
+    #[inline]
+    pub fn is_scalar(self) -> bool {
+        matches!(
+            self.kind(),
+            TyKind::Bool
+                | TyKind::Char
+                | TyKind::Int(_)
+                | TyKind::Float(_)
+                | TyKind::Uint(_)
+                | TyKind::FnDef(..)
+                | TyKind::FnPtr(..)
+                | TyKind::RawPtr(_, _)
+                | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_))
+        )
+    }
+
+    #[inline]
     pub fn is_infer(self) -> bool {
         matches!(self.kind(), TyKind::Infer(..))
     }
 
     #[inline]
+    pub fn is_numeric(self) -> bool {
+        self.is_integral() || self.is_floating_point()
+    }
+
+    #[inline]
     pub fn is_str(self) -> bool {
         matches!(self.kind(), TyKind::Str)
     }
@@ -346,10 +392,27 @@
         matches!(self.kind(), TyKind::RawPtr(..))
     }
 
+    #[inline]
+    pub fn is_array(self) -> bool {
+        matches!(self.kind(), TyKind::Array(..))
+    }
+
+    #[inline]
+    pub fn is_slice(self) -> bool {
+        matches!(self.kind(), TyKind::Slice(..))
+    }
+
     pub fn is_union(self) -> bool {
         self.as_adt().is_some_and(|(adt, _)| matches!(adt, AdtId::UnionId(_)))
     }
 
+    pub fn boxed_ty(self) -> Option<Ty<'db>> {
+        match self.kind() {
+            TyKind::Adt(adt_def, args) if adt_def.is_box() => Some(args.type_at(0)),
+            _ => None,
+        }
+    }
+
     #[inline]
     pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> {
         match self.kind() {
@@ -378,11 +441,9 @@
     ///
     /// The parameter `explicit` indicates if this is an *explicit* dereference.
     /// Some types -- notably raw ptrs -- can only be dereferenced explicitly.
-    pub fn builtin_deref(self, db: &dyn HirDatabase, explicit: bool) -> Option<Ty<'db>> {
+    pub fn builtin_deref(self, explicit: bool) -> Option<Ty<'db>> {
         match self.kind() {
-            TyKind::Adt(adt, substs) if crate::lang_items::is_box(db, adt.def_id().0) => {
-                Some(substs.as_slice()[0].expect_ty())
-            }
+            TyKind::Adt(adt, substs) if adt.is_box() => Some(substs.as_slice()[0].expect_ty()),
             TyKind::Ref(_, ty, _) => Some(ty),
             TyKind::RawPtr(ty, _) if explicit => Some(ty),
             _ => None,
@@ -562,26 +623,14 @@
         let interner = DbInterner::new_with(db, None, None);
 
         match self.kind() {
-            TyKind::Alias(AliasTyKind::Opaque, opaque_ty) => {
-                match db.lookup_intern_impl_trait_id(opaque_ty.def_id.expect_opaque_ty()) {
-                    ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                        db.return_type_impl_traits(func).map(|it| {
-                            let data =
-                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
-                            data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
-                                .collect()
-                        })
-                    }
-                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
-                        db.type_alias_impl_traits(alias).map(|it| {
-                            let data =
-                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
-                            data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
-                                .collect()
-                        })
-                    }
-                }
-            }
+            TyKind::Alias(AliasTyKind::Opaque, opaque_ty) => Some(
+                opaque_ty
+                    .def_id
+                    .expect_opaque_ty()
+                    .predicates(db)
+                    .iter_instantiated_copied(interner, opaque_ty.args.as_slice())
+                    .collect(),
+            ),
             TyKind::Param(param) => {
                 // FIXME: We shouldn't use `param.id` here.
                 let generic_params = db.generic_params(param.id.parent());
@@ -589,11 +638,8 @@
                 match param_data {
                     TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                         TypeParamProvenance::ArgumentImplTrait => {
-                            let predicates = db
-                                .generic_predicates(param.id.parent())
-                                .instantiate_identity()
-                                .into_iter()
-                                .flatten()
+                            let predicates = GenericPredicates::query_all(db, param.id.parent())
+                                .iter_identity_copied()
                                 .filter(|wc| match wc.kind().skip_binder() {
                                     ClauseKind::Trait(tr) => tr.self_ty() == self,
                                     ClauseKind::Projection(pred) => pred.self_ty() == self,
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index d113f76..972c8e2 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -1,39 +1,30 @@
 //! Various utilities for the next-trait-solver.
 
-use std::{
-    iter,
-    ops::{self, ControlFlow},
-};
+use std::ops::ControlFlow;
 
-use base_db::Crate;
-use hir_def::{BlockId, HasModule, lang_item::LangItem};
-use la_arena::Idx;
+use hir_def::TraitId;
 use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions};
 use rustc_type_ir::{
     ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, INNERMOST, IntTy, Interner,
     PredicatePolarity, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
-    TypeVisitableExt, TypeVisitor, UintTy, UniverseIndex,
-    inherent::{
-        AdtDef, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Ty as _,
-    },
+    TypeVisitableExt, TypeVisitor, UintTy, UniverseIndex, elaborate,
+    inherent::{AdtDef, GenericArg as _, IntoKind, ParamEnv as _, SliceLike, Ty as _},
     lang_items::SolverTraitLangItem,
     solve::SizedTraitKind,
 };
 
-use crate::{
-    db::HirDatabase,
-    lower::{LifetimeElisionKind, TyLoweringContext},
-    method_resolution::{TraitImpls, TyFingerprint},
-    next_solver::{
-        BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion,
-        infer::InferCtxt,
+use crate::next_solver::{
+    BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion,
+    PolyTraitRef,
+    infer::{
+        InferCtxt,
+        traits::{Obligation, ObligationCause, PredicateObligation},
     },
 };
 
 use super::{
-    Binder, BoundRegion, BoundTy, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder,
-    GenericArgs, Predicate, PredicateKind, Region, SolverDefId, TraitPredicate, TraitRef, Ty,
-    TyKind,
+    Binder, BoundRegion, BoundTy, Clause, ClauseKind, Const, DbInterner, EarlyBinder, GenericArgs,
+    Predicate, PredicateKind, Region, SolverDefId, Ty, TyKind,
     fold::{BoundVarReplacer, FnMutDelegate},
 };
 
@@ -388,54 +379,6 @@
     }
 }
 
-pub(crate) fn for_trait_impls(
-    db: &dyn HirDatabase,
-    krate: Crate,
-    block: Option<BlockId>,
-    trait_id: hir_def::TraitId,
-    self_ty_fp: Option<TyFingerprint>,
-    mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>,
-) -> ControlFlow<()> {
-    // Note: Since we're using `impls_for_trait` and `impl_provided_for`,
-    // only impls where the trait can be resolved should ever reach Chalk.
-    // `impl_datum` relies on that and will panic if the trait can't be resolved.
-    let in_self_and_deps = db.trait_impls_in_deps(krate);
-    let trait_module = trait_id.module(db);
-    let type_module = match self_ty_fp {
-        Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)),
-        Some(TyFingerprint::ForeignType(type_id)) => Some(type_id.module(db)),
-        Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)),
-        _ => None,
-    };
-
-    let mut def_blocks =
-        [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
-
-    let block_impls = iter::successors(block, |&block_id| {
-        cov_mark::hit!(block_local_impls);
-        block_id.loc(db).module.containing_block()
-    })
-    .inspect(|&block_id| {
-        // make sure we don't search the same block twice
-        def_blocks.iter_mut().for_each(|block| {
-            if *block == Some(block_id) {
-                *block = None;
-            }
-        });
-    })
-    .filter_map(|block_id| db.trait_impls_in_block(block_id));
-    for it in in_self_and_deps.iter().map(ops::Deref::deref) {
-        f(it)?;
-    }
-    for it in block_impls {
-        f(&it)?;
-    }
-    for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) {
-        f(&it)?;
-    }
-    ControlFlow::Continue(())
-}
-
 // FIXME(next-trait-solver): uplift
 pub fn sizedness_constraint_for_ty<'db>(
     interner: DbInterner<'db>,
@@ -507,79 +450,14 @@
 pub fn explicit_item_bounds<'db>(
     interner: DbInterner<'db>,
     def_id: SolverDefId,
-) -> EarlyBinder<'db, Clauses<'db>> {
+) -> EarlyBinder<'db, impl DoubleEndedIterator<Item = Clause<'db>> + ExactSizeIterator> {
     let db = interner.db();
-    match def_id {
-        SolverDefId::TypeAliasId(type_alias) => {
-            // Lower bounds -- we could/should maybe move this to a separate query in `lower`
-            let type_alias_data = db.type_alias_signature(type_alias);
-            let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
-            let mut ctx = TyLoweringContext::new(
-                db,
-                &resolver,
-                &type_alias_data.store,
-                type_alias.into(),
-                LifetimeElisionKind::AnonymousReportError,
-            );
-
-            let item_args = GenericArgs::identity_for_item(interner, def_id);
-            let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args);
-
-            let mut bounds = Vec::new();
-            for bound in &type_alias_data.bounds {
-                ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| {
-                    bounds.push(pred);
-                });
-            }
-
-            if !ctx.unsized_types.contains(&interner_ty) {
-                let sized_trait = LangItem::Sized
-                    .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate"));
-                let sized_bound = sized_trait.map(|trait_id| {
-                    let trait_ref = TraitRef::new_from_args(
-                        interner,
-                        trait_id.into(),
-                        GenericArgs::new_from_iter(interner, [interner_ty.into()]),
-                    );
-                    Clause(Predicate::new(
-                        interner,
-                        Binder::dummy(rustc_type_ir::PredicateKind::Clause(
-                            rustc_type_ir::ClauseKind::Trait(TraitPredicate {
-                                trait_ref,
-                                polarity: rustc_type_ir::PredicatePolarity::Positive,
-                            }),
-                        )),
-                    ))
-                });
-                bounds.extend(sized_bound);
-                bounds.shrink_to_fit();
-            }
-
-            rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds))
-        }
-        SolverDefId::InternedOpaqueTyId(id) => {
-            let full_id = db.lookup_intern_impl_trait_id(id);
-            match full_id {
-                crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                    let datas = db
-                        .return_type_impl_traits(func)
-                        .expect("impl trait id without impl traits");
-                    let datas = (*datas).as_ref().skip_binder();
-                    let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
-                    EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
-                }
-                crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
-                    let datas = db
-                        .type_alias_impl_traits(alias)
-                        .expect("impl trait id without impl traits");
-                    let datas = (*datas).as_ref().skip_binder();
-                    let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
-                    EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
-                }
-            }
-        }
+    let clauses = match def_id {
+        SolverDefId::TypeAliasId(type_alias) => crate::lower::type_alias_bounds(db, type_alias),
+        SolverDefId::InternedOpaqueTyId(id) => id.predicates(db),
         _ => panic!("Unexpected GenericDefId"),
-    }
+    };
+    clauses.map_bound(|clauses| clauses.iter().copied())
 }
 
 pub struct ContainsTypeErrors;
@@ -792,3 +670,34 @@
 
     false
 }
+
+/// Casts a trait reference into a reference to one of its super
+/// traits; returns `None` if `target_trait_def_id` is not a
+/// supertrait.
+pub(crate) fn upcast_choices<'db>(
+    interner: DbInterner<'db>,
+    source_trait_ref: PolyTraitRef<'db>,
+    target_trait_def_id: TraitId,
+) -> Vec<PolyTraitRef<'db>> {
+    if source_trait_ref.def_id().0 == target_trait_def_id {
+        return vec![source_trait_ref]; // Shortcut the most common case.
+    }
+
+    elaborate::supertraits(interner, source_trait_ref)
+        .filter(|r| r.def_id().0 == target_trait_def_id)
+        .collect()
+}
+
+#[inline]
+pub(crate) fn clauses_as_obligations<'db>(
+    clauses: impl IntoIterator<Item = Clause<'db>>,
+    cause: ObligationCause,
+    param_env: ParamEnv<'db>,
+) -> impl Iterator<Item = PredicateObligation<'db>> {
+    clauses.into_iter().map(move |clause| Obligation {
+        cause: cause.clone(),
+        param_env,
+        predicate: clause.as_predicate(),
+        recursion_depth: 0,
+    })
+}
diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs
index 8531f24..acf532c 100644
--- a/crates/hir-ty/src/opaques.rs
+++ b/crates/hir-ty/src/opaques.rs
@@ -7,7 +7,6 @@
 use la_arena::ArenaMap;
 use rustc_type_ir::inherent::Ty as _;
 use syntax::ast;
-use triomphe::Arc;
 
 use crate::{
     ImplTraitId,
@@ -29,7 +28,7 @@
         // A function may define its own RPITs.
         extend_with_opaques(
             db,
-            db.return_type_impl_traits(func),
+            ImplTraits::return_type_impl_traits(db, func),
             |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx),
             result,
         );
@@ -38,7 +37,7 @@
     let extend_with_taits = |type_alias| {
         extend_with_opaques(
             db,
-            db.type_alias_impl_traits(type_alias),
+            ImplTraits::type_alias_impl_traits(db, type_alias),
             |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx),
             result,
         );
@@ -75,12 +74,12 @@
 
     fn extend_with_opaques<'db>(
         db: &'db dyn HirDatabase,
-        opaques: Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>>,
+        opaques: &Option<Box<EarlyBinder<'db, ImplTraits<'db>>>>,
         mut make_impl_trait: impl FnMut(ImplTraitIdx<'db>) -> ImplTraitId<'db>,
         result: &mut Vec<SolverDefId>,
     ) {
         if let Some(opaques) = opaques {
-            for (opaque_idx, _) in (*opaques).as_ref().skip_binder().impl_traits.iter() {
+            for (opaque_idx, _) in (**opaques).as_ref().skip_binder().impl_traits.iter() {
                 let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
                 result.push(opaque_id.into());
             }
@@ -109,6 +108,14 @@
     db: &'db dyn HirDatabase,
     type_alias: TypeAliasId,
 ) -> ArenaMap<ImplTraitIdx<'db>, EarlyBinder<'db, Ty<'db>>> {
+    // Call this first, to not perform redundant work if there are no TAITs.
+    let Some(taits_count) = ImplTraits::type_alias_impl_traits(db, type_alias)
+        .as_deref()
+        .map(|taits| taits.as_ref().skip_binder().impl_traits.len())
+    else {
+        return ArenaMap::new();
+    };
+
     let loc = type_alias.loc(db);
     let module = loc.module(db);
     let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
@@ -119,10 +126,6 @@
 
     let defining_bodies = tait_defining_bodies(db, &loc);
 
-    let taits_count = db
-        .type_alias_impl_traits(type_alias)
-        .map_or(0, |taits| (*taits).as_ref().skip_binder().impl_traits.len());
-
     let mut result = ArenaMap::with_capacity(taits_count);
     for defining_body in defining_bodies {
         let infer = db.infer(defining_body);
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
index f4ee4de..304679d 100644
--- a/crates/hir-ty/src/specialization.rs
+++ b/crates/hir-ty/src/specialization.rs
@@ -2,17 +2,17 @@
 
 use hir_def::{ImplId, nameres::crate_def_map};
 use intern::sym;
+use rustc_type_ir::inherent::SliceLike;
 use tracing::debug;
 
 use crate::{
     db::HirDatabase,
+    lower::GenericPredicates,
     next_solver::{
         DbInterner, TypingMode,
-        infer::{
-            DbInternerInferExt,
-            traits::{Obligation, ObligationCause},
-        },
+        infer::{DbInternerInferExt, traits::ObligationCause},
         obligation_ctxt::ObligationCtxt,
+        util::clauses_as_obligations,
     },
 };
 
@@ -102,14 +102,12 @@
     // Now check that the source trait ref satisfies all the where clauses of the target impl.
     // This is not just for correctness; we also need this to constrain any params that may
     // only be referenced via projection predicates.
-    if let Some(predicates) =
-        db.generic_predicates(parent_impl_def_id.into()).instantiate(interner, parent_args)
-    {
-        ocx.register_obligations(
-            predicates
-                .map(|predicate| Obligation::new(interner, cause.clone(), param_env, predicate)),
-        );
-    }
+    ocx.register_obligations(clauses_as_obligations(
+        GenericPredicates::query_all(db, parent_impl_def_id.into())
+            .iter_instantiated_copied(interner, parent_args.as_slice()),
+        cause.clone(),
+        param_env,
+    ));
 
     let errors = ocx.evaluate_obligations_error_on_ambiguity();
     if !errors.is_empty() {
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 5a53db4..75800a0 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -49,7 +49,7 @@
 //- minicore: coerce_unsized
 fn test() {
     let x: &[isize] = &[1];
-                   // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize)
+                   // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
     let x: *const [isize] = &[1];
                          // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
 }
@@ -96,7 +96,7 @@
 fn test() {
     let x = if true {
         foo(&[1])
-         // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize)
+         // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
     } else {
         &[1]
     };
@@ -148,7 +148,7 @@
 fn test(i: i32) {
     let x = match i {
         2 => foo(&[2]),
-              // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize)
+              // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
         1 => &[1],
         _ => &[3],
     };
@@ -268,7 +268,7 @@
 fn returns_string() -> String { loop {} }
 fn test() {
     takes_ref_str(&{ returns_string() });
-               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('{region error}, Not))
+               // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
 }
 "#,
     );
@@ -854,8 +854,8 @@
 }
 fn test() {
     Struct == Struct;
- // ^^^^^^ adjustments: Borrow(Ref('{region error}, Not))
-           // ^^^^^^ adjustments: Borrow(Ref('{region error}, Not))
+ // ^^^^^^ adjustments: Borrow(Ref(Not))
+           // ^^^^^^ adjustments: Borrow(Ref(Not))
 }",
     );
 }
@@ -871,7 +871,7 @@
 }
 fn test() {
     Struct += Struct;
- // ^^^^^^ adjustments: Borrow(Ref('{region error}, Mut))
+ // ^^^^^^ adjustments: Borrow(Ref(Mut { allow_two_phase_borrow: Yes }))
            // ^^^^^^ adjustments:
 }",
     );
@@ -885,7 +885,7 @@
 fn test() {
     let x = [1, 2, 3];
     x[2] = 6;
- // ^ adjustments: Borrow(Ref('?0, Mut))
+ // ^ adjustments: Borrow(Ref(Mut { allow_two_phase_borrow: No }))
 }
     ",
     );
@@ -910,11 +910,11 @@
 }
 fn test() {
     Struct[0];
- // ^^^^^^ adjustments: Borrow(Ref('?0, Not))
+ // ^^^^^^ adjustments: Borrow(Ref(Not))
     StructMut[0];
- // ^^^^^^^^^ adjustments: Borrow(Ref('?1, Not))
+ // ^^^^^^^^^ adjustments: Borrow(Ref(Not))
     &mut StructMut[0];
-      // ^^^^^^^^^ adjustments: Borrow(Ref('?2, Mut))
+      // ^^^^^^^^^ adjustments: Borrow(Ref(Mut { allow_two_phase_borrow: No }))
 }",
     );
 }
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index 14ec161..e98e5e4 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -4,7 +4,7 @@
 use salsa::EventKind;
 use test_fixture::WithFixture;
 
-use crate::{db::HirDatabase, test_db::TestDB};
+use crate::{db::HirDatabase, method_resolution::TraitImpls, test_db::TestDB};
 
 use super::visit_module;
 
@@ -44,10 +44,12 @@
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
-                "return_type_impl_traits_shim",
-                "expr_scopes_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "lang_item",
                 "crate_lang_items",
+                "ImplTraits < 'db >::return_type_impl_traits_",
+                "expr_scopes_shim",
+                "lang_item",
             ]
         "#]],
     );
@@ -131,19 +133,22 @@
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
-                "return_type_impl_traits_shim",
-                "expr_scopes_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "lang_item",
                 "crate_lang_items",
                 "attrs_shim",
                 "attrs_shim",
+                "ImplTraits < 'db >::return_type_impl_traits_",
+                "expr_scopes_shim",
+                "lang_item",
                 "infer_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "expr_scopes_shim",
                 "infer_shim",
                 "function_signature_shim",
@@ -151,7 +156,8 @@
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "expr_scopes_shim",
             ]
         "#]],
@@ -230,9 +236,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "source_root_crates_shim",
@@ -241,7 +247,7 @@
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -267,9 +273,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "parse_shim",
@@ -277,7 +283,7 @@
                 "file_item_tree_query",
                 "real_span_map_shim",
                 "crate_local_def_map",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -302,9 +308,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "source_root_crates_shim",
@@ -313,7 +319,7 @@
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -340,9 +346,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "parse_shim",
@@ -350,7 +356,7 @@
                 "file_item_tree_query",
                 "real_span_map_shim",
                 "crate_local_def_map",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -375,9 +381,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "source_root_crates_shim",
@@ -386,7 +392,7 @@
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -410,9 +416,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "parse_shim",
@@ -420,7 +426,7 @@
                 "file_item_tree_query",
                 "real_span_map_shim",
                 "crate_local_def_map",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -449,9 +455,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "source_root_crates_shim",
@@ -460,7 +466,7 @@
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
             ]
         "#]],
     );
@@ -492,9 +498,9 @@
         || {
             let module = db.module_for_file(pos.file_id.file_id(&db));
             let _crate_def_map = module.def_map(&db);
-            db.trait_impls_in_crate(module.krate());
+            TraitImpls::for_crate(&db, module.krate());
         },
-        &[("trait_impls_in_crate_shim", 1)],
+        &[("TraitImpls::for_crate_", 1)],
         expect_test::expect![[r#"
             [
                 "parse_shim",
@@ -502,7 +508,7 @@
                 "file_item_tree_query",
                 "real_span_map_shim",
                 "crate_local_def_map",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
                 "attrs_shim",
                 "impl_trait_with_diagnostics_shim",
                 "impl_signature_shim",
@@ -581,37 +587,37 @@
                 "body_shim",
                 "body_with_source_map_shim",
                 "trait_environment_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "lang_item",
                 "crate_lang_items",
                 "attrs_shim",
                 "attrs_shim",
-                "generic_predicates_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "infer_shim",
                 "function_signature_shim",
                 "function_signature_with_source_map_shim",
                 "trait_environment_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "expr_scopes_shim",
                 "struct_signature_shim",
                 "struct_signature_with_source_map_shim",
-                "generic_predicates_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "value_ty_shim",
                 "VariantFields::firewall_",
                 "VariantFields::query_",
                 "lang_item",
-                "lang_item",
-                "inherent_impls_in_crate_shim",
+                "InherentImpls::for_crate_",
                 "impl_signature_shim",
                 "impl_signature_with_source_map_shim",
                 "callable_item_signature_shim",
-                "trait_impls_in_deps_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_and_deps_",
+                "TraitImpls::for_crate_",
                 "impl_trait_with_diagnostics_shim",
                 "impl_self_ty_with_diagnostics_shim",
-                "generic_predicates_shim",
-                "value_ty_shim",
-                "generic_predicates_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "lang_item",
             ]
         "#]],
     );
@@ -678,29 +684,29 @@
                 "function_signature_shim",
                 "body_with_source_map_shim",
                 "body_shim",
-                "trait_environment_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "crate_lang_items",
                 "attrs_shim",
                 "attrs_shim",
                 "attrs_shim",
-                "generic_predicates_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "infer_shim",
                 "function_signature_with_source_map_shim",
-                "return_type_impl_traits_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
+                "ImplTraits < 'db >::return_type_impl_traits_",
                 "expr_scopes_shim",
                 "struct_signature_with_source_map_shim",
-                "generic_predicates_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
                 "VariantFields::query_",
-                "inherent_impls_in_crate_shim",
+                "InherentImpls::for_crate_",
                 "impl_signature_with_source_map_shim",
                 "impl_signature_shim",
                 "callable_item_signature_shim",
-                "trait_impls_in_crate_shim",
+                "TraitImpls::for_crate_",
                 "impl_trait_with_diagnostics_shim",
                 "impl_self_ty_with_diagnostics_shim",
-                "generic_predicates_shim",
-                "generic_predicates_shim",
+                "GenericPredicates < 'db >::query_with_diagnostics_",
             ]
         "#]],
     );
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index b0afd60..274d33a 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -8,6 +8,7 @@
 fn infer_slice_method() {
     check_types(
         r#"
+//- /core.rs crate:core
 impl<T> [T] {
     #[rustc_allow_incoherent_impl]
     fn foo(&self) -> T {
@@ -27,13 +28,13 @@
 fn cross_crate_primitive_method() {
     check_types(
         r#"
-//- /main.rs crate:main deps:other_crate
+//- /main.rs crate:main deps:core
 fn test() {
     let x = 1f32;
     x.foo();
 } //^^^^^^^ f32
 
-//- /lib.rs crate:other_crate
+//- /lib.rs crate:core
 mod foo {
     impl f32 {
         #[rustc_allow_incoherent_impl]
@@ -48,6 +49,7 @@
 fn infer_array_inherent_impl() {
     check_types(
         r#"
+//- /core.rs crate:core
 impl<T, const N: usize> [T; N] {
     #[rustc_allow_incoherent_impl]
     fn foo(&self) -> T {
@@ -981,7 +983,6 @@
 
 #[test]
 fn method_resolution_overloaded_const() {
-    cov_mark::check!(const_candidate_self_type_mismatch);
     check_types(
         r#"
 struct Wrapper<T>(T);
@@ -1376,7 +1377,6 @@
 
 #[test]
 fn autoderef_visibility_method() {
-    cov_mark::check!(autoderef_candidate_not_visible);
     check(
         r#"
 //- minicore: receiver
@@ -1415,7 +1415,6 @@
 
 #[test]
 fn trait_vs_private_inherent_const() {
-    cov_mark::check!(const_candidate_not_visible);
     check(
         r#"
 mod a {
@@ -1505,6 +1504,7 @@
 fn resolve_const_generic_array_methods() {
     check_types(
         r#"
+//- /core.rs crate:core
 #[lang = "array"]
 impl<T, const N: usize> [T; N] {
     #[rustc_allow_incoherent_impl]
@@ -1536,6 +1536,7 @@
 fn resolve_const_generic_method() {
     check_types(
         r#"
+//- /core.rs crate:core
 struct Const<const N: usize>;
 
 #[lang = "array"]
@@ -1714,8 +1715,8 @@
             95..103 'u32::foo': fn foo<u32>() -> u8
             109..115 'S::foo': fn foo<S>() -> u8
             121..127 'T::foo': fn foo<T>() -> u8
-            133..139 'U::foo': {unknown}
-            145..157 '<[u32]>::foo': {unknown}
+            133..139 'U::foo': fn foo<U>() -> u8
+            145..157 '<[u32]>::foo': fn foo<[u32]>() -> u8
         "#]],
     );
 }
@@ -1869,6 +1870,7 @@
 "#,
     );
 }
+
 #[test]
 fn receiver_adjustment_autoref() {
     check(
@@ -1879,9 +1881,9 @@
 }
 fn test() {
     Foo.foo();
-  //^^^ adjustments: Borrow(Ref('?0, Not))
+  //^^^ adjustments: Borrow(Ref(Not))
     (&Foo).foo();
-  // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not))
+  // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
 }
 "#,
     );
@@ -1895,7 +1897,7 @@
 fn test() {
     let a = [1, 2, 3];
     a.len();
-} //^ adjustments: Borrow(Ref('?0, Not)), Pointer(Unsize)
+} //^ adjustments: Borrow(Ref(Not)), Pointer(Unsize)
 "#,
     );
 }
@@ -2036,6 +2038,7 @@
     check(
         r#"
 //- minicore: error, send
+//- /std.rs crate:std
 pub struct Box<T>(T);
 use core::error::Error;
 
@@ -2108,7 +2111,7 @@
 }
 fn test() {
     Box::new(Foo).foo();
-  //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?0, Not))
+  //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref(Not))
 }
 "#,
     );
@@ -2126,7 +2129,7 @@
 use core::mem::ManuallyDrop;
 fn test() {
     ManuallyDrop::new(Foo).foo();
-  //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?0, Not))
+  //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
 }
 "#,
     );
@@ -2176,6 +2179,8 @@
     check(
         r#"
 //- minicore: receiver
+#![feature(arbitrary_self_types)]
+
 use core::ops::Receiver;
 
 struct Foo;
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 607daad..aa1fff7 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -6,7 +6,7 @@
 fn infer_pattern() {
     check_infer(
         r#"
-        //- minicore: iterator
+        //- minicore: iterator, add, builtin_impls
         fn test(x: &i32) {
             let y = x;
             let &z = x;
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 75d3203..cbbca67 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -292,7 +292,7 @@
             149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}
             181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}
             191..313 'if ICE...     }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}
-            194..231 'ICE_RE..._VALUE': bool
+            194..231 'ICE_RE..._VALUE': {unknown}
             194..247 'ICE_RE...&name)': bool
             241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}
             242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}
@@ -629,7 +629,7 @@
             65..69 'self': Self
             267..271 'self': Self
             466..470 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
-            488..522 '{     ...     }': <SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> as BoxedDsl<DB>>::Output
+            488..522 '{     ...     }': {unknown}
             498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
             498..508 'self.order': O
             498..515 'self.o...into()': dyn QueryFragment<DB> + 'static
@@ -725,7 +725,7 @@
             138..146 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
             142..145 'key': &'? K
             162..165 'key': &'? K
-            224..227 '{ }': impl Future<Output = <K as Foo<R>>::Bar>
+            224..227 '{ }': ()
         "#]],
     );
 }
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index 90c81d1..933a9a5 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -180,7 +180,7 @@
     "#,
         expect![[r#"
             150..154 'self': &'a Grid
-            174..181 '{     }': <&'a Grid as IntoIterator>::IntoIter
+            174..181 '{     }': ()
         "#]],
     );
 }
@@ -414,7 +414,7 @@
             244..246 '_x': {unknown}
             249..257 'to_bytes': fn to_bytes() -> [u8; _]
             249..259 'to_bytes()': [u8; _]
-            249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item>
+            249..268 'to_byt..._vec()': {unknown}
         "#]],
     );
 }
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index c2392b3..478411f 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -645,10 +645,13 @@
 fn infer_binary_op() {
     check_infer(
         r#"
+//- minicore: add, builtin_impls
 fn f(x: bool) -> i32 {
     0i32
 }
 
+const CONST_2: isize = 0;
+
 fn test() -> bool {
     let x = a && b;
     let y = true || false;
@@ -658,8 +661,9 @@
     let h = minus_forty <= CONST_2;
     let c = f(z || y) + 5;
     let d = b;
-    let g = minus_forty ^= i;
+    let g = minus_forty += i;
     let ten: usize = 10;
+    let some_num = 0usize;
     let ten_is_eleven = ten == some_num;
 
     ten < 3
@@ -669,53 +673,56 @@
             5..6 'x': bool
             21..33 '{     0i32 }': i32
             27..31 '0i32': i32
-            53..369 '{     ... < 3 }': bool
-            63..64 'x': bool
-            67..68 'a': bool
-            67..73 'a && b': bool
-            72..73 'b': bool
-            83..84 'y': bool
-            87..91 'true': bool
-            87..100 'true || false': bool
-            95..100 'false': bool
-            110..111 'z': bool
-            114..115 'x': bool
-            114..120 'x == y': bool
-            119..120 'y': bool
-            130..131 't': bool
-            134..135 'x': bool
-            134..140 'x != y': bool
-            139..140 'y': bool
-            150..161 'minus_forty': isize
-            171..179 '-40isize': isize
-            172..179 '40isize': isize
-            189..190 'h': bool
-            193..204 'minus_forty': isize
-            193..215 'minus_...ONST_2': bool
-            208..215 'CONST_2': isize
-            225..226 'c': i32
-            229..230 'f': fn f(bool) -> i32
-            229..238 'f(z || y)': i32
-            229..242 'f(z || y) + 5': i32
-            231..232 'z': bool
-            231..237 'z || y': bool
-            236..237 'y': bool
-            241..242 '5': i32
-            252..253 'd': {unknown}
-            256..257 'b': {unknown}
-            267..268 'g': ()
-            271..282 'minus_forty': isize
-            271..287 'minus_...y ^= i': ()
-            286..287 'i': isize
-            297..300 'ten': usize
-            310..312 '10': usize
-            322..335 'ten_is_eleven': bool
-            338..341 'ten': usize
-            338..353 'ten == some_num': bool
-            345..353 'some_num': usize
-            360..363 'ten': usize
-            360..367 'ten < 3': bool
-            366..367 '3': usize
+            58..59 '0': isize
+            80..423 '{     ... < 3 }': bool
+            90..91 'x': bool
+            94..95 'a': bool
+            94..100 'a && b': bool
+            99..100 'b': bool
+            110..111 'y': bool
+            114..118 'true': bool
+            114..127 'true || false': bool
+            122..127 'false': bool
+            137..138 'z': bool
+            141..142 'x': bool
+            141..147 'x == y': bool
+            146..147 'y': bool
+            157..158 't': bool
+            161..162 'x': bool
+            161..167 'x != y': bool
+            166..167 'y': bool
+            177..188 'minus_forty': isize
+            198..206 '-40isize': isize
+            199..206 '40isize': isize
+            216..217 'h': bool
+            220..231 'minus_forty': isize
+            220..242 'minus_...ONST_2': bool
+            235..242 'CONST_2': isize
+            252..253 'c': i32
+            256..257 'f': fn f(bool) -> i32
+            256..265 'f(z || y)': i32
+            256..269 'f(z || y) + 5': i32
+            258..259 'z': bool
+            258..264 'z || y': bool
+            263..264 'y': bool
+            268..269 '5': i32
+            279..280 'd': {unknown}
+            283..284 'b': {unknown}
+            294..295 'g': ()
+            298..309 'minus_forty': isize
+            298..314 'minus_...y += i': ()
+            313..314 'i': isize
+            324..327 'ten': usize
+            337..339 '10': usize
+            349..357 'some_num': usize
+            360..366 '0usize': usize
+            376..389 'ten_is_eleven': bool
+            392..395 'ten': usize
+            392..407 'ten == some_num': bool
+            399..407 'some_num': usize
+            414..417 'ten': usize
+            414..421 'ten < 3': bool
+            420..421 '3': usize
         "#]],
     );
 }
@@ -1071,6 +1078,7 @@
 fn infer_inherent_method_str() {
     check_infer(
         r#"
+//- /core.rs crate:core
 #![rustc_coherence_is_core]
 #[lang = "str"]
 impl str {
@@ -2691,6 +2699,7 @@
 fn box_into_vec() {
     check_infer(
         r#"
+//- /core.rs crate:core
 #[lang = "sized"]
 pub trait Sized {}
 
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index c0e4393..eb4ae5e 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1,4 +1,3 @@
-use cov_mark::check;
 use expect_test::expect;
 
 use crate::tests::infer_with_mismatches;
@@ -278,11 +277,11 @@
 fn infer_ops_neg() {
     check_types(
         r#"
-//- /main.rs crate:main deps:std
+//- minicore:unary_ops
 struct Bar;
 struct Foo;
 
-impl std::ops::Neg for Bar {
+impl core::ops::Neg for Bar {
     type Output = Foo;
 }
 
@@ -291,15 +290,6 @@
     let b = -a;
     b;
 } //^ Foo
-
-//- /std.rs crate:std
-#[prelude_import] use ops::*;
-mod ops {
-    #[lang = "neg"]
-    pub trait Neg {
-        type Output;
-    }
-}
 "#,
     );
 }
@@ -308,11 +298,11 @@
 fn infer_ops_not() {
     check_types(
         r#"
-//- /main.rs crate:main deps:std
+//- minicore:unary_ops
 struct Bar;
 struct Foo;
 
-impl std::ops::Not for Bar {
+impl core::ops::Not for Bar {
     type Output = Foo;
 }
 
@@ -321,15 +311,6 @@
     let b = !a;
     b;
 } //^ Foo
-
-//- /std.rs crate:std
-#[prelude_import] use ops::*;
-mod ops {
-    #[lang = "not"]
-    pub trait Not {
-        type Output;
-    }
-}
 "#,
     );
 }
@@ -368,7 +349,6 @@
 
 #[test]
 fn trait_default_method_self_bound_implements_trait() {
-    cov_mark::check!(trait_self_implements_self);
     check(
         r#"
 trait Trait {
@@ -1211,7 +1191,7 @@
         expect![[r#"
             29..33 'self': &'? Self
             54..58 'self': &'? Self
-            98..100 '{}': impl Trait<u64>
+            98..100 '{}': ()
             110..111 'x': impl Trait<u64>
             130..131 'y': &'? impl Trait<u64>
             151..268 '{     ...2(); }': ()
@@ -2982,13 +2962,13 @@
             140..146 'IsCopy': IsCopy
             140..153 'IsCopy.test()': bool
             159..166 'NotCopy': NotCopy
-            159..173 'NotCopy.test()': {unknown}
+            159..173 'NotCopy.test()': bool
             179..195 '(IsCop...sCopy)': (IsCopy, IsCopy)
             179..202 '(IsCop...test()': bool
             180..186 'IsCopy': IsCopy
             188..194 'IsCopy': IsCopy
             208..225 '(IsCop...tCopy)': (IsCopy, NotCopy)
-            208..232 '(IsCop...test()': {unknown}
+            208..232 '(IsCop...test()': bool
             209..215 'IsCopy': IsCopy
             217..224 'NotCopy': NotCopy
         "#]],
@@ -3081,7 +3061,7 @@
             79..194 '{     ...ized }': ()
             85..88 '1u8': u8
             85..95 '1u8.test()': bool
-            101..116 '(*"foo").test()': {unknown}
+            101..116 '(*"foo").test()': bool
             102..108 '*"foo"': str
             103..108 '"foo"': &'static str
             135..145 '(1u8, 1u8)': (u8, u8)
@@ -3089,7 +3069,7 @@
             136..139 '1u8': u8
             141..144 '1u8': u8
             158..171 '(1u8, *"foo")': (u8, str)
-            158..178 '(1u8, ...test()': {unknown}
+            158..178 '(1u8, ...test()': bool
             159..162 '1u8': u8
             164..170 '*"foo"': str
             165..170 '"foo"': &'static str
@@ -3944,7 +3924,6 @@
 
 #[test]
 fn foreign_trait_with_local_trait_impl() {
-    check!(block_local_impls);
     check(
         r#"
 mod module {
@@ -3955,15 +3934,16 @@
 }
 
 fn f() {
+    struct Foo;
     use module::T;
-    impl T for usize {
+    impl T for Foo {
         const C: usize = 0;
         fn f(&self) {}
     }
-    0usize.f();
-  //^^^^^^^^^^ type: ()
-    usize::C;
-  //^^^^^^^^type: usize
+    Foo.f();
+  //^^^^^^^ type: ()
+    Foo::C;
+  //^^^^^^ type: usize
 }
 "#,
     );
@@ -4023,7 +4003,7 @@
             212..295 '{     ...ZED; }': ()
             218..239 'F::Exp..._SIZED': Yes
             245..266 'F::Imp..._SIZED': Yes
-            272..292 'F::Rel..._SIZED': {unknown}
+            272..292 'F::Rel..._SIZED': Yes
         "#]],
     );
 }
@@ -4274,7 +4254,7 @@
             127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static)
             164..195 '{     ...f(); }': ()
             170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + 'static)
-            170..184 'v.get::<i32>()': <dyn Trait<Assoc<i32> = &'a i32> + 'static as Trait>::Assoc<i32>
+            170..184 'v.get::<i32>()': <{unknown} as Trait>::Assoc<i32>
             170..192 'v.get:...eref()': {unknown}
         "#]],
     );
@@ -5051,3 +5031,28 @@
         "#]],
     );
 }
+
+#[test]
+fn implicit_sized_bound_on_param() {
+    check(
+        r#"
+//- minicore: sized
+struct PBox<T, A>(T, A);
+
+impl<T, A> PBox<T, A> {
+    fn token_with(self) {}
+}
+
+trait MoveMessage {
+    fn token<A>(self, alloc: A)
+    where
+        Self: Sized,
+    {
+        let b = PBox::<Self, A>(self, alloc);
+        b.token_with();
+     // ^^^^^^^^^^^^^^ type: ()
+    }
+}
+    "#,
+    );
+}
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 00c8eb7..2055c31 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -4,13 +4,18 @@
 use std::hash::Hash;
 
 use base_db::Crate;
-use hir_def::{BlockId, TraitId, lang_item::LangItem};
+use hir_def::{
+    AdtId, AssocItemId, BlockId, HasModule, ImplId, Lookup, TraitId,
+    lang_item::LangItem,
+    nameres::DefMap,
+    signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags},
+};
 use hir_expand::name::Name;
 use intern::sym;
 use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt};
 use rustc_type_ir::{
     TypingMode,
-    inherent::{IntoKind, Span as _},
+    inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _},
     solve::Certainty,
 };
 use triomphe::Arc;
@@ -263,3 +268,147 @@
     let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal);
     matches!(result, Ok((_, Certainty::Yes)))
 }
+
+pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool {
+    let self_ty = db.impl_self_ty(impl_id).instantiate_identity();
+    let self_ty = self_ty.kind();
+    let impl_allowed = match self_ty {
+        TyKind::Tuple(_)
+        | TyKind::FnDef(_, _)
+        | TyKind::Array(_, _)
+        | TyKind::Never
+        | TyKind::RawPtr(_, _)
+        | TyKind::Ref(_, _, _)
+        | TyKind::Slice(_)
+        | TyKind::Str
+        | TyKind::Bool
+        | TyKind::Char
+        | TyKind::Int(_)
+        | TyKind::Uint(_)
+        | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(),
+
+        TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate() == def_map.krate(),
+        TyKind::Dynamic(it, _) => it
+            .principal_def_id()
+            .is_some_and(|trait_id| trait_id.0.module(db).krate() == def_map.krate()),
+
+        _ => true,
+    };
+    impl_allowed || {
+        let rustc_has_incoherent_inherent_impls = match self_ty {
+            TyKind::Tuple(_)
+            | TyKind::FnDef(_, _)
+            | TyKind::Array(_, _)
+            | TyKind::Never
+            | TyKind::RawPtr(_, _)
+            | TyKind::Ref(_, _, _)
+            | TyKind::Slice(_)
+            | TyKind::Str
+            | TyKind::Bool
+            | TyKind::Char
+            | TyKind::Int(_)
+            | TyKind::Uint(_)
+            | TyKind::Float(_) => true,
+
+            TyKind::Adt(adt_def, _) => match adt_def.def_id().0 {
+                hir_def::AdtId::StructId(id) => db
+                    .struct_signature(id)
+                    .flags
+                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+                hir_def::AdtId::UnionId(id) => db
+                    .union_signature(id)
+                    .flags
+                    .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+                hir_def::AdtId::EnumId(it) => db
+                    .enum_signature(it)
+                    .flags
+                    .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
+            },
+            TyKind::Dynamic(it, _) => it.principal_def_id().is_some_and(|trait_id| {
+                db.trait_signature(trait_id.0)
+                    .flags
+                    .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
+            }),
+
+            _ => false,
+        };
+        let items = impl_id.impl_items(db);
+        rustc_has_incoherent_inherent_impls
+            && !items.items.is_empty()
+            && items.items.iter().all(|&(_, assoc)| match assoc {
+                AssocItemId::FunctionId(it) => {
+                    db.function_signature(it).flags.contains(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
+                }
+                AssocItemId::ConstId(it) => {
+                    db.const_signature(it).flags.contains(ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
+                }
+                AssocItemId::TypeAliasId(it) => db
+                    .type_alias_signature(it)
+                    .flags
+                    .contains(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
+            })
+    }
+}
+
+/// Checks whether the impl satisfies the orphan rules.
+///
+/// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an `impl`` is valid only if at least one of the following is true:
+/// - Trait is a local trait
+/// - All of
+///   - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
+///   - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
+pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool {
+    let Some(impl_trait) = db.impl_trait(impl_) else {
+        // not a trait impl
+        return true;
+    };
+
+    let local_crate = impl_.lookup(db).container.krate();
+    let is_local = |tgt_crate| tgt_crate == local_crate;
+
+    let trait_ref = impl_trait.instantiate_identity();
+    let trait_id = trait_ref.def_id.0;
+    if is_local(trait_id.module(db).krate()) {
+        // trait to be implemented is local
+        return true;
+    }
+
+    let unwrap_fundamental = |mut ty: Ty<'db>| {
+        // Unwrap all layers of fundamental types with a loop.
+        loop {
+            match ty.kind() {
+                TyKind::Ref(_, referenced, _) => ty = referenced,
+                TyKind::Adt(adt_def, subs) => {
+                    let AdtId::StructId(s) = adt_def.def_id().0 else {
+                        break ty;
+                    };
+                    let struct_signature = db.struct_signature(s);
+                    if struct_signature.flags.contains(StructFlags::FUNDAMENTAL) {
+                        let next = subs.types().next();
+                        match next {
+                            Some(it) => ty = it,
+                            None => break ty,
+                        }
+                    } else {
+                        break ty;
+                    }
+                }
+                _ => break ty,
+            }
+        }
+    };
+    //   - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
+
+    // FIXME: param coverage
+    //   - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
+    let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() {
+        TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate()),
+        TyKind::Error(_) => true,
+        TyKind::Dynamic(it, _) => {
+            it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate()))
+        }
+        _ => false,
+    });
+    #[allow(clippy::let_and_return)]
+    is_not_orphan
+}
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 147f1b8..cfc4080 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -1,7 +1,5 @@
 //! Attributes & documentation for hir types.
 
-use std::ops::ControlFlow;
-
 use hir_def::{
     AssocItemId, AttrDefId, ModuleDefId,
     attr::AttrsWithOwner,
@@ -14,7 +12,13 @@
     mod_path::{ModPath, PathKind},
     name::Name,
 };
-use hir_ty::{db::HirDatabase, method_resolution};
+use hir_ty::{
+    db::HirDatabase,
+    method_resolution::{
+        self, CandidateId, MethodError, MethodResolutionContext, MethodResolutionUnstableFeatures,
+    },
+    next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt},
+};
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -242,7 +246,7 @@
     name: &Name,
     ns: Option<Namespace>,
 ) -> Option<DocLinkDef> {
-    ty.iterate_assoc_items(db, ty.krate(db), move |assoc_item| {
+    ty.iterate_assoc_items(db, move |assoc_item| {
         if assoc_item.name(db)? != *name {
             return None;
         }
@@ -257,37 +261,39 @@
     name: &Name,
     ns: Option<Namespace>,
 ) -> Option<DocLinkDef> {
-    let canonical = ty.canonical(db);
     let krate = ty.krate(db);
     let environment = resolver
         .generic_def()
         .map_or_else(|| crate::TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
     let traits_in_scope = resolver.traits_in_scope(db);
 
-    let mut result = None;
-
     // `ty.iterate_path_candidates()` require a scope, which is not available when resolving
     // attributes here. Use path resolution directly instead.
     //
     // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
-    _ = method_resolution::iterate_path_candidates(
-        &canonical,
-        db,
-        environment,
-        &traits_in_scope,
-        method_resolution::VisibleFromModule::None,
-        Some(name),
-        &mut |_, assoc_item_id: AssocItemId, _| {
-            // If two traits in scope define the same item, Rustdoc links to no specific trait (for
-            // instance, given two methods `a`, Rustdoc simply links to `method.a` with no
-            // disambiguation) so we just pick the first one we find as well.
-            result = as_module_def_if_namespace_matches(assoc_item_id.into(), ns);
-
-            if result.is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
-        },
-    );
-
-    result
+    let interner = DbInterner::new_with(db, Some(environment.krate), environment.block);
+    let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+    let unstable_features =
+        MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map());
+    let ctx = MethodResolutionContext {
+        infcx: &infcx,
+        resolver: &resolver,
+        env: &environment,
+        traits_in_scope: &traits_in_scope,
+        edition: krate.edition(db),
+        unstable_features: &unstable_features,
+    };
+    let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty);
+    let resolution = match resolution {
+        Ok(resolution) => resolution.item,
+        Err(MethodError::PrivateMatch(resolution)) => resolution.item,
+        _ => return None,
+    };
+    let resolution = match resolution {
+        CandidateId::FunctionId(id) => AssocItem::Function(id.into()),
+        CandidateId::ConstId(id) => AssocItem::Const(id.into()),
+    };
+    as_module_def_if_namespace_matches(resolution, ns)
 }
 
 fn resolve_field(
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index d61c2ec..c215438 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -11,6 +11,7 @@
     type_ref::{TypeBound, TypeRef, TypeRefId},
 };
 use hir_ty::{
+    GenericPredicates,
     db::HirDatabase,
     display::{
         HirDisplay, HirDisplayError, HirDisplayWithExpressionStore, HirFormatter, SizedByDefault,
@@ -484,11 +485,9 @@
         let param_data = &params[self.id.local_id()];
         let krate = self.id.parent().krate(f.db).id;
         let ty = self.ty(f.db).ty;
-        let predicates = f.db.generic_predicates(self.id.parent());
+        let predicates = GenericPredicates::query_all(f.db, self.id.parent());
         let predicates = predicates
-            .instantiate_identity()
-            .into_iter()
-            .flatten()
+            .iter_identity_copied()
             .filter(|wc| match wc.kind().skip_binder() {
                 ClauseKind::Trait(tr) => tr.self_ty() == ty,
                 ClauseKind::Projection(proj) => proj.self_ty() == ty,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bb1741a..82c6cf7 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -75,26 +75,28 @@
     proc_macro::ProcMacroKind,
 };
 use hir_ty::{
-    TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef,
-    check_orphan_rules,
+    GenericPredicates, TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId,
+    all_super_traits, autoderef, check_orphan_rules,
     consteval::try_const_usize,
     db::{InternedClosureId, InternedCoroutineId},
     diagnostics::BodyValidationDiagnostic,
     direct_super_traits, known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
-    method_resolution,
+    method_resolution::{
+        self, InherentImpls, MethodResolutionContext, MethodResolutionUnstableFeatures,
+    },
     mir::{MutBorrowKind, interpret_mir},
     next_solver::{
-        AliasTy, Canonical, ClauseKind, ConstKind, DbInterner, ErrorGuaranteed, GenericArg,
-        GenericArgs, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode,
+        AliasTy, ClauseKind, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+        PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode,
         infer::{DbInternerInferExt, InferCtxt},
     },
-    traits::{self, FnTrait, structurally_normalize_ty},
+    traits::{self, FnTrait, is_inherent_impl_coherent, structurally_normalize_ty},
 };
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+    AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor, fast_reject,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
 };
 use smallvec::SmallVec;
@@ -136,7 +138,7 @@
         attr::{AttrSourceMap, Attrs, AttrsWithOwner},
         find_path::PrefixKind,
         import_map,
-        lang_item::LangItem,
+        lang_item::{LangItem, crate_lang_items},
         nameres::{DefMap, ModuleSource, crate_def_map},
         per_ns::Namespace,
         type_ref::{Mutability, TypeRef},
@@ -171,11 +173,12 @@
         drop::DropGlue,
         dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
         layout::LayoutError,
-        method_resolution::TyFingerprint,
         mir::{MirEvalError, MirLowerError},
         next_solver::abi::Safety,
         next_solver::clear_tls_solver_cache,
     },
+    // FIXME: These are needed for import assets, properly encapsulate them.
+    hir_ty::{method_resolution::TraitImpls, next_solver::SimplifiedType},
     intern::{Symbol, sym},
 };
 
@@ -754,8 +757,6 @@
         }
         self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
 
-        let inherent_impls = db.inherent_impls_in_crate(self.id.krate());
-
         let interner = DbInterner::new_with(db, Some(self.id.krate()), self.id.containing_block());
         let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
 
@@ -784,7 +785,9 @@
                 emit_def_diagnostic(db, acc, diag, edition);
             }
 
-            if inherent_impls.invalid_impls().contains(&impl_def.id) {
+            if impl_signature.target_trait.is_none()
+                && !is_inherent_impl_coherent(db, def_map, impl_def.id)
+            {
                 acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into())
             }
 
@@ -3350,6 +3353,15 @@
     TypeAlias(TypeAlias),
 }
 
+impl From<method_resolution::CandidateId> for AssocItem {
+    fn from(value: method_resolution::CandidateId) -> Self {
+        match value {
+            method_resolution::CandidateId::FunctionId(id) => AssocItem::Function(Function { id }),
+            method_resolution::CandidateId::ConstId(id) => AssocItem::Const(Const { id }),
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub enum AssocItemContainer {
     Trait(Trait),
@@ -3701,7 +3713,7 @@
         push_ty_diagnostics(
             db,
             acc,
-            db.generic_predicates_without_parent_with_diagnostics(def).1,
+            GenericPredicates::query_with_diagnostics(db, def).1.clone(),
             &source_map,
         );
         for (param_id, param) in generics.iter_type_or_consts() {
@@ -4196,10 +4208,13 @@
     /// parameter, not additional bounds that might be added e.g. by a method if
     /// the parameter comes from an impl!
     pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
-        db.generic_predicates_for_param(self.id.parent(), self.id.into(), None)
-            .iter()
+        let self_ty = self.ty(db).ty;
+        GenericPredicates::query_explicit(db, self.id.parent())
+            .iter_identity_copied()
             .filter_map(|pred| match &pred.kind().skip_binder() {
-                ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)),
+                ClauseKind::Trait(trait_ref) if trait_ref.self_ty() == self_ty => {
+                    Some(Trait::from(trait_ref.def_id().0))
+                }
                 _ => None,
             })
             .collect()
@@ -4361,90 +4376,81 @@
 
 impl Impl {
     pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> {
-        let inherent = db.inherent_impls_in_crate(krate.id);
-        let trait_ = db.trait_impls_in_crate(krate.id);
+        let mut result = Vec::new();
+        extend_with_def_map(db, crate_def_map(db, krate.id), &mut result);
+        return result;
 
-        inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
+        fn extend_with_def_map(db: &dyn HirDatabase, def_map: &DefMap, result: &mut Vec<Impl>) {
+            for (_, module) in def_map.modules() {
+                result.extend(module.scope.impls().map(Impl::from));
+
+                for unnamed_const in module.scope.unnamed_consts() {
+                    for (_, block_def_map) in db.body(unnamed_const.into()).blocks(db) {
+                        extend_with_def_map(db, block_def_map, result);
+                    }
+                }
+            }
+        }
     }
 
     pub fn all_in_module(db: &dyn HirDatabase, module: Module) -> Vec<Impl> {
         module.id.def_map(db)[module.id.local_id].scope.impls().map(Into::into).collect()
     }
 
+    /// **Note:** This is an **approximation** that strives to give the *human-perceived notion* of an "impl for type",
+    /// **not** answer the technical question "what are all impls applying to this type". In particular, it excludes
+    /// blanket impls, and only does a shallow type constructor check. In fact, this should've probably been on `Adt`
+    /// etc., and not on `Type`. If you would want to create a precise list of all impls applying to a type,
+    /// you would need to include blanket impls, and try to prove to predicates for each candidate.
     pub fn all_for_type<'db>(db: &'db dyn HirDatabase, Type { ty, env }: Type<'db>) -> Vec<Impl> {
-        let def_crates = match method_resolution::def_crates(db, ty, env.krate) {
-            Some(def_crates) => def_crates,
-            None => return Vec::new(),
+        let mut result = Vec::new();
+        let interner = DbInterner::new_with(db, Some(env.krate), env.block);
+        let Some(simplified_ty) =
+            fast_reject::simplify_type(interner, ty, fast_reject::TreatParams::AsRigid)
+        else {
+            return Vec::new();
         };
-
-        let filter = |impl_def: &Impl| {
-            let self_ty = impl_def.self_ty(db);
-            let rref = self_ty.remove_ref();
-            ty.equals_ctor(rref.as_ref().map_or(self_ty.ty, |it| it.ty))
-        };
-
-        let fp = TyFingerprint::for_inherent_impl(ty);
-        let fp = match fp {
-            Some(fp) => fp,
-            None => return Vec::new(),
-        };
-
-        let mut all = Vec::new();
-        def_crates.iter().for_each(|&id| {
-            all.extend(
-                db.inherent_impls_in_crate(id)
-                    .for_self_ty(ty)
-                    .iter()
-                    .cloned()
-                    .map(Self::from)
-                    .filter(filter),
-            )
-        });
-
-        for id in def_crates
-            .iter()
-            .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db))
-            .map(|Crate { id }| id)
-        {
-            all.extend(
-                db.trait_impls_in_crate(id)
-                    .for_self_ty_without_blanket_impls(fp)
-                    .map(Self::from)
-                    .filter(filter),
+        let mut extend_with_impls =
+            |impls: &[ImplId]| result.extend(impls.iter().copied().map(Impl::from));
+        extend_with_impls(method_resolution::incoherent_inherent_impls(db, simplified_ty));
+        if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) {
+            InherentImpls::for_each_crate_and_block(
+                db,
+                module.krate(),
+                module.containing_block(),
+                &mut |impls| extend_with_impls(impls.for_self_ty(&simplified_ty)),
             );
-        }
-
-        if let Some(block) = ty.as_adt().and_then(|(def, _)| def.module(db).containing_block()) {
-            if let Some(inherent_impls) = db.inherent_impls_in_block(block) {
-                all.extend(
-                    inherent_impls.for_self_ty(ty).iter().cloned().map(Self::from).filter(filter),
-                );
+            std::iter::successors(module.containing_block(), |block| {
+                block.loc(db).module.containing_block()
+            })
+            .filter_map(|block| TraitImpls::for_block(db, block).as_deref())
+            .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls));
+            for &krate in &**db.all_crates() {
+                TraitImpls::for_crate(db, krate)
+                    .for_self_ty(&simplified_ty, &mut extend_with_impls);
             }
-            if let Some(trait_impls) = db.trait_impls_in_block(block) {
-                all.extend(
-                    trait_impls
-                        .for_self_ty_without_blanket_impls(fp)
-                        .map(Self::from)
-                        .filter(filter),
-                );
+        } else {
+            for &krate in &**db.all_crates() {
+                TraitImpls::for_crate(db, krate)
+                    .for_self_ty(&simplified_ty, &mut extend_with_impls);
             }
         }
-
-        all
+        result
     }
 
     pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> {
-        let module = trait_.module(db);
-        let krate = module.krate();
+        let module = trait_.module(db).id;
         let mut all = Vec::new();
-        for Crate { id } in krate.transitive_reverse_dependencies(db) {
-            let impls = db.trait_impls_in_crate(id);
-            all.extend(impls.for_trait(trait_.id).map(Self::from))
+        let mut handle_impls = |impls: &TraitImpls| {
+            impls.for_trait(trait_.id, |impls| all.extend(impls.iter().copied().map(Impl::from)));
+        };
+        for krate in db.transitive_rev_deps(module.krate()) {
+            handle_impls(TraitImpls::for_crate(db, krate));
         }
-        if let Some(block) = module.id.containing_block()
-            && let Some(trait_impls) = db.trait_impls_in_block(block)
+        if let Some(block) = module.containing_block()
+            && let Some(impls) = TraitImpls::for_block(db, block)
         {
-            all.extend(trait_impls.for_trait(trait_.id).map(Self::from));
+            handle_impls(impls);
         }
         all
     }
@@ -5265,13 +5271,12 @@
         }
     }
 
-    pub fn fingerprint_for_trait_impl(&self) -> Option<TyFingerprint> {
-        TyFingerprint::for_trait_impl(self.ty)
-    }
-
-    pub(crate) fn canonical(&self, db: &'db dyn HirDatabase) -> Canonical<'db, Ty<'db>> {
-        let interner = DbInterner::new_with(db, None, None);
-        hir_ty::replace_errors_with_variables(interner, &self.ty)
+    pub fn fingerprint_for_trait_impl(&self) -> Option<SimplifiedType> {
+        fast_reject::simplify_type(
+            DbInterner::conjure(),
+            self.ty,
+            fast_reject::TreatParams::AsRigid,
+        )
     }
 
     /// Returns types that this type dereferences to (including this type itself). The returned
@@ -5295,11 +5300,10 @@
     pub fn iterate_assoc_items<T>(
         &self,
         db: &'db dyn HirDatabase,
-        krate: Crate,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
         let mut slot = None;
-        self.iterate_assoc_items_dyn(db, krate, &mut |assoc_item_id| {
+        self.iterate_assoc_items_dyn(db, &mut |assoc_item_id| {
             slot = callback(assoc_item_id.into());
             slot.is_some()
         });
@@ -5309,24 +5313,36 @@
     fn iterate_assoc_items_dyn(
         &self,
         db: &'db dyn HirDatabase,
-        krate: Crate,
         callback: &mut dyn FnMut(AssocItemId) -> bool,
     ) {
-        let ty_ns = self.ty;
-        let def_crates = match method_resolution::def_crates(db, ty_ns, krate.id) {
-            Some(it) => it,
-            None => return,
-        };
-        for krate in def_crates {
-            let impls = db.inherent_impls_in_crate(krate);
-
-            for impl_def in impls.for_self_ty(ty_ns) {
+        let mut handle_impls = |impls: &[ImplId]| {
+            for &impl_def in impls {
                 for &(_, item) in impl_def.impl_items(db).items.iter() {
                     if callback(item) {
                         return;
                     }
                 }
             }
+        };
+
+        let interner = DbInterner::new_with(db, None, None);
+        let Some(simplified_type) =
+            fast_reject::simplify_type(interner, self.ty, fast_reject::TreatParams::AsRigid)
+        else {
+            return;
+        };
+
+        handle_impls(method_resolution::incoherent_inherent_impls(db, simplified_type));
+
+        if let Some(module) = method_resolution::simplified_type_module(db, &simplified_type) {
+            InherentImpls::for_each_crate_and_block(
+                db,
+                module.krate(),
+                module.containing_block(),
+                &mut |impls| {
+                    handle_impls(impls.for_self_ty(&simplified_type));
+                },
+            );
         }
     }
 
@@ -5417,26 +5433,20 @@
         db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         mut callback: impl FnMut(Function) -> Option<T>,
     ) -> Option<T> {
         let _p = tracing::info_span!("iterate_method_candidates_with_traits").entered();
         let mut slot = None;
-        self.iterate_method_candidates_split_inherent(
-            db,
-            scope,
-            traits_in_scope,
-            with_local_impls,
-            name,
-            |f| match callback(f) {
+        self.iterate_method_candidates_split_inherent(db, scope, traits_in_scope, name, |f| {
+            match callback(f) {
                 it @ Some(_) => {
                     slot = it;
                     ControlFlow::Break(())
                 }
                 None => ControlFlow::Continue(()),
-            },
-        );
+            }
+        });
         slot
     }
 
@@ -5444,7 +5454,6 @@
         &self,
         db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         callback: impl FnMut(Function) -> Option<T>,
     ) -> Option<T> {
@@ -5452,12 +5461,37 @@
             db,
             scope,
             &scope.visible_traits().0,
-            with_local_impls,
             name,
             callback,
         )
     }
 
+    fn with_method_resolution<R>(
+        &self,
+        db: &'db dyn HirDatabase,
+        resolver: &Resolver<'db>,
+        traits_in_scope: &FxHashSet<TraitId>,
+        f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R,
+    ) -> R {
+        let module = resolver.module();
+        let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
+        let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+        let unstable_features =
+            MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map());
+        let environment = resolver
+            .generic_def()
+            .map_or_else(|| TraitEnvironment::empty(module.krate()), |d| db.trait_environment(d));
+        let ctx = MethodResolutionContext {
+            infcx: &infcx,
+            resolver,
+            env: &environment,
+            traits_in_scope,
+            edition: resolver.krate().data(db).edition,
+            unstable_features: &unstable_features,
+        };
+        f(&ctx)
+    }
+
     /// Allows you to treat inherent and non-inherent methods differently.
     ///
     /// Note that inherent methods may actually be trait methods! For example, in `dyn Trait`, the trait's methods
@@ -5467,67 +5501,77 @@
         db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
-        callback: impl MethodCandidateCallback,
+        mut callback: impl MethodCandidateCallback,
     ) {
-        struct Callback<T>(T);
-
-        impl<T: MethodCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> {
-            fn on_inherent_method(
-                &mut self,
-                _adjustments: method_resolution::ReceiverAdjustments,
-                item: AssocItemId,
-                _is_visible: bool,
-            ) -> ControlFlow<()> {
-                if let AssocItemId::FunctionId(func) = item {
-                    self.0.on_inherent_method(func.into())
-                } else {
-                    ControlFlow::Continue(())
-                }
-            }
-
-            fn on_trait_method(
-                &mut self,
-                _adjustments: method_resolution::ReceiverAdjustments,
-                item: AssocItemId,
-                _is_visible: bool,
-            ) -> ControlFlow<()> {
-                if let AssocItemId::FunctionId(func) = item {
-                    self.0.on_trait_method(func.into())
-                } else {
-                    ControlFlow::Continue(())
-                }
-            }
-        }
-
         let _p = tracing::info_span!(
-            "iterate_method_candidates_dyn",
-            with_local_impls = traits_in_scope.len(),
+            "iterate_method_candidates_split_inherent",
             traits_in_scope = traits_in_scope.len(),
             ?name,
         )
         .entered();
-        let interner = DbInterner::new_with(db, None, None);
-        // There should be no inference vars in types passed here
-        let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty);
 
-        let krate = scope.krate();
-        let environment = scope
-            .resolver()
-            .generic_def()
-            .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
+        self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| {
+            // There should be no inference vars in types passed here
+            let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty);
+            let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical);
 
-        _ = method_resolution::iterate_method_candidates_dyn(
-            &canonical,
-            db,
-            environment,
-            traits_in_scope,
-            with_local_impls.and_then(|b| b.id.containing_block()).into(),
-            name,
-            method_resolution::LookupMode::MethodCall,
-            &mut Callback(callback),
-        );
+            match name {
+                Some(name) => {
+                    match ctx.probe_for_name(
+                        method_resolution::Mode::MethodCall,
+                        name.clone(),
+                        self_ty,
+                    ) {
+                        Ok(candidate)
+                        | Err(method_resolution::MethodError::PrivateMatch(candidate)) => {
+                            let method_resolution::CandidateId::FunctionId(id) = candidate.item
+                            else {
+                                unreachable!("`Mode::MethodCall` can only return functions");
+                            };
+                            let id = Function { id };
+                            match candidate.kind {
+                                method_resolution::PickKind::InherentImplPick(_)
+                                | method_resolution::PickKind::ObjectPick(..)
+                                | method_resolution::PickKind::WhereClausePick(..) => {
+                                    // Candidates from where clauses and trait objects are considered inherent.
+                                    _ = callback.on_inherent_method(id);
+                                }
+                                method_resolution::PickKind::TraitPick(..) => {
+                                    _ = callback.on_trait_method(id);
+                                }
+                            }
+                        }
+                        Err(_) => {}
+                    };
+                }
+                None => {
+                    _ = ctx.probe_all(method_resolution::Mode::MethodCall, self_ty).try_for_each(
+                        |candidate| {
+                            let method_resolution::CandidateId::FunctionId(id) =
+                                candidate.candidate.item
+                            else {
+                                unreachable!("`Mode::MethodCall` can only return functions");
+                            };
+                            let id = Function { id };
+                            match candidate.candidate.kind {
+                                method_resolution::CandidateKind::InherentImplCandidate {
+                                    ..
+                                }
+                                | method_resolution::CandidateKind::ObjectCandidate(..)
+                                | method_resolution::CandidateKind::WhereClauseCandidate(..) => {
+                                    // Candidates from where clauses and trait objects are considered inherent.
+                                    callback.on_inherent_method(id)
+                                }
+                                method_resolution::CandidateKind::TraitCandidate(..) => {
+                                    callback.on_trait_method(id)
+                                }
+                            }
+                        },
+                    );
+                }
+            }
+        })
     }
 
     #[tracing::instrument(skip_all, fields(name = ?name))]
@@ -5536,27 +5580,21 @@
         db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
         let _p = tracing::info_span!("iterate_path_candidates").entered();
         let mut slot = None;
 
-        self.iterate_path_candidates_split_inherent(
-            db,
-            scope,
-            traits_in_scope,
-            with_local_impls,
-            name,
-            |item| match callback(item) {
+        self.iterate_path_candidates_split_inherent(db, scope, traits_in_scope, name, |item| {
+            match callback(item) {
                 it @ Some(_) => {
                     slot = it;
                     ControlFlow::Break(())
                 }
                 None => ControlFlow::Continue(()),
-            },
-        );
+            }
+        });
         slot
     }
 
@@ -5571,50 +5609,68 @@
         db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
-        with_local_impls: Option<Module>,
         name: Option<&Name>,
-        callback: impl PathCandidateCallback,
+        mut callback: impl PathCandidateCallback,
     ) {
-        struct Callback<T>(T);
+        let _p = tracing::info_span!(
+            "iterate_path_candidates_split_inherent",
+            traits_in_scope = traits_in_scope.len(),
+            ?name,
+        )
+        .entered();
 
-        impl<T: PathCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> {
-            fn on_inherent_method(
-                &mut self,
-                _adjustments: method_resolution::ReceiverAdjustments,
-                item: AssocItemId,
-                _is_visible: bool,
-            ) -> ControlFlow<()> {
-                self.0.on_inherent_item(item.into())
+        self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| {
+            // There should be no inference vars in types passed here
+            let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty);
+            let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical);
+
+            match name {
+                Some(name) => {
+                    match ctx.probe_for_name(
+                        method_resolution::Mode::MethodCall,
+                        name.clone(),
+                        self_ty,
+                    ) {
+                        Ok(candidate)
+                        | Err(method_resolution::MethodError::PrivateMatch(candidate)) => {
+                            let id = candidate.item.into();
+                            match candidate.kind {
+                                method_resolution::PickKind::InherentImplPick(_)
+                                | method_resolution::PickKind::ObjectPick(..)
+                                | method_resolution::PickKind::WhereClausePick(..) => {
+                                    // Candidates from where clauses and trait objects are considered inherent.
+                                    _ = callback.on_inherent_item(id);
+                                }
+                                method_resolution::PickKind::TraitPick(..) => {
+                                    _ = callback.on_trait_item(id);
+                                }
+                            }
+                        }
+                        Err(_) => {}
+                    };
+                }
+                None => {
+                    _ = ctx.probe_all(method_resolution::Mode::Path, self_ty).try_for_each(
+                        |candidate| {
+                            let id = candidate.candidate.item.into();
+                            match candidate.candidate.kind {
+                                method_resolution::CandidateKind::InherentImplCandidate {
+                                    ..
+                                }
+                                | method_resolution::CandidateKind::ObjectCandidate(..)
+                                | method_resolution::CandidateKind::WhereClauseCandidate(..) => {
+                                    // Candidates from where clauses and trait objects are considered inherent.
+                                    callback.on_inherent_item(id)
+                                }
+                                method_resolution::CandidateKind::TraitCandidate(..) => {
+                                    callback.on_trait_item(id)
+                                }
+                            }
+                        },
+                    );
+                }
             }
-
-            fn on_trait_method(
-                &mut self,
-                _adjustments: method_resolution::ReceiverAdjustments,
-                item: AssocItemId,
-                _is_visible: bool,
-            ) -> ControlFlow<()> {
-                self.0.on_trait_item(item.into())
-            }
-        }
-
-        let interner = DbInterner::new_with(db, None, None);
-        let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty);
-
-        let krate = scope.krate();
-        let environment = scope
-            .resolver()
-            .generic_def()
-            .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
-
-        _ = method_resolution::iterate_path_candidates(
-            &canonical,
-            db,
-            environment,
-            traits_in_scope,
-            with_local_impls.and_then(|b| b.id.containing_block()).into(),
-            name,
-            &mut Callback(callback),
-        );
+        })
     }
 
     pub fn as_adt(&self) -> Option<Adt> {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index ec43442..769cfd9 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1581,9 +1581,9 @@
                         hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
                             Adjust::Borrow(AutoBorrow::RawPtr(mutability(m)))
                         }
-                        hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(_, m)) => {
+                        hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => {
                             // FIXME: Handle lifetimes here
-                            Adjust::Borrow(AutoBorrow::Ref(mutability(m)))
+                            Adjust::Borrow(AutoBorrow::Ref(mutability(m.into())))
                         }
                         hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc),
                     };
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index f994ed2..ae328a9 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -35,7 +35,7 @@
         unsafe_operations,
     },
     lang_items::lang_items_for_bin_op,
-    method_resolution,
+    method_resolution::{self, CandidateId},
     next_solver::{
         DbInterner, ErrorGuaranteed, GenericArgs, Ty, TyKind, TypingMode, infer::DbInternerInferExt,
     },
@@ -651,8 +651,9 @@
         let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
         let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
 
-        let (_op_trait, op_fn) = lang_items_for_bin_op(op)
-            .and_then(|(name, lang_item)| self.lang_trait_fn(db, lang_item, &name))?;
+        let (_op_trait, op_fn) = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
+            self.lang_trait_fn(db, lang_item, &Name::new_symbol_root(name))
+        })?;
         // HACK: subst for `index()` coincides with that for `Index` because `index()` itself
         // doesn't have any generic parameters, so we skip building another subst for `index()`.
         let interner = DbInterner::new_with(db, None, None);
@@ -861,7 +862,7 @@
                 let expr_id = self.expr_id(path_expr.into())?;
                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) {
                     let (assoc, subst) = match assoc {
-                        AssocItemId::FunctionId(f_in_trait) => {
+                        CandidateId::FunctionId(f_in_trait) => {
                             match infer.type_of_expr_or_pat(expr_id) {
                                 None => {
                                     let subst = GenericSubstitution::new(
@@ -869,7 +870,7 @@
                                         subs,
                                         self.trait_environment(db),
                                     );
-                                    (assoc, subst)
+                                    (AssocItemId::from(f_in_trait), subst)
                                 }
                                 Some(func_ty) => {
                                     if let TyKind::FnDef(_fn_def, subs) = func_ty.kind() {
@@ -889,12 +890,12 @@
                                             subs,
                                             self.trait_environment(db),
                                         );
-                                        (assoc, subst)
+                                        (f_in_trait.into(), subst)
                                     }
                                 }
                             }
                         }
-                        AssocItemId::ConstId(const_id) => {
+                        CandidateId::ConstId(const_id) => {
                             let (konst, subst) =
                                 self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs);
                             let subst = GenericSubstitution::new(
@@ -904,14 +905,6 @@
                             );
                             (konst.into(), subst)
                         }
-                        AssocItemId::TypeAliasId(type_alias) => (
-                            assoc,
-                            GenericSubstitution::new(
-                                type_alias.into(),
-                                subs,
-                                self.trait_environment(db),
-                            ),
-                        ),
                     };
 
                     return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst)));
@@ -927,7 +920,7 @@
                 if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_or_pat_id)
                 {
                     let (assoc, subst) = match assoc {
-                        AssocItemId::ConstId(const_id) => {
+                        CandidateId::ConstId(const_id) => {
                             let (konst, subst) =
                                 self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs);
                             let subst = GenericSubstitution::new(
@@ -935,12 +928,12 @@
                                 subst,
                                 self.trait_environment(db),
                             );
-                            (konst.into(), subst)
+                            (AssocItemId::from(konst), subst)
                         }
-                        assoc => (
-                            assoc,
+                        CandidateId::FunctionId(function_id) => (
+                            function_id.into(),
                             GenericSubstitution::new(
-                                assoc.into(),
+                                function_id.into(),
                                 subs,
                                 self.trait_environment(db),
                             ),
diff --git a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
index 2d6a59a..156286d 100644
--- a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
+++ b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
@@ -129,7 +129,7 @@
     let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 
     let has_wanted_method = ty
-        .iterate_method_candidates(sema.db, &scope, None, Some(&wanted_method), |func| {
+        .iterate_method_candidates(sema.db, &scope, Some(&wanted_method), |func| {
             if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
                 return Some(());
             }
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 3917ca1..c8a244b 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
@@ -165,7 +165,7 @@
     let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
 
     let has_wanted_method = ty
-        .iterate_method_candidates(sema.db, &scope, None, Some(&wanted_method), |func| {
+        .iterate_method_candidates(sema.db, &scope, Some(&wanted_method), |func| {
             if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
                 return Some(());
             }
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 9a9adf2..ef4b977 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -4250,7 +4250,7 @@
         check_assist(
             extract_function,
             r#"
-//- minicore: option
+//- minicore: option, add, builtin_impls
 fn bar() -> Option<i32> { None }
 fn foo() -> Option<()> {
     let n = bar()?;
@@ -4314,7 +4314,7 @@
         check_assist(
             extract_function,
             r#"
-//- minicore: result
+//- minicore: result, add, builtin_impls
 fn foo() -> Result<(), i64> {
     let n = 1;
     $0let k = foo()?;
@@ -4345,7 +4345,7 @@
         check_assist(
             extract_function,
             r#"
-//- minicore: option
+//- minicore: option, add, builtin_impls
 fn foo() -> Option<()> {
     let n = 1;
     $0let k = foo()?;
@@ -4382,7 +4382,7 @@
         check_assist(
             extract_function,
             r#"
-//- minicore: result
+//- minicore: result, add, builtin_impls
 fn foo() -> Result<(), i64> {
     let n = 1;
     $0let k = foo()?;
@@ -4441,7 +4441,7 @@
         check_assist(
             extract_function,
             r#"
-//- minicore: result
+//- minicore: result, add, builtin_impls
 fn foo() -> Result<(), i64> {
     let n = 1;
     $0let k = foo()?;
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index da59626..7c60184 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -2189,7 +2189,7 @@
 //- minicore: index
 struct X;
 
-impl std::ops::Index<usize> for X {
+impl core::ops::Index<usize> for X {
     type Output = i32;
     fn index(&self) -> &Self::Output { 0 }
 }
@@ -2204,7 +2204,7 @@
             r#"
 struct X;
 
-impl std::ops::Index<usize> for X {
+impl core::ops::Index<usize> for X {
     type Output = i32;
     fn index(&self) -> &Self::Output { 0 }
 }
@@ -2214,8 +2214,8 @@
 }
 
 fn foo(s: &S) {
-    let $0sub = &s.sub;
-    sub[0];
+    let $0x = &s.sub;
+    x[0];
 }"#,
             "Extract into variable",
         );
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index d8a2e03..b2e791a 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,4 +1,4 @@
-use hir::{HasCrate, HasVisibility};
+use hir::HasVisibility;
 use ide_db::{FxHashSet, path_transform::PathTransform};
 use syntax::{
     ast::{
@@ -79,8 +79,7 @@
     let mut seen_names = FxHashSet::default();
 
     for ty in sema_field_ty.autoderef(ctx.db()) {
-        let krate = ty.krate(ctx.db());
-        ty.iterate_assoc_items(ctx.db(), krate, |item| {
+        ty.iterate_assoc_items(ctx.db(), |item| {
             if let hir::AssocItem::Function(f) = item {
                 let name = f.name(ctx.db());
                 if f.self_param(ctx.db()).is_some()
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 af9c493..f10b21b 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,7 +95,7 @@
 
     let scope = ctx.sema.scope(impl_.syntax())?;
     let ty = impl_def.self_ty(db);
-    ty.iterate_method_candidates(db, &scope, None, Some(fn_name), Some)
+    ty.iterate_method_candidates(db, &scope, Some(fn_name), Some)
 }
 
 #[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs
index a645c8b..102d7e6 100644
--- a/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -1,4 +1,4 @@
-use hir::{AsAssocItem, AssocItemContainer, FileRange, HasCrate, HasSource};
+use hir::{AsAssocItem, AssocItemContainer, FileRange, HasSource};
 use ide_db::{assists::AssistId, defs::Definition, search::SearchScope};
 use syntax::{
     SyntaxKind,
@@ -70,7 +70,7 @@
     let ty = impl_.self_ty(db);
     // If there exists another associated item with the same name, skip the assist.
     if ty
-        .iterate_assoc_items(db, ty.krate(db), |assoc| {
+        .iterate_assoc_items(db, |assoc| {
             // Type aliases wouldn't conflict due to different namespaces, but we're only checking
             // the items in inherent impls, so we assume `assoc` is never type alias for the sake
             // of brevity (inherent associated types exist in nightly Rust, but it's *very*
diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs
index a3fb851..b686dc0 100644
--- a/crates/ide-assists/src/handlers/replace_arith_op.rs
+++ b/crates/ide-assists/src/handlers/replace_arith_op.rs
@@ -240,12 +240,12 @@
             replace_arith_with_wrapping,
             r#"
 fn main() {
-    let x = 1*x $0+ 2;
+    let x = 1*3 $0+ 2;
 }
 "#,
             r#"
 fn main() {
-    let x = (1*x).wrapping_add(2);
+    let x = (1*3).wrapping_add(2);
 }
 "#,
         )
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
index 14161d9..753f2ab 100644
--- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -45,7 +45,6 @@
         &scope,
         &scope.visible_traits().0,
         None,
-        None,
         |func| {
             let valid = func.name(ctx.sema.db).as_str() == &*method_name_lazy
                 && func.num_params(ctx.sema.db) == n_params
@@ -127,7 +126,6 @@
         &scope,
         &scope.visible_traits().0,
         None,
-        None,
         |func| {
             let valid = func.name(ctx.sema.db).as_str() == method_name_eager
                 && func.num_params(ctx.sema.db) == n_params;
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 297ce33..20776f6 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -231,7 +231,7 @@
 macro_rules! attrs {
     // attributes applicable to all items
     [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
-        attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "docinclude", "must_use", "no_mangle" })
+        attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "docinclude", "must_use", "no_mangle", "unsafe" })
     };
     // attributes applicable to all adts
     [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
@@ -395,6 +395,7 @@
     attr("track_caller", None, None),
     attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
         .prefer_inner(),
+    attr("unsafe(…)", Some("unsafe"), Some("unsafe($0)")),
     attr("used", None, None),
     attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
     attr(
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 511b593..c9f4405 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -270,7 +270,6 @@
         ctx.db,
         &ctx.scope,
         traits_in_scope,
-        Some(ctx.module),
         None,
         Callback { ctx, f, seen_methods: FxHashSet::default() },
     );
@@ -597,7 +596,6 @@
 }
 "#,
             expect![[r#"
-                me local_method()      fn(&self)
                 me pub_module_method() fn(&self)
             "#]],
         );
@@ -1526,6 +1524,8 @@
         check_no_kw(
             r#"
 //- minicore: receiver
+#![feature(arbitrary_self_types)]
+
 use core::ops::Receiver;
 
 struct Foo;
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 5cae7bd..6bdf873 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -126,13 +126,12 @@
                 ctx.db,
                 &ctx.scope,
                 &ctx.traits_in_scope(),
-                Some(ctx.module),
                 None,
                 PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() },
             );
 
             // Iterate assoc types separately
-            ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
+            ty.iterate_assoc_items(ctx.db, |item| {
                 if let hir::AssocItem::TypeAlias(ty) = item {
                     acc.add_type_alias(ctx, ty)
                 }
@@ -196,13 +195,12 @@
                         ctx.db,
                         &ctx.scope,
                         &ctx.traits_in_scope(),
-                        Some(ctx.module),
                         None,
                         PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() },
                     );
 
                     // Iterate assoc types separately
-                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
+                    ty.iterate_assoc_items(ctx.db, |item| {
                         if let hir::AssocItem::TypeAlias(ty) = item {
                             acc.add_type_alias(ctx, ty)
                         }
@@ -232,7 +230,6 @@
                         ctx.db,
                         &ctx.scope,
                         &ctx.traits_in_scope(),
-                        Some(ctx.module),
                         None,
                         PathCallback { ctx, acc, add_assoc_item, seen: FxHashSet::default() },
                     );
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 3465b73..abcf9fc 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -67,7 +67,7 @@
             });
 
             // Iterate assoc types separately
-            ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
+            ty.iterate_assoc_items(ctx.db, |item| {
                 if let hir::AssocItem::TypeAlias(ty) = item {
                     acc.add_type_alias(ctx, ty)
                 }
@@ -110,7 +110,7 @@
                     });
 
                     // Iterate assoc types separately
-                    ty.iterate_assoc_items(ctx.db, ctx.krate, |item| {
+                    ty.iterate_assoc_items(ctx.db, |item| {
                         if let hir::AssocItem::TypeAlias(ty) = item {
                             acc.add_type_alias(ctx, ty)
                         }
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 2f166b7..31a9a74 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -615,21 +615,14 @@
         mut cb: impl FnMut(hir::AssocItem),
     ) {
         let mut seen = FxHashSet::default();
-        ty.iterate_path_candidates(
-            self.db,
-            &self.scope,
-            &self.traits_in_scope(),
-            Some(self.module),
-            None,
-            |item| {
-                // We might iterate candidates of a trait multiple times here, so deduplicate
-                // them.
-                if seen.insert(item) {
-                    cb(item)
-                }
-                None::<()>
-            },
-        );
+        ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
+            // We might iterate candidates of a trait multiple times here, so deduplicate
+            // them.
+            if seen.insert(item) {
+                cb(item)
+            }
+            None::<()>
+        });
     }
 
     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 2a62389..3538dac 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -61,6 +61,7 @@
             at target_feature(enable = "…")
             at test
             at track_caller
+            at unsafe(…)
             at used
             at warn(…)
             md mac
@@ -95,6 +96,7 @@
             at no_mangle
             at non_exhaustive
             at repr(…)
+            at unsafe(…)
             at warn(…)
             md proc_macros
             kw crate::
@@ -173,6 +175,7 @@
             at no_std
             at recursion_limit = "…"
             at type_length_limit = …
+            at unsafe(…)
             at warn(…)
             at windows_subsystem = "…"
             kw crate::
@@ -201,6 +204,7 @@
             at must_use
             at no_mangle
             at path = "…"
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -224,6 +228,7 @@
             at must_use
             at no_implicit_prelude
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -252,6 +257,7 @@
             at macro_use
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -277,6 +283,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -303,6 +310,7 @@
             at macro_use
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -328,6 +336,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -353,6 +362,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -387,6 +397,7 @@
             at no_mangle
             at non_exhaustive
             at repr(…)
+            at unsafe(…)
             at warn(…)
             md core
             kw crate::
@@ -416,6 +427,7 @@
             at no_mangle
             at non_exhaustive
             at repr(…)
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -441,6 +453,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -470,6 +483,7 @@
             at link_section = "…"
             at must_use
             at no_mangle
+            at unsafe(…)
             at used
             at warn(…)
             kw crate::
@@ -497,6 +511,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -524,6 +539,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -545,6 +561,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -572,6 +589,7 @@
             at forbid(…)
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
         "#]],
     );
@@ -592,12 +610,44 @@
             at must_use
             at no_mangle
             at on_unimplemented
+            at unsafe(…)
             at warn(…)
         "#]],
     );
 }
 
 #[test]
+fn attr_on_unsafe_attr() {
+    check(
+        r#"#[unsafe($0)] static FOO: () = ()"#,
+        expect![[r#"
+            at allow(…)
+            at cfg(…)
+            at cfg_attr(…)
+            at deny(…)
+            at deprecated
+            at doc = "…"
+            at doc = include_str!("…")
+            at doc(alias = "…")
+            at doc(hidden)
+            at expect(…)
+            at export_name = "…"
+            at forbid(…)
+            at global_allocator
+            at link_name = "…"
+            at link_section = "…"
+            at must_use
+            at no_mangle
+            at unsafe(…)
+            at used
+            at warn(…)
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
 fn attr_diagnostic_on_unimplemented() {
     check(
         r#"#[diagnostic::on_unimplemented($0)] trait Foo {}"#,
@@ -643,6 +693,7 @@
             at link
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -665,6 +716,7 @@
             at link
             at must_use
             at no_mangle
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -723,6 +775,7 @@
             at target_feature(enable = "…")
             at test
             at track_caller
+            at unsafe(…)
             at warn(…)
             kw crate::
             kw self::
@@ -773,6 +826,7 @@
             at target_feature(enable = "…")
             at test
             at track_caller
+            at unsafe(…)
             at used
             at warn(…)
             kw crate::
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 4033aa5..034bc24 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -3198,6 +3198,7 @@
 fn ambiguous_float_literal() {
     check(
         r#"
+//- /core.rs crate:core
 #![rustc_coherence_is_core]
 
 impl i32 {
@@ -3232,6 +3233,7 @@
 fn ambiguous_float_literal_in_ambiguous_method_call() {
     check(
         r#"
+//- /core.rs crate:core
 #![rustc_coherence_is_core]
 
 impl i32 {
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index 6eb0b81..df0c4e5 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -653,6 +653,7 @@
 
     check(
         r#"
+//- /core.rs crate:core
 #![rustc_coherence_is_core]
 #[lang = "u32"]
 impl u32 {
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 50edfca..c49ade2 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -5,7 +5,7 @@
 use hir::{
     AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate,
     ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
-    SemanticsScope, Trait, TyFingerprint, Type, db::HirDatabase,
+    SemanticsScope, Trait, Type,
 };
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -500,44 +500,37 @@
         ModuleDef::Adt(adt) => adt.ty(db),
         _ => return SmallVec::new(),
     };
-    ty.iterate_path_candidates::<Infallible>(
-        db,
-        scope,
-        &FxHashSet::default(),
-        None,
-        None,
-        |assoc| {
-            // FIXME: Support extra trait imports
-            if assoc.container_or_implemented_trait(db).is_some() {
-                return None;
+    ty.iterate_path_candidates::<Infallible>(db, scope, &FxHashSet::default(), None, |assoc| {
+        // FIXME: Support extra trait imports
+        if assoc.container_or_implemented_trait(db).is_some() {
+            return None;
+        }
+        let name = assoc.name(db)?;
+        let is_match = match candidate {
+            NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
+            NameToImport::Prefix(text, false) => {
+                name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
+                    name_char.eq_ignore_ascii_case(&candidate_char)
+                })
             }
-            let name = assoc.name(db)?;
-            let is_match = match candidate {
-                NameToImport::Prefix(text, true) => name.as_str().starts_with(text),
-                NameToImport::Prefix(text, false) => {
-                    name.as_str().chars().zip(text.chars()).all(|(name_char, candidate_char)| {
-                        name_char.eq_ignore_ascii_case(&candidate_char)
-                    })
-                }
-                NameToImport::Exact(text, true) => name.as_str() == text,
-                NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
-                NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
-                NameToImport::Fuzzy(text, false) => text.chars().all(|c| {
-                    name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))
-                }),
-            };
-            if !is_match {
-                return None;
-            }
-            result.push(LocatedImport::new(
-                import_path_candidate.clone(),
-                resolved_qualifier,
-                assoc_to_item(assoc),
-                complete_in_flyimport,
-            ));
-            None
-        },
-    );
+            NameToImport::Exact(text, true) => name.as_str() == text,
+            NameToImport::Exact(text, false) => name.as_str().eq_ignore_ascii_case(text),
+            NameToImport::Fuzzy(text, true) => text.chars().all(|c| name.as_str().contains(c)),
+            NameToImport::Fuzzy(text, false) => text
+                .chars()
+                .all(|c| name.as_str().chars().any(|name_char| name_char.eq_ignore_ascii_case(&c))),
+        };
+        if !is_match {
+            return None;
+        }
+        result.push(LocatedImport::new(
+            import_path_candidate.clone(),
+            resolved_qualifier,
+            assoc_to_item(assoc),
+            complete_in_flyimport,
+        ));
+        None
+    });
     result
 }
 
@@ -608,7 +601,6 @@
         deref_chain
             .into_iter()
             .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?)))
-            .sorted()
             .unique()
             .collect::<Vec<_>>()
     };
@@ -619,11 +611,11 @@
     }
 
     // in order to handle implied bounds through an associated type, keep all traits if any
-    // type in the deref chain matches `TyFingerprint::Unnameable`. This fingerprint
+    // type in the deref chain matches `SimplifiedType::Placeholder`. This fingerprint
     // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual implementations.
     if !autoderef_method_receiver
         .iter()
-        .any(|(_, fingerprint)| matches!(fingerprint, TyFingerprint::Unnameable))
+        .any(|(_, fingerprint)| matches!(fingerprint, hir::SimplifiedType::Placeholder))
     {
         trait_candidates.retain(|&candidate_trait_id| {
             // we care about the following cases:
@@ -635,17 +627,18 @@
             //    a. This is recursive for fundamental types
             let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
 
-            let trait_impls_in_crate = db.trait_impls_in_crate(defining_crate_for_trait.into());
+            let trait_impls_in_crate =
+                hir::TraitImpls::for_crate(db, defining_crate_for_trait.into());
             let definitions_exist_in_trait_crate =
-                autoderef_method_receiver.iter().any(|&(_, fingerprint)| {
+                autoderef_method_receiver.iter().any(|(_, fingerprint)| {
                     trait_impls_in_crate
                         .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
                 });
             // this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
             // we can avoid a second db lookup.
             let definitions_exist_in_receiver_crate = || {
-                autoderef_method_receiver.iter().any(|&(krate, fingerprint)| {
-                    db.trait_impls_in_crate(krate)
+                autoderef_method_receiver.iter().any(|(krate, fingerprint)| {
+                    hir::TraitImpls::for_crate(db, *krate)
                         .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
                 })
             };
@@ -663,7 +656,6 @@
             scope,
             &trait_candidates,
             None,
-            None,
             |assoc| {
                 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
                     let located_trait = assoc.container_trait(db).filter(|&it| scope_filter(it))?;
@@ -688,7 +680,6 @@
             scope,
             &trait_candidates,
             None,
-            None,
             |function| {
                 let assoc = function.as_assoc_item(db)?;
                 if let Some(&complete_in_flyimport) = required_assoc_items.get(&assoc) {
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index 1463fdb..4f8dc4a 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -83,7 +83,12 @@
                         crate_name,
                     })?;
 
-                    let cancelled = Cancelled::catch(|| _ = hir::crate_def_map(&db, crate_id));
+                    let cancelled = Cancelled::catch(|| {
+                        _ = hir::crate_def_map(&db, crate_id);
+                        // we compute the lang items here as the work for them is also highly recursive and will be trigger by the module symbols query
+                        // slowing down leaf crate analysis tremendously as we go back to being blocked on a single thread
+                        _ = hir::crate_lang_items(&db, crate_id);
+                    });
 
                     match cancelled {
                         Ok(()) => progress_sender
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 519ff19..fdc426c 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -439,10 +439,27 @@
     #[test]
     fn ignores_no_mangle_items() {
         cov_mark::check!(extern_func_no_mangle_ignored);
+        cov_mark::check!(no_mangle_static_incorrect_case_ignored);
         check_diagnostics(
             r#"
 #[no_mangle]
 extern "C" fn NonSnakeCaseName(some_var: u8) -> u8;
+#[no_mangle]
+static lower_case: () = ();
+            "#,
+        );
+    }
+
+    #[test]
+    fn ignores_unsafe_no_mangle_items() {
+        cov_mark::check!(extern_func_no_mangle_ignored);
+        cov_mark::check!(no_mangle_static_incorrect_case_ignored);
+        check_diagnostics(
+            r#"
+#[unsafe(no_mangle)]
+extern "C" fn NonSnakeCaseName(some_var: u8) -> u8;
+#[unsafe(no_mangle)]
+static lower_case: () = ();
             "#,
         );
     }
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 49f925e..ab7256d 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -226,7 +226,7 @@
 
     // Look for a ::new() associated function
     let has_new_func = ty
-        .iterate_assoc_items(ctx.sema.db, krate, |assoc_item| {
+        .iterate_assoc_items(ctx.sema.db, |assoc_item| {
             if let AssocItem::Function(func) = assoc_item
                 && func.name(ctx.sema.db) == sym::new
                 && func.assoc_fn_params(ctx.sema.db).is_empty()
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 1fc96b7..d52fc73 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -390,7 +390,6 @@
 
     #[test]
     fn expr_diverges() {
-        cov_mark::check_count!(validate_match_bailed_out, 2);
         check_diagnostics(
             r#"
 enum Either { A, B }
@@ -401,6 +400,7 @@
         Either::B => (),
     }
     match loop {} {
+       // ^^^^^^^ error: missing match arm: `B` not covered
         Either::A => (),
     }
     match loop { break Either::A } {
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index eefa1ac..18280a4 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -806,7 +806,7 @@
     _ = (x, y);
     let x = Foo;
     let y = &mut *x;
-               //^^ 💡 error: cannot mutate immutable variable `x`
+               // ^ 💡 error: cannot mutate immutable variable `x`
     _ = (x, y);
     let x = Foo;
       //^ 💡 warn: unused variable
@@ -815,13 +815,13 @@
                           //^^^^^^ 💡 error: cannot mutate immutable variable `x`
     _ = (x, y);
     let ref mut y = *x;
-                  //^^ 💡 error: cannot mutate immutable variable `x`
+                  // ^ 💡 error: cannot mutate immutable variable `x`
     _ = y;
     let (ref mut y, _) = *x;
-                       //^^ 💡 error: cannot mutate immutable variable `x`
+                       // ^ 💡 error: cannot mutate immutable variable `x`
     _ = y;
     match *x {
-        //^^ 💡 error: cannot mutate immutable variable `x`
+        // ^ 💡 error: cannot mutate immutable variable `x`
         (ref y, 5) => _ = y,
         (_, ref mut y) => _ = y,
     }
@@ -1130,7 +1130,7 @@
   //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
     let x = Box::new(5);
     let closure = || *x = 2;
-                    //^ 💡 error: cannot mutate immutable variable `x`
+                   //^^^^^^ 💡 error: cannot mutate immutable variable `x`
     _ = closure;
 }
 "#,
diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs
index 69cd0d2..23f0460 100644
--- a/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -1,6 +1,9 @@
 use hir::{EditionedFileId, FileRange, HasCrate, HasSource, Semantics};
 use ide_db::{RootDatabase, assists::Assist, source_change::SourceChange, text_edit::TextEdit};
-use syntax::{AstNode, TextRange, TextSize, ast::HasVisibility};
+use syntax::{
+    AstNode, TextRange,
+    ast::{HasName, HasVisibility},
+};
 
 use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
 
@@ -8,7 +11,6 @@
 //
 // This diagnostic is triggered if the accessed field is not visible from the current module.
 pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic {
-    // FIXME: add quickfix
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0616"),
@@ -50,11 +52,19 @@
             source.with_value(visibility.syntax()).original_file_range_opt(sema.db)?.0
         }
         None => {
-            let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
-            FileRange {
-                file_id: range.file_id,
-                range: TextRange::at(range.range.start(), TextSize::new(0)),
-            }
+            let (range, _) = source
+                .map(|it| {
+                    Some(match it {
+                        hir::FieldSource::Named(it) => {
+                            it.unsafe_token().or(it.name()?.ident_token())?.text_range()
+                        }
+                        hir::FieldSource::Pos(it) => it.ty()?.syntax().text_range(),
+                    })
+                })
+                .transpose()?
+                .original_node_file_range_opt(sema.db)?;
+
+            FileRange { file_id: range.file_id, range: TextRange::empty(range.range.start()) }
         }
     };
     let source_change = SourceChange::from_text_edit(
@@ -229,4 +239,186 @@
             "#,
         );
     }
+
+    #[test]
+    fn change_visibility_of_field_with_doc_comment() {
+        check_fix(
+            r#"
+pub mod foo {
+    pub struct Foo {
+        /// This is a doc comment
+        bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar$0;
+}
+            "#,
+            r#"
+pub mod foo {
+    pub struct Foo {
+        /// This is a doc comment
+        pub(crate) bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar;
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn change_visibility_of_field_with_line_comment() {
+        check_fix(
+            r#"
+pub mod foo {
+    pub struct Foo {
+        // This is a line comment
+        bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar$0;
+}
+            "#,
+            r#"
+pub mod foo {
+    pub struct Foo {
+        // This is a line comment
+        pub(crate) bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar;
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn change_visibility_of_field_with_multiple_doc_comments() {
+        check_fix(
+            r#"
+pub mod foo {
+    pub struct Foo {
+        /// First line
+        /// Second line
+        bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar$0;
+}
+            "#,
+            r#"
+pub mod foo {
+    pub struct Foo {
+        /// First line
+        /// Second line
+        pub(crate) bar: u32,
+    }
+}
+
+fn main() {
+    let x = foo::Foo { bar: 0 };
+    x.bar;
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn change_visibility_of_field_with_attr_and_comment() {
+        check_fix(
+            r#"
+mod foo {
+    pub struct Foo {
+        #[rustfmt::skip]
+        /// First line
+        /// Second line
+        bar: u32,
+    }
+}
+fn main() {
+    foo::Foo { $0bar: 42 };
+}
+            "#,
+            r#"
+mod foo {
+    pub struct Foo {
+        #[rustfmt::skip]
+        /// First line
+        /// Second line
+        pub(crate) bar: u32,
+    }
+}
+fn main() {
+    foo::Foo { bar: 42 };
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn change_visibility_of_field_with_macro() {
+        check_fix(
+            r#"
+macro_rules! allow_unused {
+    ($vis:vis $struct:ident $name:ident { $($fvis:vis $field:ident : $ty:ty,)* }) => {
+        $vis $struct $name {
+            $(
+                #[allow(unused)]
+                $fvis $field : $ty,
+            )*
+        }
+    };
+}
+mod foo {
+    allow_unused!(
+        pub struct Foo {
+            x: i32,
+        }
+    );
+}
+fn main() {
+    let foo = foo::Foo { x: 2 };
+    let _ = foo.$0x
+}
+            "#,
+            r#"
+macro_rules! allow_unused {
+    ($vis:vis $struct:ident $name:ident { $($fvis:vis $field:ident : $ty:ty,)* }) => {
+        $vis $struct $name {
+            $(
+                #[allow(unused)]
+                $fvis $field : $ty,
+            )*
+        }
+    };
+}
+mod foo {
+    allow_unused!(
+        pub struct Foo {
+            pub(crate) x: i32,
+        }
+    );
+}
+fn main() {
+    let foo = foo::Foo { x: 2 };
+    let _ = foo.x
+}
+            "#,
+        );
+    }
 }
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs
index a48c0f8..461de40 100644
--- a/crates/ide-ssr/src/resolving.rs
+++ b/crates/ide-ssr/src/resolving.rs
@@ -228,12 +228,10 @@
         let resolved_qualifier = self.scope.speculative_resolve(&path.qualifier()?)?;
         if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier {
             let name = path.segment()?.name_ref()?;
-            let module = self.scope.module();
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
                 &self.scope,
                 &self.scope.visible_traits().0,
-                Some(module),
                 None,
                 |assoc_item| {
                     let item_name = assoc_item.name(self.scope.db)?;
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 3a19531..071eacf 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -350,7 +350,7 @@
 fn hover_closure() {
     check(
         r#"
-//- minicore: copy
+//- minicore: copy, add, builtin_impls
 fn main() {
     let x = 2;
     let y = $0|z| x + z;
@@ -3280,7 +3280,7 @@
 
     check_hover_no_memory_layout(
         r#"
-//- minicore: copy
+//- minicore: copy, add, builtin_impls
 fn main() {
     let x = 2;
     let y = $0|z| x + z;
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index ebb0d57..b2584b6 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -352,7 +352,7 @@
         check_with_config(
             InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
             r#"
-//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
 fn main() {
     let _: u32         = loop {};
                        //^^^^^^^<never-to-any>
@@ -466,9 +466,8 @@
                 ..DISABLED_CONFIG
             },
             r#"
-//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn
+//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn, builtin_impls
 fn main() {
-
     Struct.consume();
     Struct.by_ref();
   //^^^^^^.&
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 5a9d451..0624467 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -520,4 +520,7 @@
     never_type_fallback,
     specialization,
     min_specialization,
+    arbitrary_self_types,
+    arbitrary_self_types_pointers,
+    supertrait_item_shadowing,
 }
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 93e02a9..6a38044 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -1006,7 +1006,149 @@
     }
 }
 #[macro_export]
-macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [naked_asm] => { $ crate :: SyntaxKind :: NAKED_ASM_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
+macro_rules ! T_ {
+    [$] => { $ crate :: SyntaxKind :: DOLLAR };
+    [;] => { $ crate :: SyntaxKind :: SEMICOLON };
+    [,] => { $ crate :: SyntaxKind :: COMMA };
+    ['('] => { $ crate :: SyntaxKind :: L_PAREN };
+    [')'] => { $ crate :: SyntaxKind :: R_PAREN };
+    ['{'] => { $ crate :: SyntaxKind :: L_CURLY };
+    ['}'] => { $ crate :: SyntaxKind :: R_CURLY };
+    ['['] => { $ crate :: SyntaxKind :: L_BRACK };
+    [']'] => { $ crate :: SyntaxKind :: R_BRACK };
+    [<] => { $ crate :: SyntaxKind :: L_ANGLE };
+    [>] => { $ crate :: SyntaxKind :: R_ANGLE };
+    [@] => { $ crate :: SyntaxKind :: AT };
+    [#] => { $ crate :: SyntaxKind :: POUND };
+    [~] => { $ crate :: SyntaxKind :: TILDE };
+    [?] => { $ crate :: SyntaxKind :: QUESTION };
+    [&] => { $ crate :: SyntaxKind :: AMP };
+    [|] => { $ crate :: SyntaxKind :: PIPE };
+    [+] => { $ crate :: SyntaxKind :: PLUS };
+    [*] => { $ crate :: SyntaxKind :: STAR };
+    [/] => { $ crate :: SyntaxKind :: SLASH };
+    [^] => { $ crate :: SyntaxKind :: CARET };
+    [%] => { $ crate :: SyntaxKind :: PERCENT };
+    [_] => { $ crate :: SyntaxKind :: UNDERSCORE };
+    [.] => { $ crate :: SyntaxKind :: DOT };
+    [..] => { $ crate :: SyntaxKind :: DOT2 };
+    [...] => { $ crate :: SyntaxKind :: DOT3 };
+    [..=] => { $ crate :: SyntaxKind :: DOT2EQ };
+    [:] => { $ crate :: SyntaxKind :: COLON };
+    [::] => { $ crate :: SyntaxKind :: COLON2 };
+    [=] => { $ crate :: SyntaxKind :: EQ };
+    [==] => { $ crate :: SyntaxKind :: EQ2 };
+    [=>] => { $ crate :: SyntaxKind :: FAT_ARROW };
+    [!] => { $ crate :: SyntaxKind :: BANG };
+    [!=] => { $ crate :: SyntaxKind :: NEQ };
+    [-] => { $ crate :: SyntaxKind :: MINUS };
+    [->] => { $ crate :: SyntaxKind :: THIN_ARROW };
+    [<=] => { $ crate :: SyntaxKind :: LTEQ };
+    [>=] => { $ crate :: SyntaxKind :: GTEQ };
+    [+=] => { $ crate :: SyntaxKind :: PLUSEQ };
+    [-=] => { $ crate :: SyntaxKind :: MINUSEQ };
+    [|=] => { $ crate :: SyntaxKind :: PIPEEQ };
+    [&=] => { $ crate :: SyntaxKind :: AMPEQ };
+    [^=] => { $ crate :: SyntaxKind :: CARETEQ };
+    [/=] => { $ crate :: SyntaxKind :: SLASHEQ };
+    [*=] => { $ crate :: SyntaxKind :: STAREQ };
+    [%=] => { $ crate :: SyntaxKind :: PERCENTEQ };
+    [&&] => { $ crate :: SyntaxKind :: AMP2 };
+    [||] => { $ crate :: SyntaxKind :: PIPE2 };
+    [<<] => { $ crate :: SyntaxKind :: SHL };
+    [>>] => { $ crate :: SyntaxKind :: SHR };
+    [<<=] => { $ crate :: SyntaxKind :: SHLEQ };
+    [>>=] => { $ crate :: SyntaxKind :: SHREQ };
+    [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW };
+    [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW };
+    [as] => { $ crate :: SyntaxKind :: AS_KW };
+    [become] => { $ crate :: SyntaxKind :: BECOME_KW };
+    [box] => { $ crate :: SyntaxKind :: BOX_KW };
+    [break] => { $ crate :: SyntaxKind :: BREAK_KW };
+    [const] => { $ crate :: SyntaxKind :: CONST_KW };
+    [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW };
+    [crate] => { $ crate :: SyntaxKind :: CRATE_KW };
+    [do] => { $ crate :: SyntaxKind :: DO_KW };
+    [else] => { $ crate :: SyntaxKind :: ELSE_KW };
+    [enum] => { $ crate :: SyntaxKind :: ENUM_KW };
+    [extern] => { $ crate :: SyntaxKind :: EXTERN_KW };
+    [false] => { $ crate :: SyntaxKind :: FALSE_KW };
+    [final] => { $ crate :: SyntaxKind :: FINAL_KW };
+    [fn] => { $ crate :: SyntaxKind :: FN_KW };
+    [for] => { $ crate :: SyntaxKind :: FOR_KW };
+    [if] => { $ crate :: SyntaxKind :: IF_KW };
+    [impl] => { $ crate :: SyntaxKind :: IMPL_KW };
+    [in] => { $ crate :: SyntaxKind :: IN_KW };
+    [let] => { $ crate :: SyntaxKind :: LET_KW };
+    [loop] => { $ crate :: SyntaxKind :: LOOP_KW };
+    [macro] => { $ crate :: SyntaxKind :: MACRO_KW };
+    [match] => { $ crate :: SyntaxKind :: MATCH_KW };
+    [mod] => { $ crate :: SyntaxKind :: MOD_KW };
+    [move] => { $ crate :: SyntaxKind :: MOVE_KW };
+    [mut] => { $ crate :: SyntaxKind :: MUT_KW };
+    [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW };
+    [priv] => { $ crate :: SyntaxKind :: PRIV_KW };
+    [pub] => { $ crate :: SyntaxKind :: PUB_KW };
+    [ref] => { $ crate :: SyntaxKind :: REF_KW };
+    [return] => { $ crate :: SyntaxKind :: RETURN_KW };
+    [self] => { $ crate :: SyntaxKind :: SELF_KW };
+    [static] => { $ crate :: SyntaxKind :: STATIC_KW };
+    [struct] => { $ crate :: SyntaxKind :: STRUCT_KW };
+    [super] => { $ crate :: SyntaxKind :: SUPER_KW };
+    [trait] => { $ crate :: SyntaxKind :: TRAIT_KW };
+    [true] => { $ crate :: SyntaxKind :: TRUE_KW };
+    [type] => { $ crate :: SyntaxKind :: TYPE_KW };
+    [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW };
+    [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW };
+    [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW };
+    [use] => { $ crate :: SyntaxKind :: USE_KW };
+    [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW };
+    [where] => { $ crate :: SyntaxKind :: WHERE_KW };
+    [while] => { $ crate :: SyntaxKind :: WHILE_KW };
+    [yield] => { $ crate :: SyntaxKind :: YIELD_KW };
+    [asm] => { $ crate :: SyntaxKind :: ASM_KW };
+    [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW };
+    [auto] => { $ crate :: SyntaxKind :: AUTO_KW };
+    [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW };
+    [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
+    [default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
+    [dyn] => { $ crate :: SyntaxKind :: DYN_KW };
+    [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW };
+    [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW };
+    [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW };
+    [inout] => { $ crate :: SyntaxKind :: INOUT_KW };
+    [label] => { $ crate :: SyntaxKind :: LABEL_KW };
+    [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW };
+    [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW };
+    [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW };
+    [naked_asm] => { $ crate :: SyntaxKind :: NAKED_ASM_KW };
+    [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW };
+    [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW };
+    [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW };
+    [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW };
+    [options] => { $ crate :: SyntaxKind :: OPTIONS_KW };
+    [out] => { $ crate :: SyntaxKind :: OUT_KW };
+    [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW };
+    [pure] => { $ crate :: SyntaxKind :: PURE_KW };
+    [raw] => { $ crate :: SyntaxKind :: RAW_KW };
+    [readonly] => { $ crate :: SyntaxKind :: READONLY_KW };
+    [safe] => { $ crate :: SyntaxKind :: SAFE_KW };
+    [sym] => { $ crate :: SyntaxKind :: SYM_KW };
+    [union] => { $ crate :: SyntaxKind :: UNION_KW };
+    [yeet] => { $ crate :: SyntaxKind :: YEET_KW };
+    [async] => { $ crate :: SyntaxKind :: ASYNC_KW };
+    [await] => { $ crate :: SyntaxKind :: AWAIT_KW };
+    [dyn] => { $ crate :: SyntaxKind :: DYN_KW };
+    [gen] => { $ crate :: SyntaxKind :: GEN_KW };
+    [try] => { $ crate :: SyntaxKind :: TRY_KW };
+    [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT };
+    [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER };
+    [ident] => { $ crate :: SyntaxKind :: IDENT };
+    [string] => { $ crate :: SyntaxKind :: STRING };
+    [shebang] => { $ crate :: SyntaxKind :: SHEBANG };
+    [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER };
+}
+
 impl ::core::marker::Copy for SyntaxKind {}
 impl ::core::clone::Clone for SyntaxKind {
     #[inline]
diff --git a/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
index bc04482..78630dd 100644
--- a/crates/proc-macro-srv/proc-macro-test/Cargo.toml
+++ b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
@@ -10,4 +10,4 @@
 doctest = false
 
 [build-dependencies]
-cargo_metadata = "0.20.0"
+cargo_metadata = "0.23.0"
diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml
index 0dbb309..ec44369 100644
--- a/crates/project-model/Cargo.toml
+++ b/crates/project-model/Cargo.toml
@@ -21,6 +21,7 @@
 serde.workspace = true
 serde_derive.workspace = true
 temp-dir.workspace = true
+toml.workspace = true
 tracing.workspace = true
 triomphe.workspace = true
 la-arena.workspace = true
diff --git a/crates/project-model/src/cargo_config_file.rs b/crates/project-model/src/cargo_config_file.rs
index a1e7ed0..5d6e5fd 100644
--- a/crates/project-model/src/cargo_config_file.rs
+++ b/crates/project-model/src/cargo_config_file.rs
@@ -1,37 +1,135 @@
-//! Read `.cargo/config.toml` as a JSON object
-use paths::{Utf8Path, Utf8PathBuf};
+//! Read `.cargo/config.toml` as a TOML table
+use paths::{AbsPath, Utf8Path, Utf8PathBuf};
 use rustc_hash::FxHashMap;
+use toml::{
+    Spanned,
+    de::{DeTable, DeValue},
+};
 use toolchain::Tool;
 
 use crate::{ManifestPath, Sysroot, utf8_stdout};
 
-pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
+#[derive(Clone)]
+pub struct CargoConfigFile(String);
 
-pub(crate) fn read(
-    manifest: &ManifestPath,
-    extra_env: &FxHashMap<String, Option<String>>,
-    sysroot: &Sysroot,
-) -> Option<CargoConfigFile> {
-    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
-    cargo_config
-        .args(["-Z", "unstable-options", "config", "get", "--format", "json"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    if manifest.is_rust_manifest() {
-        cargo_config.arg("-Zscript");
+impl CargoConfigFile {
+    pub(crate) fn load(
+        manifest: &ManifestPath,
+        extra_env: &FxHashMap<String, Option<String>>,
+        sysroot: &Sysroot,
+    ) -> Option<Self> {
+        let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+        cargo_config
+            .args(["-Z", "unstable-options", "config", "get", "--format", "toml", "--show-origin"])
+            .env("RUSTC_BOOTSTRAP", "1");
+        if manifest.is_rust_manifest() {
+            cargo_config.arg("-Zscript");
+        }
+
+        tracing::debug!("Discovering cargo config by {cargo_config:?}");
+        utf8_stdout(&mut cargo_config)
+            .inspect(|toml| {
+                tracing::debug!("Discovered cargo config: {toml:?}");
+            })
+            .inspect_err(|err| {
+                tracing::debug!("Failed to discover cargo config: {err:?}");
+            })
+            .ok()
+            .map(CargoConfigFile)
     }
 
-    tracing::debug!("Discovering cargo config by {:?}", cargo_config);
-    let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
-        .inspect(|json| {
-            tracing::debug!("Discovered cargo config: {:?}", json);
-        })
-        .inspect_err(|err| {
-            tracing::debug!("Failed to discover cargo config: {:?}", err);
-        })
-        .ok()
-        .and_then(|stdout| serde_json::from_str(&stdout).ok())?;
+    pub(crate) fn read<'a>(&'a self) -> Option<CargoConfigFileReader<'a>> {
+        CargoConfigFileReader::new(&self.0)
+    }
 
-    Some(json)
+    #[cfg(test)]
+    pub(crate) fn from_string_for_test(s: String) -> Self {
+        CargoConfigFile(s)
+    }
+}
+
+pub(crate) struct CargoConfigFileReader<'a> {
+    toml_str: &'a str,
+    line_ends: Vec<usize>,
+    table: Spanned<DeTable<'a>>,
+}
+
+impl<'a> CargoConfigFileReader<'a> {
+    fn new(toml_str: &'a str) -> Option<Self> {
+        let toml = DeTable::parse(toml_str)
+            .inspect_err(|err| tracing::debug!("Failed to parse cargo config into toml: {err:?}"))
+            .ok()?;
+        let mut last_line_end = 0;
+        let line_ends = toml_str
+            .lines()
+            .map(|l| {
+                last_line_end += l.len() + 1;
+                last_line_end
+            })
+            .collect();
+
+        Some(CargoConfigFileReader { toml_str, table: toml, line_ends })
+    }
+
+    pub(crate) fn get_spanned(
+        &self,
+        accessor: impl IntoIterator<Item = &'a str>,
+    ) -> Option<&Spanned<DeValue<'a>>> {
+        let mut keys = accessor.into_iter();
+        let mut val = self.table.get_ref().get(keys.next()?)?;
+        for key in keys {
+            let DeValue::Table(map) = val.get_ref() else { return None };
+            val = map.get(key)?;
+        }
+        Some(val)
+    }
+
+    pub(crate) fn get(&self, accessor: impl IntoIterator<Item = &'a str>) -> Option<&DeValue<'a>> {
+        self.get_spanned(accessor).map(|it| it.as_ref())
+    }
+
+    pub(crate) fn get_origin_root(&self, spanned: &Spanned<DeValue<'a>>) -> Option<&AbsPath> {
+        let span = spanned.span();
+
+        for &line_end in &self.line_ends {
+            if line_end < span.end {
+                continue;
+            }
+
+            let after_span = &self.toml_str[span.end..line_end];
+
+            // table.key = "value" # /parent/.cargo/config.toml
+            //                   |                            |
+            //                   span.end                     line_end
+            let origin_path = after_span
+                .strip_prefix([',']) // strip trailing comma
+                .unwrap_or(after_span)
+                .trim_start()
+                .strip_prefix(['#'])
+                .and_then(|path| {
+                    let path = path.trim();
+                    if path.starts_with("environment variable")
+                        || path.starts_with("--config cli option")
+                    {
+                        None
+                    } else {
+                        Some(path)
+                    }
+                });
+
+            return origin_path.and_then(|path| {
+                <&Utf8Path>::from(path)
+                    .try_into()
+                    .ok()
+                    // Two levels up to the config file.
+                    // See https://doc.rust-lang.org/cargo/reference/config.html#config-relative-paths
+                    .and_then(AbsPath::parent)
+                    .and_then(AbsPath::parent)
+            });
+        }
+
+        None
+    }
 }
 
 pub(crate) fn make_lockfile_copy(
@@ -54,3 +152,74 @@
         }
     }
 }
+
+#[test]
+fn cargo_config_file_reader_works() {
+    #[cfg(target_os = "windows")]
+    let root = "C://ROOT";
+
+    #[cfg(not(target_os = "windows"))]
+    let root = "/ROOT";
+
+    let toml = format!(
+        r##"
+alias.foo = "abc"
+alias.bar = "🙂" # {root}/home/.cargo/config.toml
+alias.sub-example = [
+    "sub", # {root}/foo/.cargo/config.toml
+    "example", # {root}/❤️💛💙/💝/.cargo/config.toml
+]
+build.rustflags = [
+    "--flag", # {root}/home/.cargo/config.toml
+    "env", # environment variable `CARGO_BUILD_RUSTFLAGS`
+    "cli", # --config cli option
+]
+env.CARGO_WORKSPACE_DIR.relative = true # {root}/home/.cargo/config.toml
+env.CARGO_WORKSPACE_DIR.value = "" # {root}/home/.cargo/config.toml
+"##
+    );
+
+    let reader = CargoConfigFileReader::new(&toml).unwrap();
+
+    let alias_foo = reader.get_spanned(["alias", "foo"]).unwrap();
+    assert_eq!(alias_foo.as_ref().as_str().unwrap(), "abc");
+    assert!(reader.get_origin_root(alias_foo).is_none());
+
+    let alias_bar = reader.get_spanned(["alias", "bar"]).unwrap();
+    assert_eq!(alias_bar.as_ref().as_str().unwrap(), "🙂");
+    assert_eq!(reader.get_origin_root(alias_bar).unwrap().as_str(), format!("{root}/home"));
+
+    let alias_sub_example = reader.get_spanned(["alias", "sub-example"]).unwrap();
+    assert!(reader.get_origin_root(alias_sub_example).is_none());
+    let alias_sub_example = alias_sub_example.as_ref().as_array().unwrap();
+
+    assert_eq!(alias_sub_example[0].get_ref().as_str().unwrap(), "sub");
+    assert_eq!(
+        reader.get_origin_root(&alias_sub_example[0]).unwrap().as_str(),
+        format!("{root}/foo")
+    );
+
+    assert_eq!(alias_sub_example[1].get_ref().as_str().unwrap(), "example");
+    assert_eq!(
+        reader.get_origin_root(&alias_sub_example[1]).unwrap().as_str(),
+        format!("{root}/❤️💛💙/💝")
+    );
+
+    let build_rustflags = reader.get(["build", "rustflags"]).unwrap().as_array().unwrap();
+    assert_eq!(
+        reader.get_origin_root(&build_rustflags[0]).unwrap().as_str(),
+        format!("{root}/home")
+    );
+    assert!(reader.get_origin_root(&build_rustflags[1]).is_none());
+    assert!(reader.get_origin_root(&build_rustflags[2]).is_none());
+
+    let env_cargo_workspace_dir =
+        reader.get(["env", "CARGO_WORKSPACE_DIR"]).unwrap().as_table().unwrap();
+    let env_relative = &env_cargo_workspace_dir["relative"];
+    assert!(env_relative.as_ref().as_bool().unwrap());
+    assert_eq!(reader.get_origin_root(env_relative).unwrap().as_str(), format!("{root}/home"));
+
+    let env_val = &env_cargo_workspace_dir["value"];
+    assert_eq!(env_val.as_ref().as_str().unwrap(), "");
+    assert_eq!(reader.get_origin_root(env_val).unwrap().as_str(), format!("{root}/home"));
+}
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs
index ae0458a..51c4479 100644
--- a/crates/project-model/src/env.rs
+++ b/crates/project-model/src/env.rs
@@ -2,9 +2,8 @@
 use base_db::Env;
 use paths::Utf8Path;
 use rustc_hash::FxHashMap;
-use toolchain::Tool;
 
-use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
+use crate::{PackageData, TargetKind, cargo_config_file::CargoConfigFile};
 
 /// Recreates the compile-time environment variables that Cargo sets.
 ///
@@ -48,8 +47,8 @@
     );
 }
 
-pub(crate) fn inject_cargo_env(env: &mut Env) {
-    env.set("CARGO", Tool::Cargo.path().to_string());
+pub(crate) fn inject_cargo_env(env: &mut Env, cargo_path: &Utf8Path) {
+    env.set("CARGO", cargo_path.as_str());
 }
 
 pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
@@ -62,46 +61,48 @@
 }
 
 pub(crate) fn cargo_config_env(
-    manifest: &ManifestPath,
     config: &Option<CargoConfigFile>,
     extra_env: &FxHashMap<String, Option<String>>,
 ) -> Env {
+    use toml::de::*;
+
     let mut env = Env::default();
     env.extend(extra_env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))));
 
-    let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
-    else {
+    let Some(config_reader) = config.as_ref().and_then(|c| c.read()) else {
+        return env;
+    };
+    let Some(env_toml) = config_reader.get(["env"]).and_then(|it| it.as_table()) else {
         return env;
     };
 
-    // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
-    // But cargo does not provide this information.
-    let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
-
-    for (key, entry) in env_json {
-        let value = match entry {
-            serde_json::Value::String(s) => s.clone(),
-            serde_json::Value::Object(entry) => {
+    for (key, entry) in env_toml {
+        let key = key.as_ref().as_ref();
+        let value = match entry.as_ref() {
+            DeValue::String(s) => String::from(s.clone()),
+            DeValue::Table(entry) => {
                 // Each entry MUST have a `value` key.
-                let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
+                let Some(map) = entry.get("value").and_then(|v| v.as_ref().as_str()) else {
                     continue;
                 };
                 // If the entry already exists in the environment AND the `force` key is not set to
                 // true, then don't overwrite the value.
                 if extra_env.get(key).is_some_and(Option::is_some)
-                    && !entry.get("force").and_then(|v| v.as_bool()).unwrap_or(false)
+                    && !entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false)
                 {
                     continue;
                 }
 
-                if entry
-                    .get("relative")
-                    .and_then(|v| v.as_bool())
-                    .is_some_and(std::convert::identity)
-                {
-                    base.join(value).to_string()
+                if let Some(base) = entry.get("relative").and_then(|v| {
+                    if v.as_ref().as_bool().is_some_and(std::convert::identity) {
+                        config_reader.get_origin_root(v)
+                    } else {
+                        None
+                    }
+                }) {
+                    base.join(map).to_string()
                 } else {
-                    value.to_owned()
+                    map.to_owned()
                 }
             }
             _ => continue,
@@ -115,43 +116,30 @@
 
 #[test]
 fn parse_output_cargo_config_env_works() {
+    use itertools::Itertools;
+
+    let cwd = paths::AbsPathBuf::try_from(
+        paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(),
+    )
+    .unwrap();
+    let config_path = cwd.join(".cargo").join("config.toml");
     let raw = r#"
-{
-  "env": {
-    "CARGO_WORKSPACE_DIR": {
-      "relative": true,
-      "value": ""
-    },
-    "INVALID": {
-      "relative": "invalidbool",
-      "value": "../relative"
-    },
-    "RELATIVE": {
-      "relative": true,
-      "value": "../relative"
-    },
-    "TEST": {
-      "value": "test"
-    },
-    "FORCED": {
-      "value": "test",
-      "force": true
-    },
-    "UNFORCED": {
-      "value": "test",
-      "force": false
-    },
-    "OVERWRITTEN": {
-      "value": "test"
-    },
-    "NOT_AN_OBJECT": "value"
-  }
-}
+env.CARGO_WORKSPACE_DIR.relative = true
+env.CARGO_WORKSPACE_DIR.value = ""
+env.INVALID.relative = "invalidbool"
+env.INVALID.value = "../relative"
+env.RELATIVE.relative = true
+env.RELATIVE.value = "../relative"
+env.TEST.value = "test"
+env.FORCED.value = "test"
+env.FORCED.force = true
+env.UNFORCED.value = "test"
+env.UNFORCED.forced = false
+env.OVERWRITTEN.value = "test"
+env.NOT_AN_OBJECT = "value"
 "#;
-    let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
-    let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
-    let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
-    let manifest = ManifestPath::try_from(manifest).unwrap();
+    let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n");
+    let config = CargoConfigFile::from_string_for_test(raw);
     let extra_env = [
         ("FORCED", Some("ignored")),
         ("UNFORCED", Some("newvalue")),
@@ -161,7 +149,7 @@
     .iter()
     .map(|(k, v)| (k.to_string(), v.map(ToString::to_string)))
     .collect();
-    let env = cargo_config_env(&manifest, &Some(config), &extra_env);
+    let env = cargo_config_env(&Some(config), &extra_env);
     assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
     assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
     assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index 920afe6..1b31138 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -8,6 +8,7 @@
 use std::{env, fs, ops::Not, path::Path, process::Command};
 
 use anyhow::{Result, format_err};
+use base_db::Env;
 use itertools::Itertools;
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
@@ -172,6 +173,32 @@
         }
     }
 
+    pub fn tool_path(&self, tool: Tool, current_dir: impl AsRef<Path>, envs: &Env) -> Utf8PathBuf {
+        match self.root() {
+            Some(root) => {
+                let mut cmd = toolchain::command(
+                    Tool::Rustup.path(),
+                    current_dir,
+                    &envs
+                        .into_iter()
+                        .map(|(k, v)| (k.clone(), Some(v.clone())))
+                        .collect::<FxHashMap<_, _>>(),
+                );
+                if !envs.contains_key("RUSTUP_TOOLCHAIN")
+                    && std::env::var_os("RUSTUP_TOOLCHAIN").is_none()
+                {
+                    cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(root));
+                }
+
+                cmd.arg("which");
+                cmd.arg(tool.name());
+                (|| Some(Utf8PathBuf::from(String::from_utf8(cmd.output().ok()?.stdout).ok()?)))()
+                    .unwrap_or_else(|| Utf8PathBuf::from(tool.name()))
+            }
+            _ => tool.path(),
+        }
+    }
+
     pub fn discover_proc_macro_srv(&self) -> Option<anyhow::Result<AbsPathBuf>> {
         let root = self.root()?;
         Some(
diff --git a/crates/project-model/src/toolchain_info/target_tuple.rs b/crates/project-model/src/toolchain_info/target_tuple.rs
index 9f12ede..12c64b5 100644
--- a/crates/project-model/src/toolchain_info/target_tuple.rs
+++ b/crates/project-model/src/toolchain_info/target_tuple.rs
@@ -53,7 +53,7 @@
 }
 
 fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
-    match parse_json_cargo_config_build_target(config) {
+    match parse_toml_cargo_config_build_target(config) {
         Ok(v) => v,
         Err(e) => {
             tracing::debug!("Failed to discover cargo config build target {e:?}");
@@ -63,18 +63,44 @@
 }
 
 // Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
-fn parse_json_cargo_config_build_target(
+fn parse_toml_cargo_config_build_target(
     config: &CargoConfigFile,
 ) -> anyhow::Result<Option<Vec<String>>> {
-    let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
-    match target {
-        Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
-        Some(v) => serde_json::from_value(v.clone())
-            .map(Option::Some)
-            .context("Failed to parse `build.target` as an array of target"),
-        // t`error: config value `build.target` is not set`, in which case we
-        // don't wanna log the error
-        None => Ok(None),
+    let Some(config_reader) = config.read() else {
+        return Ok(None);
+    };
+    let Some(target) = config_reader.get_spanned(["build", "target"]) else {
+        return Ok(None);
+    };
+
+    // if the target ends with `.json`, join it to the config file's parent dir.
+    // See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
+    let join_to_origin_if_json_path = |s: &str, spanned: &toml::Spanned<toml::de::DeValue<'_>>| {
+        if s.ends_with(".json") {
+            config_reader
+                .get_origin_root(spanned)
+                .map(|p| p.join(s).to_string())
+                .unwrap_or_else(|| s.to_owned())
+        } else {
+            s.to_owned()
+        }
+    };
+
+    let parse_err = "Failed to parse `build.target` as an array of target";
+
+    match target.as_ref() {
+        toml::de::DeValue::String(s) => {
+            Ok(Some(vec![join_to_origin_if_json_path(s.as_ref(), target)]))
+        }
+        toml::de::DeValue::Array(arr) => arr
+            .iter()
+            .map(|v| {
+                let s = v.as_ref().as_str().context(parse_err)?;
+                Ok(join_to_origin_if_json_path(s, v))
+            })
+            .collect::<anyhow::Result<_>>()
+            .map(Option::Some),
+        _ => Err(anyhow::anyhow!(parse_err)),
     }
 }
 
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index aa2e159..4d56668 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -2,6 +2,7 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
+use std::thread::Builder;
 use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
 
 use anyhow::Context;
@@ -12,7 +13,7 @@
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{Symbol, sym};
-use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
 use span::{Edition, FileId};
@@ -26,7 +27,7 @@
     ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
     WorkspaceBuildScripts,
     build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
-    cargo_config_file,
+    cargo_config_file::CargoConfigFile,
     cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
     env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
     project_json::{Crate, CrateArrayIdx},
@@ -267,7 +268,7 @@
 
         tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
         progress("querying project metadata".to_owned());
-        let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
+        let config_file = CargoConfigFile::load(cargo_toml, extra_env, &sysroot);
         let config_file_ = config_file.clone();
         let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
         let targets =
@@ -301,31 +302,39 @@
         // We can speed up loading a bit by spawning all of these processes in parallel (especially
         // on systems were process spawning is delayed)
         let join = thread::scope(|s| {
-            let rustc_cfg = s.spawn(|| {
-                rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
-            });
-            let target_data = s.spawn(|| {
-                target_data::get(
-                    toolchain_config,
-                    targets.first().map(Deref::deref),
-                    extra_env,
-                ).inspect_err(|e| {
-                    tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace")
+            let rustc_cfg = Builder::new()
+                .name("ProjectWorkspace::rustc_cfg".to_owned())
+                .spawn_scoped(s, || {
+                    rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
                 })
-            });
-
-            let rustc_dir = s.spawn(|| {
-                let rustc_dir = match rustc_source {
-                    Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
-                        .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
-                    Some(RustLibSource::Discover) => {
-                        sysroot.discover_rustc_src().ok_or_else(|| {
-                            Some("Failed to discover rustc source for sysroot.".to_owned())
+                .expect("failed to spawn thread");
+            let target_data = Builder::new()
+                .name("ProjectWorkspace::target_data".to_owned())
+                .spawn_scoped(s, || {
+                    target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
+                        .inspect_err(|e| {
+                            tracing::error!(%e,
+                                "failed fetching data layout for \
+                                {cargo_toml:?} workspace"
+                            )
                         })
-                    }
-                    None => Err(None),
-                };
-                rustc_dir.and_then(|rustc_dir| {
+                })
+                .expect("failed to spawn thread");
+
+            let rustc_dir = Builder::new()
+                .name("ProjectWorkspace::rustc_dir".to_owned())
+                .spawn_scoped(s, || {
+                    let rustc_dir = match rustc_source {
+                        Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+                            .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
+                        Some(RustLibSource::Discover) => {
+                            sysroot.discover_rustc_src().ok_or_else(|| {
+                                Some("Failed to discover rustc source for sysroot.".to_owned())
+                            })
+                        }
+                        None => Err(None),
+                    };
+                    rustc_dir.and_then(|rustc_dir| {
                     info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
                     match FetchMetadata::new(
                         &rustc_dir,
@@ -359,31 +368,41 @@
                         Err(e) => {
                             tracing::error!(
                                 %e,
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}",
+                                "Failed to read Cargo metadata from rustc source \
+                                at {rustc_dir}",
                             );
                             Err(Some(format!(
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
+                                "Failed to read Cargo metadata from rustc source \
+                                at {rustc_dir}: {e}"
                             )))
                         }
                     }
                 })
-            });
+                })
+                .expect("failed to spawn thread");
 
-            let cargo_metadata = s.spawn(|| fetch_metadata.exec(false, progress));
-            let loaded_sysroot = s.spawn(|| {
-                sysroot.load_workspace(
-                    &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
-                        config,
-                        workspace_dir,
-                        &targets,
-                        toolchain.clone(),
-                    )),
-                    config.no_deps,
-                    progress,
-                )
-            });
-            let cargo_env =
-                s.spawn(move || cargo_config_env(cargo_toml, &config_file, &config.extra_env));
+            let cargo_metadata = Builder::new()
+                .name("ProjectWorkspace::cargo_metadata".to_owned())
+                .spawn_scoped(s, || fetch_metadata.exec(false, progress))
+                .expect("failed to spawn thread");
+            let loaded_sysroot = Builder::new()
+                .name("ProjectWorkspace::loaded_sysroot".to_owned())
+                .spawn_scoped(s, || {
+                    sysroot.load_workspace(
+                        &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
+                            config,
+                            &targets,
+                            toolchain.clone(),
+                        )),
+                        config.no_deps,
+                        progress,
+                    )
+                })
+                .expect("failed to spawn thread");
+            let cargo_env = Builder::new()
+                .name("ProjectWorkspace::cargo_env".to_owned())
+                .spawn_scoped(s, move || cargo_config_env(&config_file, &config.extra_env))
+                .expect("failed to spawn thread");
             thread::Result::Ok((
                 rustc_cfg.join()?,
                 target_data.join()?,
@@ -481,7 +500,6 @@
                     sysroot.load_workspace(
                         &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
                             config,
-                            project_json.project_root(),
                             &targets,
                             toolchain.clone(),
                         )),
@@ -526,7 +544,7 @@
             None => Sysroot::empty(),
         };
 
-        let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
+        let config_file = CargoConfigFile::load(detached_file, &config.extra_env, &sysroot);
         let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
         let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
         let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
@@ -537,7 +555,6 @@
         let loaded_sysroot = sysroot.load_workspace(
             &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
                 config,
-                dir,
                 &targets,
                 toolchain.clone(),
             )),
@@ -563,8 +580,7 @@
             config.no_deps,
         );
         let cargo_script = fetch_metadata.exec(false, &|_| ()).ok().map(|(ws, error)| {
-            let cargo_config_extra_env =
-                cargo_config_env(detached_file, &config_file, &config.extra_env);
+            let cargo_config_extra_env = cargo_config_env(&config_file, &config.extra_env);
             (
                 CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
                 WorkspaceBuildScripts::default(),
@@ -1194,6 +1210,7 @@
         load,
         crate_ws_data.clone(),
     );
+    let cargo_path = sysroot.tool_path(Tool::Cargo, cargo.workspace_root(), cargo.env());
 
     let cfg_options = CfgOptions::from_iter(rustc_cfg);
 
@@ -1268,6 +1285,7 @@
                 } else {
                     Arc::new(pkg_data.manifest.parent().to_path_buf())
                 },
+                &cargo_path,
             );
             if let TargetKind::Lib { .. } = kind {
                 lib_tgt = Some((crate_id, name.clone()));
@@ -1375,6 +1393,7 @@
                 },
                 // FIXME: This looks incorrect but I don't think this causes problems.
                 crate_ws_data,
+                &cargo_path,
             );
         }
     }
@@ -1453,6 +1472,7 @@
     override_cfg: &CfgOverrides,
     build_scripts: &WorkspaceBuildScripts,
     crate_ws_data: Arc<CrateWorkspaceData>,
+    cargo_path: &Utf8Path,
 ) {
     let mut rustc_pkg_crates = FxHashMap::default();
     // The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1503,6 +1523,7 @@
                         } else {
                             Arc::new(pkg_data.manifest.parent().to_path_buf())
                         },
+                        cargo_path,
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
                     // Add dependencies on core / std / alloc for this crate
@@ -1560,11 +1581,12 @@
     build_data: Option<(&BuildScriptOutput, bool)>,
     cfg_options: CfgOptions,
     file_id: FileId,
-    cargo_name: &str,
+    cargo_crate_name: &str,
     kind: TargetKind,
     origin: CrateOrigin,
     crate_ws_data: Arc<CrateWorkspaceData>,
     proc_macro_cwd: Arc<AbsPathBuf>,
+    cargo_path: &Utf8Path,
 ) -> CrateBuilderId {
     let edition = pkg.edition;
     let potential_cfg_options = if pkg.features.is_empty() {
@@ -1591,8 +1613,8 @@
 
     let mut env = cargo.env().clone();
     inject_cargo_package_env(&mut env, pkg);
-    inject_cargo_env(&mut env);
-    inject_rustc_tool_env(&mut env, cargo_name, kind);
+    inject_cargo_env(&mut env, cargo_path);
+    inject_rustc_tool_env(&mut env, cargo_crate_name, kind);
 
     if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
         env.extend_from_other(envs);
@@ -1600,7 +1622,7 @@
     let crate_id = crate_graph.add_crate_root(
         file_id,
         edition,
-        Some(CrateDisplayName::from_canonical_name(cargo_name)),
+        Some(CrateDisplayName::from_canonical_name(cargo_crate_name)),
         Some(pkg.version.to_string()),
         cfg_options,
         potential_cfg_options,
@@ -1614,7 +1636,9 @@
         let proc_macro = match build_data {
             Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
                 match proc_macro_dylib_path {
-                    ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())),
+                    ProcMacroDylibPath::Path(path) => {
+                        Ok((cargo_crate_name.to_owned(), path.clone()))
+                    }
                     ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt),
                     ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound
                         if has_errors =>
@@ -1867,29 +1891,12 @@
 
 fn sysroot_metadata_config(
     config: &CargoConfig,
-    current_dir: &AbsPath,
     targets: &[String],
     toolchain_version: Option<Version>,
 ) -> CargoMetadataConfig {
-    // We run `cargo metadata` on sysroot with sysroot dir as a working directory, but still pass
-    // the `targets` from the cargo config evaluated from the workspace's `current_dir`.
-    // So, we need to *canonicalize* those *might-be-relative-paths-to-custom-target-json-files*.
-    //
-    // See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
-    let targets = targets
-        .iter()
-        .map(|target| {
-            if target.ends_with(".json") {
-                current_dir.join(target).to_string()
-            } else {
-                target.to_owned()
-            }
-        })
-        .collect();
-
     CargoMetadataConfig {
         features: Default::default(),
-        targets,
+        targets: targets.to_vec(),
         extra_args: Default::default(),
         extra_env: config.extra_env.clone(),
         toolchain_version,
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index b9dfe1f..782ec55 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -47,7 +47,7 @@
 tracing-subscriber.workspace = true
 tracing-tree.workspace = true
 triomphe.workspace = true
-toml = "0.8.23"
+toml.workspace = true
 nohash-hasher.workspace = true
 walkdir = "2.5.0"
 semver.workspace = true
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index cc8db1b..44c442f 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -307,6 +307,11 @@
         config.rediscover_workspaces();
     }
 
+    rayon::ThreadPoolBuilder::new()
+        .thread_name(|ix| format!("RayonWorker{}", ix))
+        .build_global()
+        .unwrap();
+
     // If the io_threads have an error, there's usually an error on the main
     // loop too because the channels are closed. Ensure we report both errors.
     match (rust_analyzer::main_loop(config, connection), io_threads.join()) {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c2b887c..099eed9 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -452,7 +452,11 @@
                     // Project has loaded properly, kick off initial flycheck
                     self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
                 }
-                if self.config.prefill_caches() {
+                // delay initial cache priming until proc macros are loaded, or we will load up a bunch of garbage into salsa
+                let proc_macros_loaded = self.config.prefill_caches()
+                    && !self.config.expand_proc_macros()
+                    || self.fetch_proc_macros_queue.last_op_result().copied().unwrap_or(false);
+                if proc_macros_loaded {
                     self.prime_caches_queue.request_op("became quiescent".to_owned(), ());
                 }
             }
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index bb09933..83b2413 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -156,6 +156,43 @@
     }
 }
 
+impl<S> Drop for SpanMap<S> {
+    fn drop(&mut self) {
+        struct SendPtr(*mut [()]);
+        unsafe impl Send for SendPtr {}
+        static SPAN_MAP_DROP_THREAD: std::sync::OnceLock<
+            std::sync::mpsc::Sender<(SendPtr, fn(SendPtr))>,
+        > = std::sync::OnceLock::new();
+        SPAN_MAP_DROP_THREAD
+            .get_or_init(|| {
+                let (sender, receiver) = std::sync::mpsc::channel::<(SendPtr, fn(SendPtr))>();
+                std::thread::Builder::new()
+                    .name("SpanMapDropper".to_owned())
+                    .spawn(move || receiver.iter().for_each(|(b, drop)| drop(b)))
+                    .unwrap();
+                sender
+            })
+            .send((
+                unsafe {
+                    SendPtr(std::mem::transmute::<*mut [(TextSize, SpanData<S>)], *mut [()]>(
+                        Box::<[(TextSize, SpanData<S>)]>::into_raw(
+                            std::mem::take(&mut self.spans).into_boxed_slice(),
+                        ),
+                    ))
+                },
+                |b: SendPtr| {
+                    _ = unsafe {
+                        Box::from_raw(std::mem::transmute::<
+                            *mut [()],
+                            *mut [(TextSize, SpanData<S>)],
+                        >(b.0))
+                    }
+                },
+            ))
+            .unwrap();
+    }
+}
+
 #[derive(PartialEq, Eq, Hash, Debug)]
 pub struct RealSpanMap {
     file_id: EditionedFileId,
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index a3c19f7..27d2889 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -67,7 +67,7 @@
 /// files.
 #[derive(Debug, PartialEq, Eq)]
 pub struct Parse<T> {
-    green: GreenNode,
+    green: Option<GreenNode>,
     errors: Option<Arc<[SyntaxError]>>,
     _ty: PhantomData<fn() -> T>,
 }
@@ -81,14 +81,14 @@
 impl<T> Parse<T> {
     fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
         Parse {
-            green,
+            green: Some(green),
             errors: if errors.is_empty() { None } else { Some(errors.into()) },
             _ty: PhantomData,
         }
     }
 
     pub fn syntax_node(&self) -> SyntaxNode {
-        SyntaxNode::new_root(self.green.clone())
+        SyntaxNode::new_root(self.green.as_ref().unwrap().clone())
     }
 
     pub fn errors(&self) -> Vec<SyntaxError> {
@@ -100,8 +100,10 @@
 
 impl<T: AstNode> Parse<T> {
     /// Converts this parse result into a parse result for an untyped syntax tree.
-    pub fn to_syntax(self) -> Parse<SyntaxNode> {
-        Parse { green: self.green, errors: self.errors, _ty: PhantomData }
+    pub fn to_syntax(mut self) -> Parse<SyntaxNode> {
+        let green = self.green.take();
+        let errors = self.errors.take();
+        Parse { green, errors, _ty: PhantomData }
     }
 
     /// Gets the parsed syntax tree as a typed ast node.
@@ -124,9 +126,9 @@
 }
 
 impl Parse<SyntaxNode> {
-    pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
+    pub fn cast<N: AstNode>(mut self) -> Option<Parse<N>> {
         if N::cast(self.syntax_node()).is_some() {
-            Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
+            Some(Parse { green: self.green.take(), errors: self.errors.take(), _ty: PhantomData })
         } else {
             None
         }
@@ -162,7 +164,7 @@
             edition,
         )
         .map(|(green_node, errors, _reparsed_range)| Parse {
-            green: green_node,
+            green: Some(green_node),
             errors: if errors.is_empty() { None } else { Some(errors.into()) },
             _ty: PhantomData,
         })
@@ -198,6 +200,27 @@
     }
 }
 
+impl<T> Drop for Parse<T> {
+    fn drop(&mut self) {
+        let Some(green) = self.green.take() else {
+            return;
+        };
+        static PARSE_DROP_THREAD: std::sync::OnceLock<std::sync::mpsc::Sender<GreenNode>> =
+            std::sync::OnceLock::new();
+        PARSE_DROP_THREAD
+            .get_or_init(|| {
+                let (sender, receiver) = std::sync::mpsc::channel::<GreenNode>();
+                std::thread::Builder::new()
+                    .name("ParseNodeDropper".to_owned())
+                    .spawn(move || receiver.iter().for_each(drop))
+                    .unwrap();
+                sender
+            })
+            .send(green)
+            .unwrap();
+    }
+}
+
 /// `SourceFile` represents a parse tree for a single Rust file.
 pub use crate::ast::SourceFile;
 
diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs
index d3afac8..62867fd 100644
--- a/crates/test-utils/src/lib.rs
+++ b/crates/test-utils/src/lib.rs
@@ -43,7 +43,7 @@
 #[macro_export]
 macro_rules! assert_eq_text {
     ($left:expr, $right:expr) => {
-        assert_eq_text!($left, $right,)
+        $crate::assert_eq_text!($left, $right,)
     };
     ($left:expr, $right:expr, $($tt:tt)*) => {{
         let left = $left;
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 696928b..d5905af 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -68,6 +68,7 @@
 //!     transmute:
 //!     try: infallible
 //!     tuple:
+//!     unary_ops:
 //!     unpin: sized
 //!     unsize: sized
 //!     write: fmt
@@ -591,13 +592,13 @@
         impl<T: PointeeSized> Deref for &T {
             type Target = T;
             fn deref(&self) -> &T {
-                loop {}
+                *self
             }
         }
         impl<T: PointeeSized> Deref for &mut T {
             type Target = T;
             fn deref(&self) -> &T {
-                loop {}
+                *self
             }
         }
         // region:deref_mut
@@ -1056,6 +1057,9 @@
                 type Output = $t;
                 fn add(self, other: $t) -> $t { self + other }
             }
+            impl AddAssign for $t {
+                fn add_assign(&mut self, other: $t) { *self += other; }
+            }
         )*)
     }
 
@@ -1063,6 +1067,24 @@
     // endregion:builtin_impls
     // endregion:add
 
+    // region:unary_ops
+    #[lang = "not"]
+    pub const trait Not {
+        type Output;
+
+        #[must_use]
+        fn not(self) -> Self::Output;
+    }
+
+    #[lang = "neg"]
+    pub const trait Neg {
+        type Output;
+
+        #[must_use = "this returns the result of the operation, without modifying the original"]
+        fn neg(self) -> Self::Output;
+    }
+    // endregion:unary_ops
+
     // region:coroutine
     mod coroutine {
         use crate::pin::Pin;
@@ -1118,6 +1140,12 @@
 
     pub trait Eq: PartialEq<Self> + PointeeSized {}
 
+    // region:builtin_impls
+    impl PartialEq for () {
+        fn eq(&self, other: &()) -> bool { true }
+    }
+    // endregion:builtin_impls
+
     // region:derive
     #[rustc_builtin_macro]
     pub macro PartialEq($item:item) {}
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 6dd4485..d49d19f 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -934,6 +934,29 @@
                 "url": "https://github.com/sponsors/nzakas"
             }
         },
+        "node_modules/@isaacs/balanced-match": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+            "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+            "dev": true,
+            "license": "MIT",
+            "engines": {
+                "node": "20 || >=22"
+            }
+        },
+        "node_modules/@isaacs/brace-expansion": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+            "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+            "dev": true,
+            "license": "MIT",
+            "dependencies": {
+                "@isaacs/balanced-match": "^4.0.1"
+            },
+            "engines": {
+                "node": "20 || >=22"
+            }
+        },
         "node_modules/@isaacs/cliui": {
             "version": "8.0.2",
             "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -1296,9 +1319,9 @@
             "license": "MIT"
         },
         "node_modules/@textlint/linter-formatter/node_modules/js-yaml": {
-            "version": "3.14.1",
-            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-            "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+            "version": "3.14.2",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+            "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -3725,13 +3748,13 @@
             }
         },
         "node_modules/foreground-child": {
-            "version": "3.3.0",
-            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
-            "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+            "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
             "dev": true,
             "license": "ISC",
             "dependencies": {
-                "cross-spawn": "^7.0.0",
+                "cross-spawn": "^7.0.6",
                 "signal-exit": "^4.0.1"
             },
             "engines": {
@@ -3848,15 +3871,15 @@
             "optional": true
         },
         "node_modules/glob": {
-            "version": "11.0.1",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
-            "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
+            "version": "11.1.0",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
+            "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
             "dev": true,
-            "license": "ISC",
+            "license": "BlueOak-1.0.0",
             "dependencies": {
-                "foreground-child": "^3.1.0",
-                "jackspeak": "^4.0.1",
-                "minimatch": "^10.0.0",
+                "foreground-child": "^3.3.1",
+                "jackspeak": "^4.1.1",
+                "minimatch": "^10.1.1",
                 "minipass": "^7.1.2",
                 "package-json-from-dist": "^1.0.0",
                 "path-scurry": "^2.0.0"
@@ -3885,13 +3908,13 @@
             }
         },
         "node_modules/glob/node_modules/minimatch": {
-            "version": "10.0.1",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
-            "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
+            "version": "10.1.1",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
+            "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
             "dev": true,
-            "license": "ISC",
+            "license": "BlueOak-1.0.0",
             "dependencies": {
-                "brace-expansion": "^2.0.1"
+                "@isaacs/brace-expansion": "^5.0.0"
             },
             "engines": {
                 "node": "20 || >=22"
@@ -4363,9 +4386,9 @@
             }
         },
         "node_modules/jackspeak": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
-            "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
+            "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
             "dev": true,
             "license": "BlueOak-1.0.0",
             "dependencies": {
@@ -4395,9 +4418,9 @@
             "license": "MIT"
         },
         "node_modules/js-yaml": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-            "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+            "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
             "dev": true,
             "license": "MIT",
             "dependencies": {
@@ -6976,9 +6999,9 @@
             }
         },
         "node_modules/wrap-ansi/node_modules/ansi-styles": {
-            "version": "6.2.1",
-            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
-            "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+            "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
             "dev": true,
             "license": "MIT",
             "engines": {
diff --git a/rust-version b/rust-version
index 0e89b4a..f545ef4 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-c5dabe8cf798123087d094f06417f5a767ca73e8
+6159a44067ebce42b38f062cc7df267a1348e092
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 9bd87a7..18f6c1f 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -706,7 +706,23 @@
         }
     };
 
-    add_preamble(crate::flags::CodegenType::Grammar, reformat(ast.to_string()))
+    let result = add_preamble(crate::flags::CodegenType::Grammar, reformat(ast.to_string()));
+
+    if let Some(start) = result.find("macro_rules ! T_")
+        && let Some(macro_end) = result[start..].find("\nimpl ::core::marker::Copy")
+    {
+        let macro_section = &result[start..start + macro_end];
+        let formatted_macro = macro_section
+            .replace("T_ { [", "T_ {\n    [")
+            .replace(" ; [", ";\n    [")
+            .replace(" ; }", ";\n}")
+            .trim_end()
+            .to_owned()
+            + "\n";
+        return result.replace(macro_section, &formatted_macro);
+    }
+
+    result
 }
 
 fn to_upper_snake_case(s: &str) -> String {