about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2023-05-26 00:45:37 +0330
committerhkalbasi <hamidrezakalbasi@protonmail.com>2023-05-28 23:25:15 +0330
commit51368793b462306724eafa7d9fb0797329b671f3 (patch)
tree1b013f2ec009d038d627d9621d095006ca9ac762
parent505fd09f9e020b096d014e68b667268e743c2dd6 (diff)
downloadrust-51368793b462306724eafa7d9fb0797329b671f3.tar.gz
rust-51368793b462306724eafa7d9fb0797329b671f3.zip
MIR episode 6
-rw-r--r--crates/hir-def/src/body/lower.rs157
-rw-r--r--crates/hir-def/src/body/pretty.rs30
-rw-r--r--crates/hir-def/src/body/scope.rs6
-rw-r--r--crates/hir-def/src/hir.rs19
-rw-r--r--crates/hir-ty/src/consteval.rs42
-rw-r--r--crates/hir-ty/src/consteval/tests.rs75
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs24
-rw-r--r--crates/hir-ty/src/db.rs23
-rw-r--r--crates/hir-ty/src/display.rs16
-rw-r--r--crates/hir-ty/src/infer.rs16
-rw-r--r--crates/hir-ty/src/infer/closure.rs26
-rw-r--r--crates/hir-ty/src/infer/expr.rs18
-rw-r--r--crates/hir-ty/src/infer/mutability.rs3
-rw-r--r--crates/hir-ty/src/infer/pat.rs6
-rw-r--r--crates/hir-ty/src/layout.rs40
-rw-r--r--crates/hir-ty/src/layout/adt.rs24
-rw-r--r--crates/hir-ty/src/layout/tests.rs11
-rw-r--r--crates/hir-ty/src/method_resolution.rs4
-rw-r--r--crates/hir-ty/src/mir.rs12
-rw-r--r--crates/hir-ty/src/mir/eval.rs318
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs42
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs253
-rw-r--r--crates/hir-ty/src/mir/lower.rs203
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs170
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs369
-rw-r--r--crates/hir-ty/src/mir/pretty.rs2
-rw-r--r--crates/hir-ty/src/tests/macros.rs24
-rw-r--r--crates/hir-ty/src/tests/never_type.rs28
-rw-r--r--crates/hir-ty/src/tests/patterns.rs10
-rw-r--r--crates/hir-ty/src/tests/regression.rs22
-rw-r--r--crates/hir/src/lib.rs27
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs3
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
35 files changed, 1474 insertions, 556 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index e9cb51d5bf8..acc9943481a 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -31,8 +31,8 @@ use crate::{
     expander::Expander,
     hir::{
         dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
-        ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
-        RecordLitField, Statement,
+        ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, Pat, PatId,
+        RecordFieldPat, RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
     lang_item::LangItem,
@@ -295,13 +295,7 @@ impl ExprCollector<'_> {
 
                 self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
             }
-            ast::Expr::ForExpr(e) => {
-                let label = e.label().map(|label| self.collect_label(label));
-                let iterable = self.collect_expr_opt(e.iterable());
-                let pat = self.collect_pat_top(e.pat());
-                let body = self.collect_labelled_block_opt(label, e.loop_body());
-                self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
-            }
+            ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
             ast::Expr::CallExpr(e) => {
                 let is_rustc_box = {
                     let attrs = e.attrs();
@@ -703,6 +697,91 @@ impl ExprCollector<'_> {
         expr_id
     }
 
+    /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
+    /// ```ignore (pseudo-rust)
+    /// match IntoIterator::into_iter(<head>) {
+    ///     mut iter => {
+    ///         [opt_ident]: loop {
+    ///             match Iterator::next(&mut iter) {
+    ///                 None => break,
+    ///                 Some(<pat>) => <body>,
+    ///             };
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
+        let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: {
+            if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) {
+                if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) {
+                    if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) {
+                        if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) {
+                            break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none);
+                        }
+                    }
+                }
+            }
+            // Some of the needed lang items are missing, so we can't desugar
+            return self.alloc_expr(Expr::Missing, syntax_ptr);
+        };
+        let head = self.collect_expr_opt(e.iterable());
+        let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone());
+        let iterator = self.alloc_expr(
+            Expr::Call {
+                callee: into_iter_fn_expr,
+                args: Box::new([head]),
+                is_assignee_expr: false,
+            },
+            syntax_ptr.clone(),
+        );
+        let none_arm = MatchArm {
+            pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+            guard: None,
+            expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()),
+        };
+        let some_pat = Pat::TupleStruct {
+            path: Some(Box::new(option_some)),
+            args: Box::new([self.collect_pat_top(e.pat())]),
+            ellipsis: None,
+        };
+        let some_arm = MatchArm {
+            pat: self.alloc_pat_desugared(some_pat),
+            guard: None,
+            expr: self.collect_expr_opt(e.loop_body().map(|x| x.into())),
+        };
+        let iter_name = Name::generate_new_name();
+        let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable);
+        let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone());
+        let iter_expr_mut = self.alloc_expr(
+            Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
+            syntax_ptr.clone(),
+        );
+        let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone());
+        let iter_next_expr = self.alloc_expr(
+            Expr::Call {
+                callee: iter_next_fn_expr,
+                args: Box::new([iter_expr_mut]),
+                is_assignee_expr: false,
+            },
+            syntax_ptr.clone(),
+        );
+        let loop_inner = self.alloc_expr(
+            Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
+            syntax_ptr.clone(),
+        );
+        let label = e.label().map(|label| self.collect_label(label));
+        let loop_outer =
+            self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+        let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+        self.alloc_expr(
+            Expr::Match {
+                expr: iterator,
+                arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]),
+            },
+            syntax_ptr.clone(),
+        )
+    }
+
     /// Desugar `ast::TryExpr` from: `<expr>?` into:
     /// ```ignore (pseudo-rust)
     /// match Try::branch(<expr>) {
@@ -1159,22 +1238,12 @@ impl ExprCollector<'_> {
             }
             #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
             ast::Pat::LiteralPat(lit) => 'b: {
-                if let Some(ast_lit) = lit.literal() {
-                    let mut hir_lit: Literal = ast_lit.kind().into();
-                    if lit.minus_token().is_some() {
-                        let Some(h) = hir_lit.negate() else {
-                            break 'b Pat::Missing;
-                        };
-                        hir_lit = h;
-                    }
-                    let expr = Expr::Literal(hir_lit);
-                    let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
-                    let expr_id = self.alloc_expr(expr, expr_ptr);
-                    Pat::Lit(expr_id)
-                } else {
-                    Pat::Missing
-                }
-            },
+                let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
+                let expr = Expr::Literal(hir_lit);
+                let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
+                let expr_id = self.alloc_expr(expr, expr_ptr);
+                Pat::Lit(expr_id)
+            }
             ast::Pat::RestPat(_) => {
                 // `RestPat` requires special handling and should not be mapped
                 // to a Pat. Here we are using `Pat::Missing` as a fallback for
@@ -1215,8 +1284,30 @@ impl ExprCollector<'_> {
                 }
                 None => Pat::Missing,
             },
-            // FIXME: implement
-            ast::Pat::RangePat(_) => Pat::Missing,
+            // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
+            ast::Pat::RangePat(p) => {
+                let mut range_part_lower = |p: Option<ast::Pat>| {
+                    p.and_then(|x| match &x {
+                        ast::Pat::LiteralPat(x) => {
+                            Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0)))
+                        }
+                        ast::Pat::IdentPat(p) => {
+                            let name =
+                                p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+                            Some(Box::new(LiteralOrConst::Const(name.into())))
+                        }
+                        ast::Pat::PathPat(p) => p
+                            .path()
+                            .and_then(|path| self.expander.parse_path(self.db, path))
+                            .map(LiteralOrConst::Const)
+                            .map(Box::new),
+                        _ => None,
+                    })
+                };
+                let start = range_part_lower(p.start());
+                let end = range_part_lower(p.end());
+                Pat::Range { start, end }
+            }
         };
         let ptr = AstPtr::new(&pat);
         self.alloc_pat(pattern, Either::Left(ptr))
@@ -1338,6 +1429,18 @@ impl ExprCollector<'_> {
     // endregion: labels
 }
 
+fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
+    let ast_lit = lit.literal()?;
+    let mut hir_lit: Literal = ast_lit.kind().into();
+    if lit.minus_token().is_some() {
+        let Some(h) = hir_lit.negate() else {
+            return None;
+        };
+        hir_lit = h;
+    }
+    Some((hir_lit, ast_lit))
+}
+
 impl ExprCollector<'_> {
     fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.to_source(ptr);
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 7390a8f1f2b..88380aa355d 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -7,7 +7,8 @@ use syntax::ast::HasName;
 
 use crate::{
     hir::{
-        Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement,
+        Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
+        Movability, Statement,
     },
     pretty::{print_generic_args, print_path, print_type_ref},
     type_ref::TypeRef,
@@ -184,16 +185,6 @@ impl<'a> Printer<'a> {
                 self.print_expr(*condition);
                 self.print_expr(*body);
             }
-            Expr::For { iterable, pat, body, label } => {
-                if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name.display(self.db));
-                }
-                w!(self, "for ");
-                self.print_pat(*pat);
-                w!(self, " in ");
-                self.print_expr(*iterable);
-                self.print_expr(*body);
-            }
             Expr::Call { callee, args, is_assignee_expr: _ } => {
                 self.print_expr(*callee);
                 w!(self, "(");
@@ -534,9 +525,13 @@ impl<'a> Printer<'a> {
                 w!(self, "}}");
             }
             Pat::Range { start, end } => {
-                self.print_expr(*start);
-                w!(self, "...");
-                self.print_expr(*end);
+                if let Some(start) = start {
+                    self.print_literal_or_const(start);
+                }
+                w!(self, "..=");
+                if let Some(end) = end {
+                    self.print_literal_or_const(end);
+                }
             }
             Pat::Slice { prefix, slice, suffix } => {
                 w!(self, "[");
@@ -627,6 +622,13 @@ impl<'a> Printer<'a> {
         }
     }
 
+    fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+        match literal_or_const {
+            LiteralOrConst::Literal(l) => self.print_literal(l),
+            LiteralOrConst::Const(c) => self.print_path(c),
+        }
+    }
+
     fn print_literal(&mut self, literal: &Literal) {
         match literal {
             Literal::String(it) => w!(self, "{:?}", it),
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index b81130c0c24..69741c445fb 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -228,12 +228,6 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             scopes.set_scope(expr, scope);
             compute_block_scopes(statements, *tail, body, scopes, &mut scope);
         }
-        Expr::For { iterable, pat, body: body_expr, label } => {
-            compute_expr_scopes(*iterable, body, scopes, scope);
-            let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            scopes.add_pat_bindings(body, scope, *pat);
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
-        }
         Expr::While { condition, body: body_expr, label } => {
             let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
             compute_expr_scopes(*condition, body, scopes, &mut scope);
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index a42f8183abc..8102efdba30 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -96,6 +96,13 @@ pub enum Literal {
     Float(FloatTypeWrapper, Option<BuiltinFloat>),
 }
 
+#[derive(Debug, Clone, Eq, PartialEq)]
+/// Used in range patterns.
+pub enum LiteralOrConst {
+    Literal(Literal),
+    Const(Path),
+}
+
 impl Literal {
     pub fn negate(self) -> Option<Self> {
         if let Literal::Int(i, k) = self {
@@ -189,12 +196,6 @@ pub enum Expr {
         body: ExprId,
         label: Option<LabelId>,
     },
-    For {
-        iterable: ExprId,
-        pat: PatId,
-        body: ExprId,
-        label: Option<LabelId>,
-    },
     Call {
         callee: ExprId,
         args: Box<[ExprId]>,
@@ -382,10 +383,6 @@ impl Expr {
                 f(*condition);
                 f(*body);
             }
-            Expr::For { iterable, body, .. } => {
-                f(*iterable);
-                f(*body);
-            }
             Expr::Call { callee, args, .. } => {
                 f(*callee);
                 args.iter().copied().for_each(f);
@@ -526,7 +523,7 @@ pub enum Pat {
     Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
     Or(Box<[PatId]>),
     Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
-    Range { start: ExprId, end: ExprId },
+    Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
     Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
     Path(Box<Path>),
     Lit(ExprId),
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 0df2b39bbdc..40b63b17b5a 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -14,9 +14,9 @@ use stdx::never;
 use triomphe::Arc;
 
 use crate::{
-    db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
-    to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
-    Interner, MemoryMap, Substitution, Ty, TyBuilder,
+    db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
+    mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
+    ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
 };
 
 use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@@ -130,14 +130,15 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
 
 /// Interns a constant scalar with the given type
 pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+    let layout = db.layout_of_ty(ty.clone(), krate);
     let bytes = match value {
         ConstRef::Int(i) => {
             // FIXME: We should handle failure of layout better.
-            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
             ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
         }
         ConstRef::UInt(i) => {
-            let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+            let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
             ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
         }
         ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
@@ -206,15 +207,22 @@ pub(crate) fn const_eval_query(
     subst: Substitution,
 ) -> Result<Const, ConstEvalError> {
     let body = match def {
-        GeneralConstId::ConstId(c) => db.mir_body(c.into())?,
+        GeneralConstId::ConstId(c) => {
+            db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
+        }
         GeneralConstId::AnonymousConstId(c) => {
             let (def, root) = db.lookup_intern_anonymous_const(c);
             let body = db.body(def);
             let infer = db.infer(def);
-            Arc::new(lower_to_mir(db, def, &body, &infer, root)?)
+            Arc::new(monomorphize_mir_body_bad(
+                db,
+                lower_to_mir(db, def, &body, &infer, root)?,
+                subst,
+                db.trait_environment_for_body(def),
+            )?)
         }
     };
-    let c = interpret_mir(db, &body, subst, false).0?;
+    let c = interpret_mir(db, &body, false).0?;
     Ok(c)
 }
 
@@ -222,8 +230,12 @@ pub(crate) fn const_eval_static_query(
     db: &dyn HirDatabase,
     def: StaticId,
 ) -> Result<Const, ConstEvalError> {
-    let body = db.mir_body(def.into())?;
-    let c = interpret_mir(db, &body, Substitution::empty(Interner), false).0?;
+    let body = db.monomorphized_mir_body(
+        def.into(),
+        Substitution::empty(Interner),
+        db.trait_environment_for_body(def.into()),
+    )?;
+    let c = interpret_mir(db, &body, false).0?;
     Ok(c)
 }
 
@@ -245,8 +257,12 @@ pub(crate) fn const_eval_discriminant_variant(
         };
         return Ok(value);
     }
-    let mir_body = db.mir_body(def)?;
-    let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false).0?;
+    let mir_body = db.monomorphized_mir_body(
+        def,
+        Substitution::empty(Interner),
+        db.trait_environment_for_body(def),
+    )?;
+    let c = interpret_mir(db, &mir_body, false).0?;
     let c = try_const_usize(db, &c).unwrap() as i128;
     Ok(c)
 }
@@ -271,7 +287,7 @@ pub(crate) fn eval_to_const(
     }
     let infer = ctx.clone().resolve_all();
     if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
-        if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true).0 {
+        if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
             return result;
         }
     }
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 6ff8e2de67d..6a7d0456486 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -157,10 +157,22 @@ fn casts() {
         r#"
     //- minicore: coerce_unsized, index, slice
     const GOAL: usize = {
+        let a = &[10, 20, 30, 40] as &[i32];
+        a.len()
+    };
+        "#,
+        4,
+    );
+    check_number(
+        r#"
+    //- minicore: coerce_unsized, index, slice
+    const GOAL: usize = {
         let a = [10, 20, 3, 15];
         let x: &[i32] = &a;
         let y: *const [i32] = x;
         let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
+        let q = z as *const str;
+        let p = q as *const [u8];
         let w = unsafe { &*z };
         w.len()
     };
@@ -987,11 +999,15 @@ fn path_pattern_matching() {
 
     const MY_SEASON: Season = Summer;
 
+    impl Season {
+        const FALL: Season = Fall;
+    }
+
     const fn f(x: Season) -> i32 {
         match x {
             Spring => 1,
             MY_SEASON => 2,
-            Fall => 3,
+            Season::FALL => 3,
             Winter => 4,
         }
     }
@@ -1032,6 +1048,27 @@ fn pattern_matching_literal() {
 }
 
 #[test]
+fn pattern_matching_range() {
+    check_number(
+        r#"
+    pub const L: i32 = 6;
+    mod x {
+        pub const R: i32 = 100;
+    }
+    const fn f(x: i32) -> i32 {
+        match x {
+            -1..=5 => x * 10,
+            L..=x::R => x * 100,
+            _ => x,
+        }
+    }
+    const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000);
+        "#,
+        11008,
+    );
+}
+
+#[test]
 fn pattern_matching_slice() {
     check_number(
         r#"
@@ -1045,6 +1082,22 @@ fn pattern_matching_slice() {
         "#,
         10 + 4 + 60 + 16,
     );
+    check_number(
+        r#"
+    //- minicore: slice, index, coerce_unsized, copy
+    const fn f(x: &[usize]) -> usize {
+        match x {
+            [] => 0,
+            [a] => *a,
+            &[a, b] => a + b,
+            [a, b @ .., c, d] => *a + b.len() + *c + *d,
+        }
+    }
+    const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100])
+        + f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]);
+        "#,
+        33213,
+    );
 }
 
 #[test]
@@ -2105,6 +2158,26 @@ fn const_generic_subst_fn() {
     "#,
         11,
     );
+    check_number(
+        r#"
+    fn f<const N: usize>(x: [i32; N]) -> usize {
+        N
+    }
+
+    trait ArrayExt {
+        fn f(self) -> usize;
+    }
+
+    impl<T, const N: usize> ArrayExt for [T; N] {
+        fn g(self) -> usize {
+            f(self)
+        }
+    }
+
+    const GOAL: usize = f([1, 2, 5]);
+    "#,
+        3,
+    );
 }
 
 #[test]
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 0dd120e5b9e..1feb9a4441c 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -68,6 +68,30 @@ fn wrapping_add() {
 }
 
 #[test]
+fn saturating_add() {
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn saturating_add<T>(a: T, b: T) -> T;
+        }
+
+        const GOAL: u8 = saturating_add(10, 250);
+        "#,
+        255,
+    );
+    check_number(
+        r#"
+        extern "rust-intrinsic" {
+            pub fn saturating_add<T>(a: T, b: T) -> T;
+        }
+
+        const GOAL: i8 = saturating_add(5, 8);
+        "#,
+        13,
+    );
+}
+
+#[test]
 fn allocator() {
     check_number(
         r#"
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 3aa43dedcd5..ca8a394e360 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -41,6 +41,23 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
     fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
 
+    #[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
+    #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
+    fn monomorphized_mir_body(
+        &self,
+        def: DefWithBodyId,
+        subst: Substitution,
+        env: Arc<crate::TraitEnvironment>,
+    ) -> Result<Arc<MirBody>, MirLowerError>;
+
+    #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
+    fn monomorphized_mir_body_for_closure(
+        &self,
+        def: ClosureId,
+        subst: Substitution,
+        env: Arc<crate::TraitEnvironment>,
+    ) -> Result<Arc<MirBody>, MirLowerError>;
+
     #[salsa::invoke(crate::mir::borrowck_query)]
     fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
 
@@ -84,7 +101,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
         def: AdtId,
         subst: Substitution,
         krate: CrateId,
-    ) -> Result<Layout, LayoutError>;
+    ) -> Result<Arc<Layout>, LayoutError>;
+
+    #[salsa::invoke(crate::layout::layout_of_ty_query)]
+    #[salsa::cycle(crate::layout::layout_of_ty_recover)]
+    fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result<Arc<Layout>, LayoutError>;
 
     #[salsa::invoke(crate::layout::target_data_layout_query)]
     fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 3a34485adc4..058d5059b16 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -26,9 +26,7 @@ use stdx::never;
 
 use crate::{
     db::HirDatabase,
-    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
-    layout::layout_of_ty,
-    lt_from_placeholder_idx,
+    from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
     mapping::from_chalk,
     mir::pad16,
     primitive, to_assoc_type_id,
@@ -309,6 +307,8 @@ pub enum ClosureStyle {
     RANotation,
     /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
     ClosureWithId,
+    /// `{closure#14825}<i32, ()>`, useful for internal usage.
+    ClosureWithSubst,
     /// `…`, which is the `TYPE_HINT_TRUNCATION`
     Hide,
 }
@@ -507,7 +507,7 @@ fn render_const_scalar(
             _ => f.write_str("<ref-not-supported>"),
         },
         chalk_ir::TyKind::Tuple(_, subst) => {
-            let Ok(layout) = layout_of_ty(f.db, ty, krate) else {
+            let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else {
                 return f.write_str("<layout-error>");
             };
             f.write_str("(")?;
@@ -520,7 +520,7 @@ fn render_const_scalar(
                 }
                 let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
                 let offset = layout.fields.offset(id).bytes_usize();
-                let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
                     f.write_str("<layout-error>")?;
                     continue;
                 };
@@ -545,7 +545,7 @@ fn render_const_scalar(
                                 .offset(u32::from(id.into_raw()) as usize)
                                 .bytes_usize();
                             let ty = field_types[id].clone().substitute(Interner, subst);
-                            let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+                            let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
                                 return f.write_str("<layout-error>");
                             };
                             let size = layout.size.bytes_usize();
@@ -931,6 +931,10 @@ impl HirDisplay for Ty {
                     ClosureStyle::ClosureWithId => {
                         return write!(f, "{{closure#{:?}}}", id.0.as_u32())
                     }
+                    ClosureStyle::ClosureWithSubst => {
+                        write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
+                        return hir_fmt_generics(f, substs, None);
+                    }
                     _ => (),
                 }
                 let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 11c0ccf547d..fa81fe39aa1 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1153,22 +1153,6 @@ impl<'a> InferenceContext<'a> {
         self.db.lang_item(krate, item)
     }
 
-    fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
-        let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
-            .as_function()?
-            .lookup(self.db.upcast()).container
-        else { return None };
-        self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
-    }
-
-    fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
-        let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)?
-            .as_function()?
-            .lookup(self.db.upcast()).container
-        else { return None };
-        self.db.trait_data(trait_).associated_type_by_name(&name![Item])
-    }
-
     fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
         self.db.trait_data(trait_).associated_type_by_name(&name![Output])
     }
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 787c5c54a29..662f2e5fa16 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -5,7 +5,7 @@ use std::{cmp, collections::HashMap, convert::Infallible, mem};
 use chalk_ir::{
     cast::Cast,
     fold::{FallibleTypeFolder, TypeFoldable},
-    AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
+    AliasEq, AliasTy, BoundVar, ConstData, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
 };
 use hir_def::{
     data::adt::VariantData,
@@ -29,8 +29,8 @@ use crate::{
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
     utils::{self, generics, pattern_matching_dereference_count, Generics},
-    Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, Interner,
-    Substitution, Ty, TyExt,
+    Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, ConstValue, DynTy, FnPointer, FnSig,
+    Interner, Substitution, Ty, TyExt,
 };
 
 use super::{Expectation, InferenceContext};
@@ -259,6 +259,23 @@ impl CapturedItemWithoutTy {
                     Interner
                 }
 
+                fn try_fold_free_placeholder_const(
+                    &mut self,
+                    ty: chalk_ir::Ty<Interner>,
+                    idx: chalk_ir::PlaceholderIndex,
+                    outer_binder: DebruijnIndex,
+                ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+                    let x = from_placeholder_idx(self.db, idx);
+                    let Some(idx) = self.generics.param_idx(x) else {
+                        return Err(());
+                    };
+                    Ok(ConstData {
+                        ty,
+                        value: ConstValue::BoundVar(BoundVar::new(outer_binder, idx)),
+                    }
+                    .intern(Interner))
+                }
+
                 fn try_fold_free_placeholder_ty(
                     &mut self,
                     idx: chalk_ir::PlaceholderIndex,
@@ -490,8 +507,7 @@ impl InferenceContext<'_> {
                     self.consume_expr(*tail);
                 }
             }
-            Expr::While { condition, body, label: _ }
-            | Expr::For { iterable: condition, pat: _, body, label: _ } => {
+            Expr::While { condition, body, label: _ } => {
                 self.consume_expr(*condition);
                 self.consume_expr(*body);
             }
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index d7c6691ea0c..33e98ac86cf 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -211,24 +211,6 @@ impl<'a> InferenceContext<'a> {
                 self.diverges = Diverges::Maybe;
                 TyBuilder::unit()
             }
-            &Expr::For { iterable, body, pat, label } => {
-                let iterable_ty = self.infer_expr(iterable, &Expectation::none());
-                let into_iter_ty =
-                    self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
-                let pat_ty = self
-                    .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item());
-
-                self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty);
-
-                self.infer_top_pat(pat, &pat_ty);
-                self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
-                    this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
-                });
-
-                // the body may not run, so it diverging doesn't mean we diverge
-                self.diverges = Diverges::Maybe;
-                TyBuilder::unit()
-            }
             Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
                 assert_eq!(args.len(), arg_types.len());
 
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 7794bd5c39f..44783424390 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -69,8 +69,7 @@ impl<'a> InferenceContext<'a> {
                     self.infer_mut_expr(*tail, Mutability::Not);
                 }
             }
-            &Expr::For { iterable: c, pat: _, body, label: _ }
-            | &Expr::While { condition: c, body, label: _ } => {
+            &Expr::While { condition: c, body, label: _ } => {
                 self.infer_mut_expr(c, Mutability::Not);
                 self.infer_mut_expr(body, Mutability::Not);
             }
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 0c2b179a10d..2480f8babac 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -255,9 +255,9 @@ impl<'a> InferenceContext<'a> {
                 self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
             }
             Pat::Wild => expected.clone(),
-            Pat::Range { start, end } => {
-                let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
-                self.infer_expr(*end, &Expectation::has_type(start_ty))
+            Pat::Range { .. } => {
+                // FIXME: do some checks here.
+                expected.clone()
             }
             &Pat::Lit(expr) => {
                 // Don't emit type mismatches again, the expression lowering already did that.
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 693c0494dbd..35d3407c16c 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -77,14 +77,18 @@ impl<'a> LayoutCalculator for LayoutCx<'a> {
     }
 }
 
-pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
+pub fn layout_of_ty_query(
+    db: &dyn HirDatabase,
+    ty: Ty,
+    krate: CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
     let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
     let cx = LayoutCx { krate, target: &target };
     let dl = &*cx.current_data_layout();
     let trait_env = Arc::new(TraitEnvironment::empty(krate));
     let ty = normalize(db, trait_env, ty.clone());
-    let layout = match ty.kind(Interner) {
-        TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone(), krate)?,
+    let result = match ty.kind(Interner) {
+        TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate),
         TyKind::Scalar(s) => match s {
             chalk_ir::Scalar::Bool => Layout::scalar(
                 dl,
@@ -141,9 +145,9 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
 
             let fields = tys
                 .iter(Interner)
-                .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
+                .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate))
                 .collect::<Result<Vec<_>, _>>()?;
-            let fields = fields.iter().collect::<Vec<_>>();
+            let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
             let fields = fields.iter().collect::<Vec<_>>();
             cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
         }
@@ -151,7 +155,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(
                 "unevaluated or mistyped const generic parameter".to_string(),
             ))? as u64;
-            let element = layout_of_ty(db, element, krate)?;
+            let element = db.layout_of_ty(element.clone(), krate)?;
             let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
 
             let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
@@ -172,7 +176,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             }
         }
         TyKind::Slice(element) => {
-            let element = layout_of_ty(db, element, krate)?;
+            let element = db.layout_of_ty(element.clone(), krate)?;
             Layout {
                 variants: Variants::Single { index: struct_variant_idx() },
                 fields: FieldsShape::Array { stride: element.size, count: 0 },
@@ -206,7 +210,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
                 }
                 _ => {
                     // pointee is sized
-                    return Ok(Layout::scalar(dl, data_ptr));
+                    return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
                 }
             };
 
@@ -248,7 +252,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             match impl_trait_id {
                 crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                     let infer = db.infer(func.into());
-                    layout_of_ty(db, &infer.type_of_rpit[idx], krate)?
+                    return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate);
                 }
                 crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
                     return Err(LayoutError::NotImplemented)
@@ -262,14 +266,13 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
             let fields = captures
                 .iter()
                 .map(|x| {
-                    layout_of_ty(
-                        db,
-                        &x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
+                    db.layout_of_ty(
+                        x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
                         krate,
                     )
                 })
                 .collect::<Result<Vec<_>, _>>()?;
-            let fields = fields.iter().collect::<Vec<_>>();
+            let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
             let fields = fields.iter().collect::<Vec<_>>();
             cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
                 .ok_or(LayoutError::Unknown)?
@@ -284,7 +287,16 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
         | TyKind::BoundVar(_)
         | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
     };
-    Ok(layout)
+    Ok(Arc::new(result))
+}
+
+pub fn layout_of_ty_recover(
+    _: &dyn HirDatabase,
+    _: &[String],
+    _: &Ty,
+    _: &CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
+    user_error!("infinite sized recursive type");
 }
 
 fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index da609a09573..bd2752a7119 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -10,6 +10,7 @@ use hir_def::{
 };
 use la_arena::RawIdx;
 use smallvec::SmallVec;
+use triomphe::Arc;
 
 use crate::{
     db::HirDatabase,
@@ -18,7 +19,7 @@ use crate::{
     Substitution,
 };
 
-use super::{layout_of_ty, LayoutCx};
+use super::LayoutCx;
 
 pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
     RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
@@ -29,14 +30,14 @@ pub fn layout_of_adt_query(
     def: AdtId,
     subst: Substitution,
     krate: CrateId,
-) -> Result<Layout, LayoutError> {
+) -> Result<Arc<Layout>, LayoutError> {
     let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
     let cx = LayoutCx { krate, target: &target };
     let dl = cx.current_data_layout();
     let handle_variant = |def: VariantId, var: &VariantData| {
         var.fields()
             .iter()
-            .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
+            .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate))
             .collect::<Result<Vec<_>, _>>()
     };
     let (variants, repr) = match def {
@@ -67,11 +68,13 @@ pub fn layout_of_adt_query(
             (r, data.repr.unwrap_or_default())
         }
     };
-    let variants =
-        variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
+    let variants = variants
+        .iter()
+        .map(|x| x.iter().map(|x| &**x).collect::<Vec<_>>())
+        .collect::<SmallVec<[_; 1]>>();
     let variants = variants.iter().map(|x| x.iter().collect()).collect();
-    if matches!(def, AdtId::UnionId(..)) {
-        cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
+    let result = if matches!(def, AdtId::UnionId(..)) {
+        cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
     } else {
         cx.layout_of_struct_or_enum(
             &repr,
@@ -103,8 +106,9 @@ pub fn layout_of_adt_query(
                     .and_then(|x| x.last().map(|x| x.is_unsized()))
                     .unwrap_or(true),
         )
-        .ok_or(LayoutError::SizeOverflow)
-    }
+        .ok_or(LayoutError::SizeOverflow)?
+    };
+    Ok(Arc::new(result))
 }
 
 fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
@@ -129,7 +133,7 @@ pub fn layout_of_adt_recover(
     _: &AdtId,
     _: &Substitution,
     _: &CrateId,
-) -> Result<Layout, LayoutError> {
+) -> Result<Arc<Layout>, LayoutError> {
     user_error!("infinite sized recursive type");
 }
 
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 1a90a467157..fca2e09ff0a 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
 use base_db::fixture::WithFixture;
 use chalk_ir::{AdtId, TyKind};
 use hir_def::db::DefDatabase;
+use triomphe::Arc;
 
 use crate::{
     db::HirDatabase,
@@ -11,15 +12,13 @@ use crate::{
     Interner, Substitution,
 };
 
-use super::layout_of_ty;
-
 mod closure;
 
 fn current_machine_data_layout() -> String {
     project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
 }
 
-fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
     let target_data_layout = current_machine_data_layout();
     let ra_fixture = format!(
         "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
@@ -47,11 +46,11 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
         })
         .unwrap();
     let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
-    layout_of_ty(&db, &goal_ty, module_id.krate())
+    db.layout_of_ty(goal_ty, module_id.krate())
 }
 
 /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
-fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
     let target_data_layout = current_machine_data_layout();
     let ra_fixture = format!(
         "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}",
@@ -75,7 +74,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
     let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
     let infer = db.infer(adt_id.into());
     let goal_ty = infer.type_of_binding[b].clone();
-    layout_of_ty(&db, &goal_ty, module_id.krate())
+    db.layout_of_ty(goal_ty, module_id.krate())
 }
 
 #[track_caller]
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 7208bebb3bd..6fa3d1351a9 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -782,7 +782,9 @@ fn find_matching_impl(
                 .into_iter()
                 .map(|b| b.cast(Interner));
             let goal = crate::Goal::all(Interner, wcs);
-            table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
+            table.try_obligation(goal.clone())?;
+            table.register_obligation(goal);
+            Some((impl_data, table.resolve_completely(impl_substs)))
         })
     })
 }
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index f8451f28d73..2345bab0bb4 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -19,12 +19,17 @@ mod eval;
 mod lower;
 mod borrowck;
 mod pretty;
+mod monomorphization;
 
 pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
 pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
 pub use lower::{
     lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
 };
+pub use monomorphization::{
+    monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
+    monomorphized_mir_body_query, monomorphized_mir_body_recover,
+};
 use smallvec::{smallvec, SmallVec};
 use stdx::{impl_from, never};
 
@@ -37,7 +42,7 @@ fn return_slot() -> LocalId {
     LocalId::from_raw(RawIdx::from(0))
 }
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Local {
     pub ty: Ty,
 }
@@ -780,7 +785,6 @@ pub enum CastKind {
     FloatToInt,
     FloatToFloat,
     IntToFloat,
-    PtrToPtr,
     FnPtrToPtr,
 }
 
@@ -952,7 +956,7 @@ pub struct Statement {
     pub span: MirSpan,
 }
 
-#[derive(Debug, Default, PartialEq, Eq)]
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
 pub struct BasicBlock {
     /// List of statements in this block.
     pub statements: Vec<Statement>,
@@ -974,7 +978,7 @@ pub struct BasicBlock {
     pub is_cleanup: bool,
 }
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct MirBody {
     pub basic_blocks: Arena<BasicBlock>,
     pub locals: Arena<Local>,
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index a1f69b1f286..28e7759db39 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -3,20 +3,17 @@
 use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range};
 
 use base_db::{CrateId, FileId};
-use chalk_ir::{
-    fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
-    DebruijnIndex, Mutability, ProjectionTy,
-};
+use chalk_ir::Mutability;
 use either::Either;
 use hir_def::{
     builtin_type::BuiltinType,
     data::adt::{StructFlags, VariantData},
     lang_item::{lang_attr, LangItem},
     layout::{TagEncoding, Variants},
-    AdtId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, HasModule, ItemContainerId,
-    Lookup, StaticId, TypeOrConstParamId, VariantId,
+    AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
+    VariantId,
 };
-use hir_expand::{name::Name, InFile};
+use hir_expand::InFile;
 use intern::Interned;
 use la_arena::ArenaMap;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -27,14 +24,13 @@ use crate::{
     consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
     db::HirDatabase,
     display::{ClosureStyle, HirDisplay},
-    from_placeholder_idx,
-    infer::{normalize, PointerCast},
-    layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx},
+    infer::PointerCast,
+    layout::{Layout, LayoutError, RustcEnumVariantIdx},
     mapping::from_chalk,
-    method_resolution::{is_dyn_method, lookup_impl_const, lookup_impl_method},
+    method_resolution::{is_dyn_method, lookup_impl_method},
     name, static_lifetime,
     traits::FnTrait,
-    utils::{generics, ClosureSubst, Generics},
+    utils::ClosureSubst,
     CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
     Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
 };
@@ -279,7 +275,6 @@ pub enum MirEvalError {
     /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
     /// then use this type of error.
     UndefinedBehavior(String),
-    GenericArgNotProvided(TypeOrConstParamId, Substitution),
     Panic(String),
     MirLowerError(FunctionId, MirLowerError),
     MirLowerErrorForClosure(ClosureId, MirLowerError),
@@ -348,20 +343,6 @@ impl MirEvalError {
                     ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
                 )?;
             }
-            MirEvalError::GenericArgNotProvided(id, subst) => {
-                let parent = id.parent;
-                let param = &db.generic_params(parent).type_or_consts[id.local_id];
-                writeln!(
-                    f,
-                    "Generic arg not provided for {}",
-                    param.name().unwrap_or(&Name::missing()).display(db.upcast())
-                )?;
-                writeln!(f, "Provided args: [")?;
-                for g in subst.iter(Interner) {
-                    write!(f, "    {},", g.display(db).to_string())?;
-                }
-                writeln!(f, "]")?;
-            }
             MirEvalError::MirLowerError(func, err) => {
                 let function_name = db.function_data(*func);
                 writeln!(
@@ -416,7 +397,6 @@ impl std::fmt::Debug for MirEvalError {
             Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
             Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
             Self::StackOverflow => write!(f, "stack overflow"),
-            Self::GenericArgNotProvided(..) => f.debug_tuple("GenericArgNotProvided").finish(),
             Self::MirLowerError(arg0, arg1) => {
                 f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
             }
@@ -471,14 +451,12 @@ impl DropFlags {
 struct Locals<'a> {
     ptr: &'a ArenaMap<LocalId, Interval>,
     body: &'a MirBody,
-    subst: &'a Substitution,
     drop_flags: DropFlags,
 }
 
 pub fn interpret_mir(
     db: &dyn HirDatabase,
     body: &MirBody,
-    subst: Substitution,
     // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
     // they share their body with their parent, so in MIR lowering we have locals of the parent body, which
     // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
@@ -489,17 +467,11 @@ pub fn interpret_mir(
     let ty = body.locals[return_slot()].ty.clone();
     let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
     let x: Result<Const> = (|| {
-        let ty = evaluator.ty_filler(&ty, &subst, body.owner)?;
-        let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?;
+        let bytes = evaluator.interpret_mir(&body, None.into_iter())?;
         let mut memory_map = evaluator.create_memory_map(
             &bytes,
             &ty,
-            &Locals {
-                ptr: &ArenaMap::new(),
-                body: &body,
-                subst: &subst,
-                drop_flags: DropFlags::default(),
-            },
+            &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() },
         )?;
         memory_map.vtable = evaluator.vtable_map.clone();
         return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
@@ -565,8 +537,7 @@ impl Evaluator<'_> {
         locals: &'a Locals<'a>,
     ) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
         let mut addr = locals.ptr[p.local].addr;
-        let mut ty: Ty =
-            self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
+        let mut ty: Ty = locals.body.locals[p.local].ty.clone();
         let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
         for proj in &*p.projection {
             let prev_ty = ty.clone();
@@ -689,17 +660,13 @@ impl Evaluator<'_> {
         Ok((addr, ty, metadata))
     }
 
-    fn layout(&self, ty: &Ty) -> Result<Layout> {
-        layout_of_ty(self.db, ty, self.crate_id)
+    fn layout(&self, ty: &Ty) -> Result<Arc<Layout>> {
+        self.db
+            .layout_of_ty(ty.clone(), self.crate_id)
             .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
     }
 
-    fn layout_filled(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Layout> {
-        let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?;
-        self.layout(ty)
-    }
-
-    fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> {
+    fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Arc<Layout>> {
         self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| {
             MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
         })
@@ -735,7 +702,6 @@ impl Evaluator<'_> {
         &mut self,
         body: &MirBody,
         args: impl Iterator<Item = Vec<u8>>,
-        subst: Substitution,
     ) -> Result<Vec<u8>> {
         if let Some(x) = self.stack_depth_limit.checked_sub(1) {
             self.stack_depth_limit = x;
@@ -743,12 +709,8 @@ impl Evaluator<'_> {
             return Err(MirEvalError::StackOverflow);
         }
         let mut current_block_idx = body.start_block;
-        let mut locals = Locals {
-            ptr: &ArenaMap::new(),
-            body: &body,
-            subst: &subst,
-            drop_flags: DropFlags::default(),
-        };
+        let mut locals =
+            Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
         let (locals_ptr, stack_size) = {
             let mut stack_ptr = self.stack.len();
             let addr = body
@@ -882,7 +844,17 @@ impl Evaluator<'_> {
                 }
                 Owned(r)
             }
-            Rvalue::Len(_) => not_supported!("rvalue len"),
+            Rvalue::Len(p) => {
+                let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+                match metadata {
+                    Some(m) => m,
+                    None => {
+                        return Err(MirEvalError::TypeError(
+                            "type without metadata is used for Rvalue::Len",
+                        ));
+                    }
+                }
+            }
             Rvalue::UnaryOp(op, val) => {
                 let mut c = self.eval_operand(val, locals)?.get(&self)?;
                 let mut ty = self.operand_ty(val, locals)?;
@@ -1080,7 +1052,7 @@ impl Evaluator<'_> {
                     }
                     return Ok(Owned(0u128.to_le_bytes().to_vec()));
                 };
-                match layout.variants {
+                match &layout.variants {
                     Variants::Single { index } => {
                         let r = self.const_eval_discriminant(EnumVariantId {
                             parent: enum_id,
@@ -1102,14 +1074,14 @@ impl Evaluator<'_> {
                             TagEncoding::Niche { untagged_variant, niche_start, .. } => {
                                 let tag = &bytes[offset..offset + size];
                                 let candidate_tag = i128::from_le_bytes(pad16(tag, false))
-                                    .wrapping_sub(niche_start as i128)
+                                    .wrapping_sub(*niche_start as i128)
                                     as usize;
                                 let variant = variants
                                     .iter_enumerated()
                                     .map(|(x, _)| x)
-                                    .filter(|x| *x != untagged_variant)
+                                    .filter(|x| x != untagged_variant)
                                     .nth(candidate_tag)
-                                    .unwrap_or(untagged_variant)
+                                    .unwrap_or(*untagged_variant)
                                     .0;
                                 let result = self.const_eval_discriminant(EnumVariantId {
                                     parent: enum_id,
@@ -1122,7 +1094,7 @@ impl Evaluator<'_> {
                 }
             }
             Rvalue::Repeat(x, len) => {
-                let len = match try_const_usize(self.db, len) {
+                let len = match try_const_usize(self.db, &len) {
                     Some(x) => x as usize,
                     None => not_supported!("non evaluatable array len in repeat Rvalue"),
                 };
@@ -1154,7 +1126,7 @@ impl Evaluator<'_> {
                         Owned(r)
                     }
                     AggregateKind::Tuple(ty) => {
-                        let layout = self.layout_filled(&ty, locals)?;
+                        let layout = self.layout(&ty)?;
                         Owned(self.make_by_layout(
                             layout.size.bytes_usize(),
                             &layout,
@@ -1174,9 +1146,8 @@ impl Evaluator<'_> {
                         Owned(result)
                     }
                     AggregateKind::Adt(x, subst) => {
-                        let subst = self.subst_filler(subst, locals);
                         let (size, variant_layout, tag) =
-                            self.layout_of_variant(*x, subst, locals)?;
+                            self.layout_of_variant(*x, subst.clone(), locals)?;
                         Owned(self.make_by_layout(
                             size,
                             &variant_layout,
@@ -1185,7 +1156,7 @@ impl Evaluator<'_> {
                         )?)
                     }
                     AggregateKind::Closure(ty) => {
-                        let layout = self.layout_filled(&ty, locals)?;
+                        let layout = self.layout(&ty)?;
                         Owned(self.make_by_layout(
                             layout.size.bytes_usize(),
                             &layout,
@@ -1220,7 +1191,10 @@ impl Evaluator<'_> {
                         // This is no-op
                         Borrowed(self.eval_operand(operand, locals)?)
                     }
-                    x => not_supported!("pointer cast {x:?}"),
+                    PointerCast::ArrayToPointer => {
+                        // We should remove the metadata part if the current type is slice
+                        Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size()))
+                    }
                 },
                 CastKind::DynStar => not_supported!("dyn star cast"),
                 CastKind::IntToInt
@@ -1235,12 +1209,6 @@ impl Evaluator<'_> {
                 CastKind::FloatToInt => not_supported!("float to int cast"),
                 CastKind::FloatToFloat => not_supported!("float to float cast"),
                 CastKind::IntToFloat => not_supported!("float to int cast"),
-                CastKind::PtrToPtr => {
-                    let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
-                    let dest_size =
-                        self.size_of_sized(target_ty, locals, "destination of ptr to ptr cast")?;
-                    Owned(current[0..dest_size].to_vec())
-                }
                 CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
             },
         })
@@ -1300,8 +1268,8 @@ impl Evaluator<'_> {
                                 r.extend(len.to_le_bytes().into_iter());
                                 Owned(r)
                             }
-                            _ => {
-                                not_supported!("slice unsizing from non arrays")
+                            t => {
+                                not_supported!("slice unsizing from non array type {t:?}")
                             }
                         },
                     }
@@ -1327,7 +1295,7 @@ impl Evaluator<'_> {
         x: VariantId,
         subst: Substitution,
         locals: &Locals<'_>,
-    ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+    ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
         let adt = x.adt_id();
         if let DefWithBodyId::VariantId(f) = locals.body.owner {
             if let VariantId::EnumVariantId(x) = x {
@@ -1340,7 +1308,7 @@ impl Evaluator<'_> {
             }
         }
         let layout = self.layout_adt(adt, subst)?;
-        Ok(match layout.variants {
+        Ok(match &layout.variants {
             Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
             Variants::Multiple { variants, tag, tag_encoding, .. } => {
                 let cx = self
@@ -1357,22 +1325,22 @@ impl Evaluator<'_> {
                 let have_tag = match tag_encoding {
                     TagEncoding::Direct => true,
                     TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
-                        if untagged_variant == rustc_enum_variant_idx {
+                        if *untagged_variant == rustc_enum_variant_idx {
                             false
                         } else {
                             discriminant = (variants
                                 .iter_enumerated()
-                                .filter(|(x, _)| *x != untagged_variant)
+                                .filter(|(x, _)| x != untagged_variant)
                                 .position(|(x, _)| x == rustc_enum_variant_idx)
                                 .unwrap() as i128)
-                                .wrapping_add(niche_start as i128);
+                                .wrapping_add(*niche_start as i128);
                             true
                         }
                     }
                 };
                 (
                     layout.size.bytes_usize(),
-                    variant_layout,
+                    Arc::new(variant_layout),
                     if have_tag {
                         Some((
                             layout.fields.offset(0).bytes_usize(),
@@ -1419,15 +1387,7 @@ impl Evaluator<'_> {
             Operand::Constant(konst) => {
                 let data = &konst.data(Interner);
                 match &data.value {
-                    chalk_ir::ConstValue::BoundVar(b) => {
-                        let c = locals
-                            .subst
-                            .as_slice(Interner)
-                            .get(b.index)
-                            .ok_or(MirEvalError::TypeError("missing generic arg"))?
-                            .assert_const_ref(Interner);
-                        self.eval_operand(&Operand::Constant(c.clone()), locals)?
-                    }
+                    chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"),
                     chalk_ir::ConstValue::InferenceVar(_) => {
                         not_supported!("inference var constant")
                     }
@@ -1471,29 +1431,8 @@ impl Evaluator<'_> {
                 self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
                 Interval::new(addr, size)
             }
-            ConstScalar::UnevaluatedConst(const_id, subst) => {
-                let mut const_id = *const_id;
-                let mut subst = self.subst_filler(subst, locals);
-                if let GeneralConstId::ConstId(c) = const_id {
-                    let (c, s) = lookup_impl_const(
-                        self.db,
-                        self.db.trait_environment_for_body(locals.body.owner),
-                        c,
-                        subst,
-                    );
-                    const_id = GeneralConstId::ConstId(c);
-                    subst = s;
-                }
-                let c = self.db.const_eval(const_id.into(), subst).map_err(|e| {
-                    let name = const_id.name(self.db.upcast());
-                    MirEvalError::ConstEvalError(name, Box::new(e))
-                })?;
-                if let chalk_ir::ConstValue::Concrete(c) = &c.data(Interner).value {
-                    if let ConstScalar::Bytes(_, _) = &c.interned {
-                        return self.allocate_const_in_heap(&c, ty, locals, konst);
-                    }
-                }
-                not_supported!("failing at evaluating unevaluated const");
+            ConstScalar::UnevaluatedConst(..) => {
+                not_supported!("unevaluated const present in monomorphized mir");
             }
             ConstScalar::Unknown => not_supported!("evaluating unknown const"),
         })
@@ -1555,7 +1494,7 @@ impl Evaluator<'_> {
                 }
             }
         }
-        let layout = self.layout_filled(ty, locals);
+        let layout = self.layout(ty);
         if self.assert_placeholder_ty_is_unused {
             if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
                 return Ok(Some((0, 1)));
@@ -1576,122 +1515,12 @@ impl Evaluator<'_> {
         }
     }
 
-    /// Uses `ty_filler` to fill an entire subst
-    fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
-        Substitution::from_iter(
-            Interner,
-            subst.iter(Interner).map(|x| match x.data(Interner) {
-                chalk_ir::GenericArgData::Ty(ty) => {
-                    let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
-                        return x.clone();
-                    };
-                    chalk_ir::GenericArgData::Ty(ty).intern(Interner)
-                }
-                _ => x.clone(),
-            }),
-        )
-    }
-
-    /// This function substitutes placeholders of the body with the provided subst, effectively plays
-    /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
-    /// position impl traits) with their underlying type.
-    fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
-        struct Filler<'a> {
-            db: &'a dyn HirDatabase,
-            subst: &'a Substitution,
-            generics: Option<Generics>,
-        }
-        impl FallibleTypeFolder<Interner> for Filler<'_> {
-            type Error = MirEvalError;
-
-            fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
-                self
-            }
-
-            fn interner(&self) -> Interner {
-                Interner
-            }
-
-            fn try_fold_ty(
-                &mut self,
-                ty: Ty,
-                outer_binder: DebruijnIndex,
-            ) -> std::result::Result<Ty, Self::Error> {
-                match ty.kind(Interner) {
-                    TyKind::AssociatedType(id, subst) => {
-                        // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
-                        // this kind of associated types.
-                        Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
-                            associated_ty_id: *id,
-                            substitution: subst.clone().try_fold_with(self, outer_binder)?,
-                        }))
-                        .intern(Interner))
-                    }
-                    TyKind::OpaqueType(id, subst) => {
-                        let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
-                        let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
-                        match impl_trait_id {
-                            crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                                let infer = self.db.infer(func.into());
-                                let filler = &mut Filler {
-                                    db: self.db,
-                                    subst: &subst,
-                                    generics: Some(generics(self.db.upcast(), func.into())),
-                                };
-                                filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
-                            }
-                            crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
-                                not_supported!("async block impl trait");
-                            }
-                        }
-                    }
-                    _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
-                }
-            }
-
-            fn try_fold_free_placeholder_ty(
-                &mut self,
-                idx: chalk_ir::PlaceholderIndex,
-                _outer_binder: DebruijnIndex,
-            ) -> std::result::Result<Ty, Self::Error> {
-                let x = from_placeholder_idx(self.db, idx);
-                let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
-                    not_supported!("missing idx in generics");
-                };
-                Ok(self
-                    .subst
-                    .as_slice(Interner)
-                    .get(idx)
-                    .and_then(|x| x.ty(Interner))
-                    .ok_or_else(|| MirEvalError::GenericArgNotProvided(x, self.subst.clone()))?
-                    .clone())
-            }
-        }
-        let g_def = match owner {
-            DefWithBodyId::FunctionId(f) => Some(f.into()),
-            DefWithBodyId::StaticId(_) => None,
-            DefWithBodyId::ConstId(f) => Some(f.into()),
-            DefWithBodyId::VariantId(f) => Some(f.into()),
-        };
-        let generics = g_def.map(|g_def| generics(self.db.upcast(), g_def));
-        let filler = &mut Filler { db: self.db, subst, generics };
-        Ok(normalize(
-            self.db,
-            self.trait_env.clone(),
-            ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?,
-        ))
-    }
-
     fn heap_allocate(&mut self, size: usize, _align: usize) -> Address {
         let pos = self.heap.len();
         self.heap.extend(iter::repeat(0).take(size));
         Address::Heap(pos)
     }
 
-    pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
-        self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
-    }
-
     fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
         use LangItem::*;
         let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
@@ -1849,21 +1678,24 @@ impl Evaluator<'_> {
     ) -> Result<()> {
         let mir_body = self
             .db
-            .mir_body_for_closure(closure)
+            .monomorphized_mir_body_for_closure(
+                closure,
+                generic_args.clone(),
+                self.trait_env.clone(),
+            )
             .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
-        let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned()))
+        let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some()
+        {
+            closure_data.addr.to_bytes()
+        } else {
+            closure_data.get(self)?.to_owned()
+        };
+        let arg_bytes = iter::once(Ok(closure_data))
             .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
             .collect::<Result<Vec<_>>>()?;
-        let bytes = self
-            .interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone())
-            .map_err(|e| {
-                MirEvalError::InFunction(
-                    Either::Right(closure),
-                    Box::new(e),
-                    span,
-                    locals.body.owner,
-                )
-            })?;
+        let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| {
+            MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner)
+        })?;
         destination.write_from_bytes(self, &bytes)
     }
 
@@ -1877,7 +1709,7 @@ impl Evaluator<'_> {
         span: MirSpan,
     ) -> Result<()> {
         let def: CallableDefId = from_chalk(self.db, def);
-        let generic_args = self.subst_filler(generic_args, &locals);
+        let generic_args = generic_args.clone();
         match def {
             CallableDefId::FunctionId(def) => {
                 if let Some(_) = self.detect_fn_trait(def) {
@@ -1982,14 +1814,14 @@ impl Evaluator<'_> {
         span: MirSpan,
         destination: Interval,
     ) -> Result<()> {
-        let generic_args = self.subst_filler(&generic_args, &locals);
         let def = imp.into();
-        let mir_body = self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?;
-        let result = self
-            .interpret_mir(&mir_body, arg_bytes.iter().cloned(), generic_args)
-            .map_err(|e| {
-                MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
-            })?;
+        let mir_body = self
+            .db
+            .monomorphized_mir_body(def, generic_args, self.trait_env.clone())
+            .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
+        let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| {
+            MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
+        })?;
         destination.write_from_bytes(self, &result)?;
         Ok(())
     }
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 9ff58b27bb4..e05004eeb6a 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -1,6 +1,8 @@
 //! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
 //! is not available.
 
+use std::cmp;
+
 use super::*;
 
 macro_rules! from_bytes {
@@ -254,6 +256,7 @@ impl Evaluator<'_> {
                     }
                     _ => not_supported!("write to arbitrary file descriptor"),
                 }
+                destination.write_from_interval(self, len.interval)?;
                 Ok(())
             }
             "pthread_key_create" => {
@@ -437,7 +440,7 @@ impl Evaluator<'_> {
                 let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
                     return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
                 };
-                let align = self.layout_filled(ty, locals)?.align.abi.bytes();
+                let align = self.layout(ty)?.align.abi.bytes();
                 destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
             }
             "needs_drop" => {
@@ -456,6 +459,22 @@ impl Evaluator<'_> {
                 let ans = lhs.get(self)? == rhs.get(self)?;
                 destination.write_from_bytes(self, &[u8::from(ans)])
             }
+            "saturating_add" => {
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("saturating_add args are not provided"));
+                };
+                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+                let ans = lhs.saturating_add(rhs);
+                let bits = destination.size * 8;
+                // FIXME: signed
+                let is_signed = false;
+                let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 };
+                // FIXME: signed
+                let mn: u128 = 0;
+                let ans = cmp::min(mx, cmp::max(mn, ans));
+                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+            }
             "wrapping_add" | "unchecked_add" => {
                 let [lhs, rhs] = args else {
                     return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
@@ -474,6 +493,15 @@ impl Evaluator<'_> {
                 let ans = lhs.wrapping_sub(rhs);
                 destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
             }
+            "wrapping_mul" | "unchecked_mul" => {
+                let [lhs, rhs] = args else {
+                    return Err(MirEvalError::TypeError("wrapping_mul args are not provided"));
+                };
+                let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+                let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+                let ans = lhs.wrapping_mul(rhs);
+                destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+            }
             "unchecked_rem" => {
                 // FIXME: signed
                 let [lhs, rhs] = args else {
@@ -498,7 +526,7 @@ impl Evaluator<'_> {
                 })?;
                 destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
             }
-            "add_with_overflow" => {
+            "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
                 let [lhs, rhs] = args else {
                     return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
                 };
@@ -511,8 +539,14 @@ impl Evaluator<'_> {
                     self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?;
                 let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
                 let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
-                let ans = lhs.wrapping_add(rhs);
-                let is_overflow = false;
+                let (ans, u128overflow) = match as_str {
+                    "add_with_overflow" => lhs.overflowing_add(rhs),
+                    "sub_with_overflow" => lhs.overflowing_sub(rhs),
+                    "mul_with_overflow" => lhs.overflowing_mul(rhs),
+                    _ => unreachable!(),
+                };
+                let is_overflow = u128overflow
+                    || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255);
                 let is_overflow = vec![u8::from(is_overflow)];
                 let layout = self.layout(&result_ty)?;
                 let result = self.make_by_layout(
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 6a9d3c5b4f3..453c93de8e7 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -21,10 +21,15 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
             }
             _ => None,
         })
-        .unwrap();
-    let body =
-        db.mir_body(func_id.into()).map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
-    let (result, stdout, stderr) = interpret_mir(db, &body, Substitution::empty(Interner), false);
+        .expect("no main function found");
+    let body = db
+        .monomorphized_mir_body(
+            func_id.into(),
+            Substitution::empty(Interner),
+            db.trait_environment(func_id.into()),
+        )
+        .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
+    let (result, stdout, stderr) = interpret_mir(db, &body, false);
     result?;
     Ok((stdout, stderr))
 }
@@ -34,7 +39,8 @@ fn check_pass(ra_fixture: &str) {
 }
 
 fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) {
-    let (db, file_id) = TestDB::with_single_file(ra_fixture);
+    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+    let file_id = *file_ids.last().unwrap();
     let x = eval_main(&db, file_id);
     match x {
         Err(e) => {
@@ -271,6 +277,243 @@ fn main() {
 }
 
 #[test]
+fn from_fn() {
+    check_pass(
+        r#"
+//- minicore: fn, iterator
+struct FromFn<F>(F);
+
+impl<T, F: FnMut() -> Option<T>> Iterator for FromFn<F> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.0)()
+    }
+}
+
+fn main() {
+    let mut tokenize = {
+        FromFn(move || Some(2))
+    };
+    let s = tokenize.next();
+}
+        "#,
+    );
+}
+
+#[test]
+fn for_loop() {
+    check_pass(
+        r#"
+//- minicore: iterator, add
+fn should_not_reach() {
+    _ // FIXME: replace this function with panic when that works
+}
+
+struct X;
+struct XIter(i32);
+
+impl IntoIterator for X {
+    type Item = i32;
+
+    type IntoIter = XIter;
+
+    fn into_iter(self) -> Self::IntoIter {
+        XIter(0)
+    }
+}
+
+impl Iterator for XIter {
+    type Item = i32;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.0 == 5 {
+            None
+        } else {
+            self.0 += 1;
+            Some(self.0)
+        }
+    }
+}
+
+fn main() {
+    let mut s = 0;
+    for x in X {
+        s += x;
+    }
+    if s != 15 {
+        should_not_reach();
+    }
+}
+        "#,
+    );
+}
+
+#[test]
+fn field_with_associated_type() {
+    check_pass(
+        r#"
+//- /b/mod.rs crate:b
+pub trait Tr {
+    fn f(self);
+}
+
+pub trait Tr2 {
+    type Ty: Tr;
+}
+
+pub struct S<T: Tr2> {
+    pub t: T::Ty,
+}
+
+impl<T: Tr2> S<T> {
+    pub fn g(&self) {
+        let k = (self.t, self.t);
+        self.t.f();
+    }
+}
+
+//- /a/mod.rs crate:a deps:b
+use b::{Tr, Tr2, S};
+
+struct A(i32);
+struct B(u8);
+
+impl Tr for A {
+    fn f(&self) {
+    }
+}
+
+impl Tr2 for B {
+    type Ty = A;
+}
+
+#[test]
+fn main() {
+    let s: S<B> = S { t: A(2) };
+    s.g();
+}
+    "#,
+    );
+}
+
+#[test]
+fn specialization_array_clone() {
+    check_pass(
+        r#"
+//- minicore: copy, derive, slice, index, coerce_unsized
+impl<T: Clone, const N: usize> Clone for [T; N] {
+    #[inline]
+    fn clone(&self) -> Self {
+        SpecArrayClone::clone(self)
+    }
+}
+
+trait SpecArrayClone: Clone {
+    fn clone<const N: usize>(array: &[Self; N]) -> [Self; N];
+}
+
+impl<T: Clone> SpecArrayClone for T {
+    #[inline]
+    default fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+        // FIXME: panic here when we actually implement specialization.
+        from_slice(array)
+    }
+}
+
+fn from_slice<T, const N: usize>(s: &[T]) -> [T; N] {
+    [s[0]; N]
+}
+
+impl<T: Copy> SpecArrayClone for T {
+    #[inline]
+    fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+        *array
+    }
+}
+
+#[derive(Clone, Copy)]
+struct X(i32);
+
+fn main() {
+    let ar = [X(1), X(2)];
+    ar.clone();
+}
+        "#,
+    );
+}
+
+#[test]
+fn short_circuit_operator() {
+    check_pass(
+        r#"
+fn should_not_reach() -> bool {
+    _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+    if false && should_not_reach() {
+        should_not_reach();
+    }
+    true || should_not_reach();
+
+}
+    "#,
+    );
+}
+
+#[test]
+fn closure_state() {
+    check_pass(
+        r#"
+//- minicore: fn, add, copy
+fn should_not_reach() {
+    _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+    let mut x = 2;
+    let mut c = move || {
+        x += 1;
+        x
+    };
+    c();
+    c();
+    c();
+    if x != 2 {
+        should_not_reach();
+    }
+    if c() != 6 {
+        should_not_reach();
+    }
+}
+        "#,
+    );
+}
+
+#[test]
+fn closure_capture_array_const_generic() {
+    check_pass(
+        r#"
+//- minicore: fn, add, copy
+struct X(i32);
+
+fn f<const N: usize>(mut x: [X; N]) { // -> impl FnOnce() {
+    let c = || {
+        x;
+    };
+    c();
+}
+
+fn main() {
+    let s = f([X(1)]);
+    //s();
+}
+        "#,
+    );
+}
+
+#[test]
 fn posix_tls() {
     check_pass(
         r#"
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index b77e2c1c801..6fe157f45cf 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -8,14 +8,14 @@ use hir_def::{
     body::Body,
     data::adt::{StructKind, VariantData},
     hir::{
-        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
-        Pat, PatId, RecordFieldPat, RecordLitField,
+        ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
+        LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
     },
     lang_item::{LangItem, LangItemTarget},
     path::Path,
-    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
     AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
-    TraitId,
+    TraitId, TypeOrConstParamId,
 };
 use hir_expand::name::Name;
 use la_arena::ArenaMap;
@@ -29,9 +29,10 @@ use crate::{
     display::HirDisplay,
     infer::{CaptureKind, CapturedItem, TypeMismatch},
     inhabitedness::is_ty_uninhabited_from,
-    layout::{layout_of_ty, LayoutError},
+    layout::LayoutError,
     mapping::ToChalk,
     static_lifetime,
+    traits::FnTrait,
     utils::{generics, ClosureSubst},
     Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
 };
@@ -41,8 +42,6 @@ use super::*;
 mod as_place;
 mod pattern_matching;
 
-use pattern_matching::AdtPatternShape;
-
 #[derive(Debug, Clone)]
 struct LoopBlocks {
     begin: BasicBlockId,
@@ -74,6 +73,7 @@ pub enum MirLowerError {
     ConstEvalError(String, Box<ConstEvalError>),
     LayoutError(LayoutError),
     IncompleteExpr,
+    IncompletePattern,
     /// Trying to lower a trait function, instead of an implementation
     TraitFunctionDefinition(TraitId, Name),
     UnresolvedName(String),
@@ -96,6 +96,9 @@ pub enum MirLowerError {
     UnresolvedLabel,
     UnresolvedUpvar(Place),
     UnaccessableLocal,
+
+    // monomorphization errors:
+    GenericArgNotProvided(TypeOrConstParamId, Substitution),
 }
 
 impl MirLowerError {
@@ -129,9 +132,24 @@ impl MirLowerError {
                     e.actual.display(db),
                 )?;
             }
+            MirLowerError::GenericArgNotProvided(id, subst) => {
+                let parent = id.parent;
+                let param = &db.generic_params(parent).type_or_consts[id.local_id];
+                writeln!(
+                    f,
+                    "Generic arg not provided for {}",
+                    param.name().unwrap_or(&Name::missing()).display(db.upcast())
+                )?;
+                writeln!(f, "Provided args: [")?;
+                for g in subst.iter(Interner) {
+                    write!(f, "    {},", g.display(db).to_string())?;
+                }
+                writeln!(f, "]")?;
+            }
             MirLowerError::LayoutError(_)
             | MirLowerError::UnsizedTemporary(_)
             | MirLowerError::IncompleteExpr
+            | MirLowerError::IncompletePattern
             | MirLowerError::UnaccessableLocal
             | MirLowerError::TraitFunctionDefinition(_, _)
             | MirLowerError::UnresolvedName(_)
@@ -528,61 +546,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
                     Ok(())
                 })
             }
-            &Expr::For { iterable, pat, body, label } => {
-                let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
-                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
-                let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
-                    .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
-                let option_some = self.resolve_lang_item(LangItem::OptionSome)?
-                    .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
-                let option = option_some.parent;
-                let into_iter_fn_op = Operand::const_zst(
-                    TyKind::FnDef(
-                        self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
-                        Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
-                    ).intern(Interner));
-                let iter_next_fn_op = Operand::const_zst(
-                    TyKind::FnDef(
-                        self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
-                        Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
-                    ).intern(Interner));
-                let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
-                    return Err(MirLowerError::TypeError("unknown for loop iterator type"));
-                };
-                let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
-                let item_ty = &self.infer.type_of_pat[pat];
-                let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
-                let iterator_place: Place = self.temp(iterator_ty.clone(), current, expr_id.into())?.into();
-                let option_item_place: Place = self.temp(option_item_ty.clone(), current, expr_id.into())?.into();
-                let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty, current, expr_id.into())?.into();
-                let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())?
-                else {
-                    return Ok(None);
-                };
-                self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
-                self.lower_loop(current, place, label, expr_id.into(), |this, begin| {
-                    let Some(current) = this.lower_call(iter_next_fn_op, Box::new([Operand::Copy(ref_mut_iterator_place)]), option_item_place.clone(), begin, false, expr_id.into())?
-                    else {
-                        return Ok(());
-                    };
-                    let end = this.current_loop_end()?;
-                    let (current, _) = this.pattern_matching_variant(
-                        option_item_ty.clone(),
-                        BindingAnnotation::Unannotated,
-                        option_item_place.into(),
-                        option_some.into(),
-                        current,
-                        pat.into(),
-                        Some(end),
-                        AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
-                    )?;
-                    if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
-                        let block = this.pop_drop_scope(block);
-                        this.set_goto(block, begin, expr_id.into());
-                    }
-                    Ok(())
-                })
-            },
             Expr::Call { callee, args, .. } => {
                 if let Some((func_id, generic_args)) =
                     self.infer.method_resolution(expr_id) {
@@ -918,6 +881,27 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
                     return Ok(None);
                 };
+                if let hir_def::hir::BinaryOp::LogicOp(op) = op {
+                    let value_to_short = match op {
+                        syntax::ast::LogicOp::And => 0,
+                        syntax::ast::LogicOp::Or => 1,
+                    };
+                    let start_of_then = self.new_basic_block();
+                    self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into());
+                    let end_of_then = Some(start_of_then);
+                    let start_of_else = self.new_basic_block();
+                    let end_of_else =
+                        self.lower_expr_to_place(*rhs, place, start_of_else)?;
+                    self.set_terminator(
+                        current,
+                        TerminatorKind::SwitchInt {
+                            discr: lhs_op,
+                            targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else),
+                        },
+                        expr_id.into(),
+                    );
+                    return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
+                }
                 let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
                     return Ok(None);
                 };
@@ -1135,8 +1119,39 @@ impl<'ctx> MirLowerCtx<'ctx> {
         Ok(())
     }
 
+    fn lower_literal_or_const_to_operand(
+        &mut self,
+        ty: Ty,
+        loc: &LiteralOrConst,
+    ) -> Result<Operand> {
+        match loc {
+            LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
+            LiteralOrConst::Const(c) => {
+                let unresolved_name = || MirLowerError::unresolved_path(self.db, c);
+                let resolver = self.owner.resolver(self.db.upcast());
+                let pr = resolver
+                    .resolve_path_in_value_ns(self.db.upcast(), c)
+                    .ok_or_else(unresolved_name)?;
+                match pr {
+                    ResolveValueResult::ValueNs(v) => {
+                        if let ValueNs::ConstId(c) = v {
+                            self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
+                        } else {
+                            not_supported!("bad path in range pattern");
+                        }
+                    }
+                    ResolveValueResult::Partial(_, _) => {
+                        not_supported!("associated constants in range pattern")
+                    }
+                }
+            }
+        }
+    }
+
     fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
-        let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+        let size = self
+            .db
+            .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())?
             .size
             .bytes_usize();
         let bytes = match l {
@@ -1196,6 +1211,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
         span: MirSpan,
         ty: Ty,
     ) -> Result<()> {
+        let c = self.lower_const_to_operand(subst, const_id, ty)?;
+        self.push_assignment(prev_block, place, c.into(), span);
+        Ok(())
+    }
+
+    fn lower_const_to_operand(
+        &mut self,
+        subst: Substitution,
+        const_id: GeneralConstId,
+        ty: Ty,
+    ) -> Result<Operand> {
         let c = if subst.len(Interner) != 0 {
             // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
             intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
@@ -1205,18 +1231,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 .const_eval(const_id.into(), subst)
                 .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
         };
-        self.write_const_to_place(c, prev_block, place, span)
-    }
-
-    fn write_const_to_place(
-        &mut self,
-        c: Const,
-        prev_block: BasicBlockId,
-        place: Place,
-        span: MirSpan,
-    ) -> Result<()> {
-        self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
-        Ok(())
+        Ok(Operand::Constant(c))
     }
 
     fn write_bytes_to_place(
@@ -1673,8 +1688,23 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
         },
         (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
         (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
-        (TyKind::Raw(..) | TyKind::Ref(..), TyKind::Raw(..) | TyKind::Ref(..)) => {
-            CastKind::PtrToPtr
+        (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
+            CastKind::Pointer(if a == b {
+                PointerCast::MutToConstPointer
+            } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+                && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+            {
+                // slice to slice cast is no-op (metadata is not touched), so we use this
+                PointerCast::MutToConstPointer
+            } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+                PointerCast::Unsize
+            } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
+                PointerCast::ArrayToPointer
+            } else {
+                // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
+                // for it in `PointerCast` so we use `MutToConstPointer`
+                PointerCast::MutToConstPointer
+            })
         }
         // Enum to int casts
         (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
@@ -1697,11 +1727,19 @@ pub fn mir_body_for_closure_query(
     let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
         implementation_error!("closure expression is not closure");
     };
-    let (captures, _) = infer.closure_info(&closure);
+    let (captures, kind) = infer.closure_info(&closure);
     let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
     // 0 is return local
     ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
-    let closure_local = ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
+    let closure_local = ctx.result.locals.alloc(Local {
+        ty: match kind {
+            FnTrait::FnOnce => infer[expr].clone(),
+            FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone())
+                .intern(Interner),
+            FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone())
+                .intern(Interner),
+        },
+    });
     ctx.result.param_locals.push(closure_local);
     let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
         implementation_error!("closure has not callable sig");
@@ -1721,6 +1759,10 @@ pub fn mir_body_for_closure_query(
     }
     let mut err = None;
     let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
+    let closure_projection = match kind {
+        FnTrait::FnOnce => vec![],
+        FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
+    };
     ctx.result.walk_places(|p| {
         if let Some(x) = upvar_map.get(&p.local) {
             let r = x.iter().find(|x| {
@@ -1743,7 +1785,8 @@ pub fn mir_body_for_closure_query(
             match r {
                 Some(x) => {
                     p.local = closure_local;
-                    let mut next_projs = vec![PlaceElem::TupleOrClosureField(x.1)];
+                    let mut next_projs = closure_projection.clone();
+                    next_projs.push(PlaceElem::TupleOrClosureField(x.1));
                     let prev_projs = mem::take(&mut p.projection);
                     if x.0.kind != CaptureKind::ByValue {
                         next_projs.push(ProjectionElem::Deref);
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 00864907ac8..ee2a0306d5e 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,6 +1,6 @@
 //! MIR lowering for patterns
 
-use hir_def::resolver::HasResolver;
+use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
 
 use crate::utils::pattern_matching_dereference_count;
 
@@ -38,7 +38,7 @@ impl MirLowerCtx<'_> {
         mut binding_mode: BindingAnnotation,
     ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
         Ok(match &self.body.pats[pattern] {
-            Pat::Missing => return Err(MirLowerError::IncompleteExpr),
+            Pat::Missing => return Err(MirLowerError::IncompletePattern),
             Pat::Wild => (current, current_else),
             Pat::Tuple { args, ellipsis } => {
                 pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
@@ -106,9 +106,92 @@ impl MirLowerCtx<'_> {
                     AdtPatternShape::Record { args: &*args },
                 )?
             }
-            Pat::Range { .. } => not_supported!("range pattern"),
+            Pat::Range { start, end } => {
+                let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> {
+                    let lv = self.lower_literal_or_const_to_operand(cond_ty.clone(), l)?;
+                    let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
+                    let next = self.new_basic_block();
+                    let discr: Place =
+                        self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+                    self.push_assignment(
+                        current,
+                        discr.clone(),
+                        Rvalue::CheckedBinaryOp(binop, lv, Operand::Copy(cond_place.clone())),
+                        pattern.into(),
+                    );
+                    let discr = Operand::Copy(discr);
+                    self.set_terminator(
+                        current,
+                        TerminatorKind::SwitchInt {
+                            discr,
+                            targets: SwitchTargets::static_if(1, next, else_target),
+                        },
+                        pattern.into(),
+                    );
+                    current = next;
+                    Ok(())
+                };
+                if let Some(start) = start {
+                    add_check(start, BinOp::Le)?;
+                }
+                if let Some(end) = end {
+                    add_check(end, BinOp::Ge)?;
+                }
+                (current, current_else)
+            }
             Pat::Slice { prefix, slice, suffix } => {
                 pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
+                if let TyKind::Slice(_) = cond_ty.kind(Interner) {
+                    let pattern_len = prefix.len() + suffix.len();
+                    let place_len: Place =
+                        self.temp(TyBuilder::usize(), current, pattern.into())?.into();
+                    self.push_assignment(
+                        current,
+                        place_len.clone(),
+                        Rvalue::Len(cond_place.clone()),
+                        pattern.into(),
+                    );
+                    let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
+                    let next = self.new_basic_block();
+                    if slice.is_none() {
+                        self.set_terminator(
+                            current,
+                            TerminatorKind::SwitchInt {
+                                discr: Operand::Copy(place_len),
+                                targets: SwitchTargets::static_if(
+                                    pattern_len as u128,
+                                    next,
+                                    else_target,
+                                ),
+                            },
+                            pattern.into(),
+                        );
+                    } else {
+                        let c = Operand::from_concrete_const(
+                            pattern_len.to_le_bytes().to_vec(),
+                            MemoryMap::default(),
+                            TyBuilder::usize(),
+                        );
+                        let discr: Place =
+                            self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+                        self.push_assignment(
+                            current,
+                            discr.clone(),
+                            Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)),
+                            pattern.into(),
+                        );
+                        let discr = Operand::Copy(discr);
+                        self.set_terminator(
+                            current,
+                            TerminatorKind::SwitchInt {
+                                discr,
+                                targets: SwitchTargets::static_if(1, next, else_target),
+                            },
+                            pattern.into(),
+                        );
+                    }
+                    current = next;
+                }
                 for (i, &pat) in prefix.iter().enumerate() {
                     let next_place = cond_place.project(ProjectionElem::ConstantIndex {
                         offset: i as u64,
@@ -174,53 +257,44 @@ impl MirLowerCtx<'_> {
                     let pr = resolver
                         .resolve_path_in_value_ns(self.db.upcast(), p)
                         .ok_or_else(unresolved_name)?;
-                    match pr {
-                        ResolveValueResult::ValueNs(v) => match v {
-                            ValueNs::ConstId(c) => {
-                                let tmp: Place =
-                                    self.temp(cond_ty.clone(), current, pattern.into())?.into();
-                                let span = pattern.into();
-                                self.lower_const(
-                                    c.into(),
-                                    current,
-                                    tmp.clone(),
-                                    Substitution::empty(Interner),
-                                    span,
-                                    cond_ty.clone(),
-                                )?;
-                                let tmp2: Place =
-                                    self.temp(TyBuilder::bool(), current, pattern.into())?.into();
-                                self.push_assignment(
-                                    current,
-                                    tmp2.clone(),
-                                    Rvalue::CheckedBinaryOp(
-                                        BinOp::Eq,
-                                        Operand::Copy(tmp),
-                                        Operand::Copy(cond_place),
-                                    ),
-                                    span,
-                                );
-                                let next = self.new_basic_block();
-                                let else_target =
-                                    current_else.unwrap_or_else(|| self.new_basic_block());
-                                self.set_terminator(
-                                    current,
-                                    TerminatorKind::SwitchInt {
-                                        discr: Operand::Copy(tmp2),
-                                        targets: SwitchTargets::static_if(1, next, else_target),
-                                    },
-                                    span,
-                                );
-                                (next, Some(else_target))
+                    let (c, subst) = 'b: {
+                        if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
+                            if let AssocItemId::ConstId(c) = x.0 {
+                                break 'b (c, x.1);
                             }
-                            _ => not_supported!(
-                                "path in pattern position that is not const or variant"
-                            ),
-                        },
-                        ResolveValueResult::Partial(_, _) => {
-                            not_supported!("assoc const in patterns")
                         }
-                    }
+                        if let ResolveValueResult::ValueNs(v) = pr {
+                            if let ValueNs::ConstId(c) = v {
+                                break 'b (c, Substitution::empty(Interner));
+                            }
+                        }
+                        not_supported!("path in pattern position that is not const or variant")
+                    };
+                    let tmp: Place = self.temp(cond_ty.clone(), current, pattern.into())?.into();
+                    let span = pattern.into();
+                    self.lower_const(c.into(), current, tmp.clone(), subst, span, cond_ty.clone())?;
+                    let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+                    self.push_assignment(
+                        current,
+                        tmp2.clone(),
+                        Rvalue::CheckedBinaryOp(
+                            BinOp::Eq,
+                            Operand::Copy(tmp),
+                            Operand::Copy(cond_place),
+                        ),
+                        span,
+                    );
+                    let next = self.new_basic_block();
+                    let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+                    self.set_terminator(
+                        current,
+                        TerminatorKind::SwitchInt {
+                            discr: Operand::Copy(tmp2),
+                            targets: SwitchTargets::static_if(1, next, else_target),
+                        },
+                        span,
+                    );
+                    (next, Some(else_target))
                 }
             },
             Pat::Lit(l) => match &self.body.exprs[*l] {
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
new file mode 100644
index 00000000000..b17ac583656
--- /dev/null
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -0,0 +1,369 @@
+//! Monomorphization of mir, which is used in mir interpreter and const eval.
+//!
+//! The job of monomorphization is:
+//! * Monomorphization. That is, replacing `Option<T>` with `Option<i32>` where `T:=i32` substitution
+//!   is provided
+//! * Normalizing types, for example replacing RPIT of other functions called in this body.
+//!
+//! So the monomorphization should be called even if the substitution is empty.
+
+use std::mem;
+
+use chalk_ir::{
+    fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+    ConstData, DebruijnIndex,
+};
+use hir_def::{DefWithBodyId, GeneralConstId};
+use triomphe::Arc;
+
+use crate::{
+    consteval::unknown_const,
+    db::HirDatabase,
+    from_placeholder_idx,
+    infer::normalize,
+    method_resolution::lookup_impl_const,
+    utils::{generics, Generics},
+    ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
+};
+
+use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
+
+macro_rules! not_supported {
+    ($x: expr) => {
+        return Err(MirLowerError::NotSupported(format!($x)))
+    };
+}
+
+struct Filler<'a> {
+    db: &'a dyn HirDatabase,
+    trait_env: Arc<TraitEnvironment>,
+    subst: &'a Substitution,
+    generics: Option<Generics>,
+    owner: DefWithBodyId,
+}
+impl FallibleTypeFolder<Interner> for Filler<'_> {
+    type Error = MirLowerError;
+
+    fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+        self
+    }
+
+    fn interner(&self) -> Interner {
+        Interner
+    }
+
+    fn try_fold_ty(
+        &mut self,
+        ty: Ty,
+        outer_binder: DebruijnIndex,
+    ) -> std::result::Result<Ty, Self::Error> {
+        match ty.kind(Interner) {
+            TyKind::AssociatedType(id, subst) => {
+                // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
+                // this kind of associated types.
+                Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
+                    associated_ty_id: *id,
+                    substitution: subst.clone().try_fold_with(self, outer_binder)?,
+                }))
+                .intern(Interner))
+            }
+            TyKind::OpaqueType(id, subst) => {
+                let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+                let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
+                match impl_trait_id {
+                    crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+                        let infer = self.db.infer(func.into());
+                        let filler = &mut Filler {
+                            db: self.db,
+                            owner: self.owner,
+                            trait_env: self.trait_env.clone(),
+                            subst: &subst,
+                            generics: Some(generics(self.db.upcast(), func.into())),
+                        };
+                        filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+                    }
+                    crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+                        not_supported!("async block impl trait");
+                    }
+                }
+            }
+            _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+        }
+    }
+
+    fn try_fold_free_placeholder_const(
+        &mut self,
+        _ty: chalk_ir::Ty<Interner>,
+        idx: chalk_ir::PlaceholderIndex,
+        _outer_binder: DebruijnIndex,
+    ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
+        let x = from_placeholder_idx(self.db, idx);
+        let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+            not_supported!("missing idx in generics");
+        };
+        Ok(self
+            .subst
+            .as_slice(Interner)
+            .get(idx)
+            .and_then(|x| x.constant(Interner))
+            .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+            .clone())
+    }
+
+    fn try_fold_free_placeholder_ty(
+        &mut self,
+        idx: chalk_ir::PlaceholderIndex,
+        _outer_binder: DebruijnIndex,
+    ) -> std::result::Result<Ty, Self::Error> {
+        let x = from_placeholder_idx(self.db, idx);
+        let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+            not_supported!("missing idx in generics");
+        };
+        Ok(self
+            .subst
+            .as_slice(Interner)
+            .get(idx)
+            .and_then(|x| x.ty(Interner))
+            .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+            .clone())
+    }
+
+    fn try_fold_const(
+        &mut self,
+        constant: chalk_ir::Const<Interner>,
+        outer_binder: DebruijnIndex,
+    ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+        let next_ty = normalize(
+            self.db,
+            self.trait_env.clone(),
+            constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?,
+        );
+        ConstData { ty: next_ty, value: constant.data(Interner).value.clone() }
+            .intern(Interner)
+            .try_super_fold_with(self, outer_binder)
+    }
+}
+
+impl Filler<'_> {
+    fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> {
+        let tmp = mem::replace(ty, TyKind::Error.intern(Interner));
+        *ty = normalize(
+            self.db,
+            self.trait_env.clone(),
+            tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?,
+        );
+        Ok(())
+    }
+
+    fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> {
+        let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone()));
+        *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+        Ok(())
+    }
+
+    fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> {
+        let tmp = mem::replace(ty, Substitution::empty(Interner));
+        *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+        Ok(())
+    }
+
+    fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
+        match op {
+            Operand::Constant(c) => {
+                match &c.data(Interner).value {
+                    chalk_ir::ConstValue::BoundVar(b) => {
+                        let resolved = self
+                            .subst
+                            .as_slice(Interner)
+                            .get(b.index)
+                            .ok_or_else(|| {
+                                MirLowerError::GenericArgNotProvided(
+                                    self.generics
+                                        .as_ref()
+                                        .and_then(|x| x.iter().nth(b.index))
+                                        .unwrap()
+                                        .0,
+                                    self.subst.clone(),
+                                )
+                            })?
+                            .assert_const_ref(Interner);
+                        *c = resolved.clone();
+                    }
+                    chalk_ir::ConstValue::InferenceVar(_)
+                    | chalk_ir::ConstValue::Placeholder(_) => {}
+                    chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+                        crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
+                            let mut const_id = *const_id;
+                            let mut subst = subst.clone();
+                            self.fill_subst(&mut subst)?;
+                            if let GeneralConstId::ConstId(c) = const_id {
+                                let (c, s) = lookup_impl_const(
+                                    self.db,
+                                    self.db.trait_environment_for_body(self.owner),
+                                    c,
+                                    subst,
+                                );
+                                const_id = GeneralConstId::ConstId(c);
+                                subst = s;
+                            }
+                            let result =
+                                self.db.const_eval(const_id.into(), subst).map_err(|e| {
+                                    let name = const_id.name(self.db.upcast());
+                                    MirLowerError::ConstEvalError(name, Box::new(e))
+                                })?;
+                            *c = result;
+                        }
+                        crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
+                    },
+                }
+                self.fill_const(c)?;
+            }
+            Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (),
+        }
+        Ok(())
+    }
+
+    fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> {
+        for (_, l) in body.locals.iter_mut() {
+            self.fill_ty(&mut l.ty)?;
+        }
+        for (_, bb) in body.basic_blocks.iter_mut() {
+            for statement in &mut bb.statements {
+                match &mut statement.kind {
+                    StatementKind::Assign(_, r) => match r {
+                        Rvalue::Aggregate(ak, ops) => {
+                            for op in &mut **ops {
+                                self.fill_operand(op)?;
+                            }
+                            match ak {
+                                super::AggregateKind::Array(ty)
+                                | super::AggregateKind::Tuple(ty)
+                                | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?,
+                                super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?,
+                                super::AggregateKind::Union(_, _) => (),
+                            }
+                        }
+                        Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => {
+                            self.fill_ty(ty)?;
+                        }
+                        Rvalue::Use(op) => {
+                            self.fill_operand(op)?;
+                        }
+                        Rvalue::Repeat(op, len) => {
+                            self.fill_operand(op)?;
+                            self.fill_const(len)?;
+                        }
+                        Rvalue::Ref(_, _)
+                        | Rvalue::Len(_)
+                        | Rvalue::Cast(_, _, _)
+                        | Rvalue::CheckedBinaryOp(_, _, _)
+                        | Rvalue::UnaryOp(_, _)
+                        | Rvalue::Discriminant(_)
+                        | Rvalue::CopyForDeref(_) => (),
+                    },
+                    StatementKind::Deinit(_)
+                    | StatementKind::StorageLive(_)
+                    | StatementKind::StorageDead(_)
+                    | StatementKind::Nop => (),
+                }
+            }
+            if let Some(terminator) = &mut bb.terminator {
+                match &mut terminator.kind {
+                    TerminatorKind::Call { func, args, .. } => {
+                        self.fill_operand(func)?;
+                        for op in &mut **args {
+                            self.fill_operand(op)?;
+                        }
+                    }
+                    TerminatorKind::SwitchInt { discr, .. } => {
+                        self.fill_operand(discr)?;
+                    }
+                    TerminatorKind::Goto { .. }
+                    | TerminatorKind::Resume
+                    | TerminatorKind::Abort
+                    | TerminatorKind::Return
+                    | TerminatorKind::Unreachable
+                    | TerminatorKind::Drop { .. }
+                    | TerminatorKind::DropAndReplace { .. }
+                    | TerminatorKind::Assert { .. }
+                    | TerminatorKind::Yield { .. }
+                    | TerminatorKind::GeneratorDrop
+                    | TerminatorKind::FalseEdge { .. }
+                    | TerminatorKind::FalseUnwind { .. } => (),
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+pub fn monomorphized_mir_body_query(
+    db: &dyn HirDatabase,
+    owner: DefWithBodyId,
+    subst: Substitution,
+    trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+    let g_def = match owner {
+        DefWithBodyId::FunctionId(f) => Some(f.into()),
+        DefWithBodyId::StaticId(_) => None,
+        DefWithBodyId::ConstId(f) => Some(f.into()),
+        DefWithBodyId::VariantId(f) => Some(f.into()),
+    };
+    let generics = g_def.map(|g_def| generics(db.upcast(), g_def));
+    let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+    let body = db.mir_body(owner)?;
+    let mut body = (*body).clone();
+    filler.fill_body(&mut body)?;
+    Ok(Arc::new(body))
+}
+
+pub fn monomorphized_mir_body_recover(
+    _: &dyn HirDatabase,
+    _: &[String],
+    _: &DefWithBodyId,
+    _: &Substitution,
+    _: &Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+    return Err(MirLowerError::Loop);
+}
+
+pub fn monomorphized_mir_body_for_closure_query(
+    db: &dyn HirDatabase,
+    closure: ClosureId,
+    subst: Substitution,
+    trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+    let (owner, _) = db.lookup_intern_closure(closure.into());
+    let g_def = match owner {
+        DefWithBodyId::FunctionId(f) => Some(f.into()),
+        DefWithBodyId::StaticId(_) => None,
+        DefWithBodyId::ConstId(f) => Some(f.into()),
+        DefWithBodyId::VariantId(f) => Some(f.into()),
+    };
+    let generics = g_def.map(|g_def| generics(db.upcast(), g_def));
+    let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+    let body = db.mir_body_for_closure(closure)?;
+    let mut body = (*body).clone();
+    filler.fill_body(&mut body)?;
+    Ok(Arc::new(body))
+}
+
+// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query.
+pub fn monomorphize_mir_body_bad(
+    db: &dyn HirDatabase,
+    mut body: MirBody,
+    subst: Substitution,
+    trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<MirBody, MirLowerError> {
+    let owner = body.owner;
+    let g_def = match owner {
+        DefWithBodyId::FunctionId(f) => Some(f.into()),
+        DefWithBodyId::StaticId(_) => None,
+        DefWithBodyId::ConstId(f) => Some(f.into()),
+        DefWithBodyId::VariantId(f) => Some(f.into()),
+    };
+    let generics = g_def.map(|g_def| generics(db.upcast(), g_def));
+    let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+    filler.fill_body(&mut body)?;
+    Ok(body)
+}
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 3821e6a59c2..58662b01b99 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -437,6 +437,6 @@ impl<'a> MirPrettyCtx<'a> {
     }
 
     fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
-        ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithId)
+        ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
     }
 }
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 772bd3e536e..111ac0b618e 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -140,6 +140,7 @@ fn infer_path_qualified_macros_expanded() {
 fn expr_macro_def_expanded_in_various_places() {
     check_infer(
         r#"
+        //- minicore: iterator
         macro spam() {
             1isize
         }
@@ -195,8 +196,17 @@ fn expr_macro_def_expanded_in_various_places() {
             !0..6 '1isize': isize
             39..442 '{     ...!(); }': ()
             73..94 'spam!(...am!())': {unknown}
+            100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+            100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+            100..119 'for _ ...!() {}': !
+            100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+            100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+            100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+            100..119 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
             100..119 'for _ ...!() {}': ()
-            104..105 '_': {unknown}
+            100..119 'for _ ...!() {}': ()
+            100..119 'for _ ...!() {}': ()
+            104..105 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
             117..119 '{}': ()
             124..134 '|| spam!()': impl Fn() -> isize
             140..156 'while ...!() {}': ()
@@ -221,6 +231,7 @@ fn expr_macro_def_expanded_in_various_places() {
 fn expr_macro_rules_expanded_in_various_places() {
     check_infer(
         r#"
+        //- minicore: iterator
         macro_rules! spam {
             () => (1isize);
         }
@@ -276,8 +287,17 @@ fn expr_macro_rules_expanded_in_various_places() {
             !0..6 '1isize': isize
             53..456 '{     ...!(); }': ()
             87..108 'spam!(...am!())': {unknown}
+            114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+            114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+            114..133 'for _ ...!() {}': !
+            114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+            114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+            114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+            114..133 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
+            114..133 'for _ ...!() {}': ()
+            114..133 'for _ ...!() {}': ()
             114..133 'for _ ...!() {}': ()
-            118..119 '_': {unknown}
+            118..119 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
             131..133 '{}': ()
             138..148 '|| spam!()': impl Fn() -> isize
             154..170 'while ...!() {}': ()
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index 09c63f873ee..59046c0435a 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -327,6 +327,7 @@ fn diverging_expression_2() {
 fn diverging_expression_3_break() {
     check_infer_with_mismatches(
         r"
+        //- minicore: iterator
         //- /main.rs
         fn test1() {
             // should give type mismatch
@@ -360,6 +361,15 @@ fn diverging_expression_3_break() {
             97..343 '{     ...; }; }': ()
             140..141 'x': u32
             149..175 '{ for ...; }; }': u32
+            151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+            151..172 'for a ...eak; }': {unknown}
+            151..172 'for a ...eak; }': !
+            151..172 'for a ...eak; }': {unknown}
+            151..172 'for a ...eak; }': &mut {unknown}
+            151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+            151..172 'for a ...eak; }': Option<{unknown}>
+            151..172 'for a ...eak; }': ()
+            151..172 'for a ...eak; }': ()
             151..172 'for a ...eak; }': ()
             155..156 'a': {unknown}
             160..161 'b': {unknown}
@@ -367,12 +377,30 @@ fn diverging_expression_3_break() {
             164..169 'break': !
             226..227 'x': u32
             235..253 '{ for ... {}; }': u32
+            237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+            237..250 'for a in b {}': {unknown}
+            237..250 'for a in b {}': !
+            237..250 'for a in b {}': {unknown}
+            237..250 'for a in b {}': &mut {unknown}
+            237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+            237..250 'for a in b {}': Option<{unknown}>
+            237..250 'for a in b {}': ()
+            237..250 'for a in b {}': ()
             237..250 'for a in b {}': ()
             241..242 'a': {unknown}
             246..247 'b': {unknown}
             248..250 '{}': ()
             304..305 'x': u32
             313..340 '{ for ...; }; }': u32
+            315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+            315..337 'for a ...urn; }': {unknown}
+            315..337 'for a ...urn; }': !
+            315..337 'for a ...urn; }': {unknown}
+            315..337 'for a ...urn; }': &mut {unknown}
+            315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+            315..337 'for a ...urn; }': Option<{unknown}>
+            315..337 'for a ...urn; }': ()
+            315..337 'for a ...urn; }': ()
             315..337 'for a ...urn; }': ()
             319..320 'a': {unknown}
             324..325 'b': {unknown}
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index b73f0d72a3f..d683113d5d6 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -6,6 +6,7 @@ use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches
 fn infer_pattern() {
     check_infer(
         r#"
+        //- minicore: iterator
         fn test(x: &i32) {
             let y = x;
             let &z = x;
@@ -46,6 +47,15 @@ fn infer_pattern() {
             82..94 '(1, "hello")': (i32, &str)
             83..84 '1': i32
             86..93 '"hello"': &str
+            101..151 'for (e...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+            101..151 'for (e...     }': {unknown}
+            101..151 'for (e...     }': !
+            101..151 'for (e...     }': {unknown}
+            101..151 'for (e...     }': &mut {unknown}
+            101..151 'for (e...     }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+            101..151 'for (e...     }': Option<({unknown}, {unknown})>
+            101..151 'for (e...     }': ()
+            101..151 'for (e...     }': ()
             101..151 'for (e...     }': ()
             105..111 '(e, f)': ({unknown}, {unknown})
             106..107 'e': {unknown}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 8f4b807f560..a7b0f46e5e5 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -246,6 +246,7 @@ fn infer_std_crash_5() {
     // taken from rustc
     check_infer(
         r#"
+        //- minicore: iterator
         fn extra_compiler_flags() {
             for content in doesnt_matter {
                 let name = if doesnt_matter {
@@ -264,6 +265,15 @@ fn infer_std_crash_5() {
         "#,
         expect![[r#"
             26..322 '{     ...   } }': ()
+            32..320 'for co...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+            32..320 'for co...     }': {unknown}
+            32..320 'for co...     }': !
+            32..320 'for co...     }': {unknown}
+            32..320 'for co...     }': &mut {unknown}
+            32..320 'for co...     }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+            32..320 'for co...     }': Option<{unknown}>
+            32..320 'for co...     }': ()
+            32..320 'for co...     }': ()
             32..320 'for co...     }': ()
             36..43 'content': {unknown}
             47..60 'doesnt_matter': {unknown}
@@ -1215,6 +1225,7 @@ fn mamba(a: U32!(), p: u32) -> u32 {
 fn for_loop_block_expr_iterable() {
     check_infer(
         r#"
+//- minicore: iterator
 fn test() {
     for _ in { let x = 0; } {
         let y = 0;
@@ -1223,8 +1234,17 @@ fn test() {
         "#,
         expect![[r#"
             10..68 '{     ...   } }': ()
+            16..66 'for _ ...     }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter
+            16..66 'for _ ...     }': IntoIterator::IntoIter<()>
+            16..66 'for _ ...     }': !
+            16..66 'for _ ...     }': IntoIterator::IntoIter<()>
+            16..66 'for _ ...     }': &mut IntoIterator::IntoIter<()>
+            16..66 'for _ ...     }': fn next<IntoIterator::IntoIter<()>>(&mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item>
+            16..66 'for _ ...     }': Option<Iterator::Item<IntoIterator::IntoIter<()>>>
+            16..66 'for _ ...     }': ()
+            16..66 'for _ ...     }': ()
             16..66 'for _ ...     }': ()
-            20..21 '_': {unknown}
+            20..21 '_': Iterator::Item<IntoIterator::IntoIter<()>>
             25..39 '{ let x = 0; }': ()
             31..32 'x': i32
             35..36 '0': i32
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5392cb6a32a..38add4eda1a 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -62,7 +62,7 @@ use hir_ty::{
     consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
     diagnostics::BodyValidationDiagnostic,
     display::HexifiedConst,
-    layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx, TagEncoding},
+    layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding},
     method_resolution::{self, TyFingerprint},
     mir::{self, interpret_mir},
     primitive::UintTy,
@@ -961,8 +961,8 @@ impl Field {
         Type::new(db, var_id, ty)
     }
 
-    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
-        layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into())
+    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Arc<Layout>, LayoutError> {
+        db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into())
     }
 
     pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
@@ -1135,7 +1135,7 @@ impl Enum {
         self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
     }
 
-    pub fn layout(self, db: &dyn HirDatabase) -> Result<(Layout, usize), LayoutError> {
+    pub fn layout(self, db: &dyn HirDatabase) -> Result<(Arc<Layout>, usize), LayoutError> {
         let layout = Adt::from(self).layout(db)?;
         let tag_size =
             if let layout::Variants::Multiple { tag, tag_encoding, .. } = &layout.variants {
@@ -1219,11 +1219,11 @@ impl Variant {
         let parent_enum = self.parent_enum(db);
         let (parent_layout, tag_size) = parent_enum.layout(db)?;
         Ok((
-            match parent_layout.variants {
+            match &parent_layout.variants {
                 layout::Variants::Multiple { variants, .. } => {
                     variants[RustcEnumVariantIdx(self.id)].clone()
                 }
-                _ => parent_layout,
+                _ => (*parent_layout).clone(),
             },
             tag_size,
         ))
@@ -1255,7 +1255,7 @@ impl Adt {
         })
     }
 
-    pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+    pub fn layout(self, db: &dyn HirDatabase) -> Result<Arc<Layout>, LayoutError> {
         if db.generic_params(self.into()).iter().count() != 0 {
             return Err(LayoutError::HasPlaceholder);
         }
@@ -1949,7 +1949,11 @@ impl Function {
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
     ) -> String {
-        let body = match db.mir_body(self.id.into()) {
+        let body = match db.monomorphized_mir_body(
+            self.id.into(),
+            Substitution::empty(Interner),
+            db.trait_environment(self.id.into()),
+        ) {
             Ok(body) => body,
             Err(e) => {
                 let mut r = String::new();
@@ -1957,8 +1961,7 @@ impl Function {
                 return r;
             }
         };
-        let (result, stdout, stderr) =
-            interpret_mir(db, &body, Substitution::empty(Interner), false);
+        let (result, stdout, stderr) = interpret_mir(db, &body, false);
         let mut text = match result {
             Ok(_) => "pass".to_string(),
             Err(e) => {
@@ -4240,8 +4243,8 @@ impl Type {
             .collect()
     }
 
-    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
-        layout_of_ty(db, &self.ty, self.env.krate)
+    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Arc<Layout>, LayoutError> {
+        db.layout_of_ty(self.ty.clone(), self.env.krate)
     }
 }
 
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 62cc7488298..7587aea55cf 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -4728,6 +4728,7 @@ const fn $0fun_name() {
         check_assist(
             extract_function,
             r#"
+//- minicore: iterator
 fn foo() {
     let mut x = 5;
     for _ in 0..10 {
@@ -4751,6 +4752,7 @@ fn $0fun_name(x: &mut i32) {
         check_assist(
             extract_function,
             r#"
+//- minicore: iterator
 fn foo() {
     for _ in 0..10 {
         let mut x = 5;
@@ -4774,6 +4776,7 @@ fn $0fun_name(mut x: i32) {
         check_assist(
             extract_function,
             r#"
+//- minicore: iterator
 fn foo() {
     loop {
         let mut x = 5;
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 3b79def639d..2c6cbf6146a 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -329,6 +329,7 @@ fn foo() {
     fn complete_label_in_for_iterable() {
         check(
             r#"
+//- minicore: iterator
 fn foo() {
     'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
 }
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 7baa7b64268..30576c71fb7 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -124,12 +124,14 @@ fn foo() {
 
     #[test]
     fn value_break_in_for_loop() {
+        // FIXME: the error is correct, but the message is terrible
         check_diagnostics(
             r#"
+//- minicore: iterator
 fn test() {
     for _ in [()] {
         break 3;
-     // ^^^^^^^ error: can't break with a value in this position
+           // ^ error: expected (), found i32
     }
 }
 "#,
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 79fef0dcbd0..887d18b989a 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1137,5 +1137,5 @@ fn benchmark_syntax_highlighting_parser() {
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
             .count()
     };
-    assert_eq!(hash, 1170);
+    assert_eq!(hash, 1169);
 }