Merge pull request #20902 from krobelus/support-rustfmt-again

Fix rustfmt for files that use 2024-edition syntax
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 2ef7963..9b58abb 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -277,7 +277,7 @@
 
     // Interned IDs for solver integration
     #[salsa::interned]
-    fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
+    fn intern_impl_trait_id(&self, id: ImplTraitId<'_>) -> InternedOpaqueTyId;
 
     #[salsa::interned]
     fn intern_closure(&self, id: InternedClosure) -> InternedClosureId;
@@ -322,7 +322,7 @@
 #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedOpaqueTyId {
-    pub loc: ImplTraitId,
+    pub loc: ImplTraitId<'db>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index e807ce6..dd1b212 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -38,7 +38,8 @@
 use rustc_ast_ir::FloatTy;
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
+    AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, CoroutineClosureArgsParts, RegionKind,
+    Upcast,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, Tys as _},
 };
 use smallvec::SmallVec;
@@ -48,7 +49,7 @@
 
 use crate::{
     CallableDefId, FnAbi, ImplTraitId, MemoryMap, TraitEnvironment, consteval,
-    db::{HirDatabase, InternedClosure},
+    db::{HirDatabase, InternedClosure, InternedCoroutine},
     generics::generics,
     layout::Layout,
     mir::pad16,
@@ -58,7 +59,6 @@
         TraitRef, Ty, TyKind, TypingMode,
         abi::Safety,
         infer::{DbInternerInferExt, traits::ObligationCause},
-        mapping::ChalkToNextSolver,
     },
     primitive,
     utils::{self, detect_variant_from_bytes},
@@ -1126,9 +1126,9 @@
                             let datas = db
                                 .return_type_impl_traits(func)
                                 .expect("impl trait id without data");
-                            let data = (*datas).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data = (*datas)
+                                .as_ref()
+                                .map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             let bounds =
                                 || data.iter_instantiated_copied(f.interner, ty.args.as_slice());
                             let mut len = bounds().count();
@@ -1358,9 +1358,8 @@
                     ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         let datas =
                             db.return_type_impl_traits(func).expect("impl trait id without data");
-                        let data = (*datas).as_ref().map_bound(|rpit| {
-                            &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                        });
+                        let data =
+                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                         let bounds = data
                             .iter_instantiated_copied(interner, alias_ty.args.as_slice())
                             .collect::<Vec<_>>();
@@ -1377,9 +1376,8 @@
                     ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         let datas =
                             db.type_alias_impl_traits(alias).expect("impl trait id without data");
-                        let data = (*datas).as_ref().map_bound(|rpit| {
-                            &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                        });
+                        let data =
+                            (*datas).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                         let bounds = data
                             .iter_instantiated_copied(interner, alias_ty.args.as_slice())
                             .collect::<Vec<_>>();
@@ -1392,33 +1390,6 @@
                             SizedByDefault::Sized { anchor: krate },
                         )?;
                     }
-                    ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
-                        let future_trait =
-                            LangItem::Future.resolve_trait(db, body.module(db).krate());
-                        let output = future_trait.and_then(|t| {
-                            t.trait_items(db)
-                                .associated_type_by_name(&Name::new_symbol_root(sym::Output))
-                        });
-                        write!(f, "impl ")?;
-                        if let Some(t) = future_trait {
-                            f.start_location_link(t.into());
-                        }
-                        write!(f, "Future")?;
-                        if future_trait.is_some() {
-                            f.end_location_link();
-                        }
-                        write!(f, "<")?;
-                        if let Some(t) = output {
-                            f.start_location_link(t.into());
-                        }
-                        write!(f, "Output")?;
-                        if output.is_some() {
-                            f.end_location_link();
-                        }
-                        write!(f, " = ")?;
-                        alias_ty.args.type_at(0).hir_fmt(f)?;
-                        write!(f, ">")?;
-                    }
                 }
             }
             TyKind::Closure(id, substs) => {
@@ -1474,14 +1445,83 @@
                     }
                     if f.closure_style == ClosureStyle::RANotation || !sig.output().is_unit() {
                         write!(f, " -> ")?;
-                        // FIXME: We display `AsyncFn` as `-> impl Future`, but this is hard to fix because
-                        // we don't have a trait environment here, required to normalize `<Ret as Future>::Output`.
                         sig.output().hir_fmt(f)?;
                     }
                 } else {
                     write!(f, "{{closure}}")?;
                 }
             }
+            TyKind::CoroutineClosure(id, args) => {
+                let id = id.0;
+                if f.display_kind.is_source_code() {
+                    if !f.display_kind.allows_opaque() {
+                        return Err(HirDisplayError::DisplaySourceCodeError(
+                            DisplaySourceCodeError::OpaqueType,
+                        ));
+                    } else if f.closure_style != ClosureStyle::ImplFn {
+                        never!("Only `impl Fn` is valid for displaying closures in source code");
+                    }
+                }
+                match f.closure_style {
+                    ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
+                    ClosureStyle::ClosureWithId => {
+                        return write!(
+                            f,
+                            "{{async closure#{:?}}}",
+                            salsa::plumbing::AsId::as_id(&id).index()
+                        );
+                    }
+                    ClosureStyle::ClosureWithSubst => {
+                        write!(
+                            f,
+                            "{{async closure#{:?}}}",
+                            salsa::plumbing::AsId::as_id(&id).index()
+                        )?;
+                        return hir_fmt_generics(f, args.as_slice(), None, None);
+                    }
+                    _ => (),
+                }
+                let CoroutineClosureArgsParts { closure_kind_ty, signature_parts_ty, .. } =
+                    args.split_coroutine_closure_args();
+                let kind = closure_kind_ty.to_opt_closure_kind().unwrap();
+                let kind = match kind {
+                    rustc_type_ir::ClosureKind::Fn => "AsyncFn",
+                    rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut",
+                    rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce",
+                };
+                let TyKind::FnPtr(coroutine_sig, _) = signature_parts_ty.kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let coroutine_sig = coroutine_sig.skip_binder();
+                let coroutine_inputs = coroutine_sig.inputs();
+                let TyKind::Tuple(coroutine_inputs) = coroutine_inputs.as_slice()[1].kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let TyKind::Tuple(coroutine_output) = coroutine_sig.output().kind() else {
+                    unreachable!("invalid coroutine closure signature");
+                };
+                let coroutine_output = coroutine_output.as_slice()[1];
+                match f.closure_style {
+                    ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,
+                    ClosureStyle::RANotation => write!(f, "async |")?,
+                    _ => unreachable!(),
+                }
+                if coroutine_inputs.is_empty() {
+                } else if f.should_truncate() {
+                    write!(f, "{TYPE_HINT_TRUNCATION}")?;
+                } else {
+                    f.write_joined(coroutine_inputs, ", ")?;
+                };
+                match f.closure_style {
+                    ClosureStyle::ImplFn => write!(f, ")")?,
+                    ClosureStyle::RANotation => write!(f, "|")?,
+                    _ => unreachable!(),
+                }
+                if f.closure_style == ClosureStyle::RANotation || !coroutine_output.is_unit() {
+                    write!(f, " -> ")?;
+                    coroutine_output.hir_fmt(f)?;
+                }
+            }
             TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?,
             TyKind::Param(param) => {
                 // FIXME: We should not access `param.id`, it should be removed, and we should know the
@@ -1570,28 +1610,69 @@
                 }
             }
             TyKind::Infer(..) => write!(f, "_")?,
-            TyKind::Coroutine(_, subst) => {
-                if f.display_kind.is_source_code() {
-                    return Err(HirDisplayError::DisplaySourceCodeError(
-                        DisplaySourceCodeError::Coroutine,
-                    ));
-                }
+            TyKind::Coroutine(coroutine_id, subst) => {
+                let InternedCoroutine(owner, expr_id) = coroutine_id.0.loc(db);
                 let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =
                     subst.split_coroutine_args();
-                write!(f, "|")?;
-                resume_ty.hir_fmt(f)?;
-                write!(f, "|")?;
+                let body = db.body(owner);
+                let expr = &body[expr_id];
+                match expr {
+                    hir_def::hir::Expr::Closure {
+                        closure_kind: hir_def::hir::ClosureKind::Async,
+                        ..
+                    }
+                    | hir_def::hir::Expr::Async { .. } => {
+                        let future_trait =
+                            LangItem::Future.resolve_trait(db, owner.module(db).krate());
+                        let output = future_trait.and_then(|t| {
+                            t.trait_items(db)
+                                .associated_type_by_name(&Name::new_symbol_root(sym::Output))
+                        });
+                        write!(f, "impl ")?;
+                        if let Some(t) = future_trait {
+                            f.start_location_link(t.into());
+                        }
+                        write!(f, "Future")?;
+                        if future_trait.is_some() {
+                            f.end_location_link();
+                        }
+                        write!(f, "<")?;
+                        if let Some(t) = output {
+                            f.start_location_link(t.into());
+                        }
+                        write!(f, "Output")?;
+                        if output.is_some() {
+                            f.end_location_link();
+                        }
+                        write!(f, " = ")?;
+                        return_ty.hir_fmt(f)?;
+                        write!(f, ">")?;
+                    }
+                    hir_def::hir::Expr::Closure {
+                        closure_kind: hir_def::hir::ClosureKind::Coroutine(..),
+                        ..
+                    } => {
+                        if f.display_kind.is_source_code() {
+                            return Err(HirDisplayError::DisplaySourceCodeError(
+                                DisplaySourceCodeError::Coroutine,
+                            ));
+                        }
+                        write!(f, "|")?;
+                        resume_ty.hir_fmt(f)?;
+                        write!(f, "|")?;
 
-                write!(f, " yields ")?;
-                yield_ty.hir_fmt(f)?;
+                        write!(f, " yields ")?;
+                        yield_ty.hir_fmt(f)?;
 
-                write!(f, " -> ")?;
-                return_ty.hir_fmt(f)?;
+                        write!(f, " -> ")?;
+                        return_ty.hir_fmt(f)?;
+                    }
+                    _ => panic!("invalid expr for coroutine: {expr:?}"),
+                }
             }
             TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
             TyKind::Pat(_, _) => write!(f, "{{pat}}")?,
             TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?,
-            TyKind::CoroutineClosure(_, _) => write!(f, "{{coroutine closure}}")?,
             TyKind::Alias(_, _) => write!(f, "{{alias}}")?,
         }
         Ok(())
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 9891f3f..361e665 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -73,7 +73,6 @@
         abi::Safety,
         fold::fold_tys,
         infer::traits::{Obligation, ObligationCause},
-        mapping::ChalkToNextSolver,
     },
     traits::FnTrait,
     utils::TargetFeatureIsSafeInTarget,
@@ -1228,9 +1227,7 @@
                     if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
                         // RPITs don't have `tait_coercion_table`, so use inserted inference
                         // vars for them.
-                        if let Some(ty) =
-                            self.result.type_of_rpit.get(idx.to_nextsolver(self.interner()))
-                        {
+                        if let Some(ty) = self.result.type_of_rpit.get(idx) {
                             return *ty;
                         }
                         return ty;
@@ -1246,15 +1243,13 @@
                     }
                     (self.db.type_alias_impl_traits(def), idx)
                 }
-                _ => unreachable!(),
             };
             let Some(impl_traits) = impl_traits else {
                 return ty;
             };
-            let bounds = (*impl_traits).as_ref().map_bound(|its| {
-                its.impl_traits[idx.to_nextsolver(self.interner())].predicates.as_slice()
-            });
-            let var = match self.result.type_of_rpit.entry(idx.to_nextsolver(self.interner())) {
+            let bounds =
+                (*impl_traits).as_ref().map_bound(|its| its.impl_traits[idx].predicates.as_slice());
+            let var = match self.result.type_of_rpit.entry(idx) {
                 Entry::Occupied(entry) => return *entry.get(),
                 Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()),
             };
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 3dc2770..06f8307 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -11,8 +11,9 @@
     type_ref::TypeRefId,
 };
 use rustc_type_ir::{
-    ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, Interner, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor,
+    ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
+    CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor,
     inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _},
 };
 use tracing::debug;
@@ -22,8 +23,9 @@
     db::{InternedClosure, InternedCoroutine},
     infer::{BreakableKind, Diverges, coerce::CoerceMany},
     next_solver::{
-        AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig,
-        PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind,
+        AliasTy, Binder, BoundRegionKind, BoundVarKind, BoundVarKinds, ClauseKind, DbInterner,
+        ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, PolyProjectionPredicate, Predicate,
+        PredicateKind, SolverDefId, Ty, TyKind,
         abi::Safety,
         infer::{
             BoundRegionConversionTime, InferOk, InferResult,
@@ -72,6 +74,8 @@
         let sig_ty = Ty::new_fn_ptr(interner, bound_sig);
 
         let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
+        // FIXME: Make this an infer var and infer it later.
+        let tupled_upvars_ty = self.types.unit;
         let (id, ty, resume_yield_tys) = match closure_kind {
             ClosureKind::Coroutine(_) => {
                 let yield_ty = self.table.next_ty_var();
@@ -80,11 +84,11 @@
                 // FIXME: Infer the upvars later.
                 let parts = CoroutineArgsParts {
                     parent_args,
-                    kind_ty: Ty::new_unit(interner),
+                    kind_ty: self.types.unit,
                     resume_ty,
                     yield_ty,
                     return_ty: body_ret_ty,
-                    tupled_upvars_ty: Ty::new_unit(interner),
+                    tupled_upvars_ty,
                 };
 
                 let coroutine_id =
@@ -97,9 +101,7 @@
 
                 (None, coroutine_ty, Some((resume_ty, yield_ty)))
             }
-            // FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`.
-            // But for now we treat it as a closure.
-            ClosureKind::Closure | ClosureKind::Async => {
+            ClosureKind::Closure => {
                 let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr));
                 match expected_kind {
                     Some(kind) => {
@@ -117,7 +119,7 @@
                     }
                     None => {}
                 };
-                // FIXME: Infer the kind and the upvars later when needed.
+                // FIXME: Infer the kind later if needed.
                 let parts = ClosureArgsParts {
                     parent_args,
                     closure_kind_ty: Ty::from_closure_kind(
@@ -125,7 +127,7 @@
                         expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
                     ),
                     closure_sig_as_fn_ptr_ty: sig_ty,
-                    tupled_upvars_ty: Ty::new_unit(interner),
+                    tupled_upvars_ty,
                 };
                 let closure_ty = Ty::new_closure(
                     interner,
@@ -136,6 +138,61 @@
                 self.add_current_closure_dependency(closure_id);
                 (Some(closure_id), closure_ty, None)
             }
+            ClosureKind::Async => {
+                // async closures always return the type ascribed after the `->` (if present),
+                // and yield `()`.
+                let bound_return_ty = bound_sig.skip_binder().output();
+                let bound_yield_ty = self.types.unit;
+                // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
+                let resume_ty = self.types.unit;
+
+                // FIXME: Infer the kind later if needed.
+                let closure_kind_ty = Ty::from_closure_kind(
+                    interner,
+                    expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
+                );
+
+                // FIXME: Infer captures later.
+                // `for<'env> fn() -> ()`, for no captures.
+                let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
+                    interner,
+                    Binder::bind_with_vars(
+                        interner.mk_fn_sig([], self.types.unit, false, Safety::Safe, FnAbi::Rust),
+                        BoundVarKinds::new_from_iter(
+                            interner,
+                            [BoundVarKind::Region(BoundRegionKind::ClosureEnv)],
+                        ),
+                    ),
+                );
+                let closure_args = CoroutineClosureArgs::new(
+                    interner,
+                    CoroutineClosureArgsParts {
+                        parent_args,
+                        closure_kind_ty,
+                        signature_parts_ty: Ty::new_fn_ptr(
+                            interner,
+                            bound_sig.map_bound(|sig| {
+                                interner.mk_fn_sig(
+                                    [
+                                        resume_ty,
+                                        Ty::new_tup_from_iter(interner, sig.inputs().iter()),
+                                    ],
+                                    Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]),
+                                    sig.c_variadic,
+                                    sig.safety,
+                                    sig.abi,
+                                )
+                            }),
+                        ),
+                        tupled_upvars_ty,
+                        coroutine_captures_by_ref_ty,
+                    },
+                );
+
+                let coroutine_id =
+                    self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
+                (None, Ty::new_coroutine_closure(interner, coroutine_id, closure_args.args), None)
+            }
         };
 
         // Now go through the argument patterns
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index efb7244..fd4e374 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -18,7 +18,7 @@
 use intern::sym;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::{
-    AliasTyKind, InferTy, Interner,
+    CoroutineArgs, CoroutineArgsParts, InferTy, Interner,
     inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _},
 };
 use syntax::ast::RangeOp;
@@ -29,6 +29,7 @@
     IncorrectGenericsLenKind, Rawness, TraitEnvironment,
     autoderef::overloaded_deref_ty,
     consteval,
+    db::InternedCoroutine,
     generics::generics,
     infer::{
         AllowTwoPhase, BreakableKind,
@@ -43,7 +44,7 @@
     },
     method_resolution::{self, VisibleFromModule},
     next_solver::{
-        AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind,
+        Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, TraitRef, Ty, TyKind,
         TypeError,
         infer::{
             InferOk,
@@ -1132,18 +1133,26 @@
         inner_ty: Ty<'db>,
         tgt_expr: ExprId,
     ) -> Ty<'db> {
-        // Use the first type parameter as the output type of future.
-        // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
-        let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
-        let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
-        Ty::new_alias(
+        let coroutine_id = InternedCoroutine(self.owner, tgt_expr);
+        let coroutine_id = self.db.intern_coroutine(coroutine_id).into();
+        let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into());
+        Ty::new_coroutine(
             self.interner(),
-            AliasTyKind::Opaque,
-            AliasTy::new(
+            coroutine_id,
+            CoroutineArgs::new(
                 self.interner(),
-                opaque_ty_id,
-                GenericArgs::new_from_iter(self.interner(), [inner_ty.into()]),
-            ),
+                CoroutineArgsParts {
+                    parent_args,
+                    kind_ty: self.types.unit,
+                    // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
+                    resume_ty: self.types.unit,
+                    yield_ty: self.types.unit,
+                    return_ty: inner_ty,
+                    // FIXME: Infer upvars.
+                    tupled_upvars_ty: self.types.unit,
+                },
+            )
+            .args,
         )
     }
 
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 094a3e5..25579e0 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -27,6 +27,7 @@
 mod inhabitedness;
 mod lower;
 pub mod next_solver;
+mod specialization;
 mod target_feature;
 mod utils;
 
@@ -53,11 +54,10 @@
 
 use std::hash::Hash;
 
-use hir_def::{CallableDefId, TypeOrConstParamId, hir::ExprId, type_ref::Rawness};
+use hir_def::{CallableDefId, TypeOrConstParamId, type_ref::Rawness};
 use hir_expand::name::Name;
 use indexmap::{IndexMap, map::Entry};
 use intern::{Symbol, sym};
-use la_arena::Idx;
 use mir::{MirEvalError, VTableMap};
 use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
 use rustc_type_ir::{
@@ -332,17 +332,11 @@
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
-pub enum ImplTraitId {
-    ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), // FIXME(next-solver): Should be crate::nextsolver::ImplTraitIdx.
-    TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
-    AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
+pub enum ImplTraitId<'db> {
+    ReturnTypeImplTrait(hir_def::FunctionId, next_solver::ImplTraitIdx<'db>),
+    TypeAliasImplTrait(hir_def::TypeAliasId, next_solver::ImplTraitIdx<'db>),
 }
 
-#[derive(PartialEq, Eq, Debug, Hash)]
-pub struct ImplTrait {}
-
-pub type ImplTraitIdx = Idx<ImplTrait>;
-
 /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
 /// ensures there are no unbound variables or inference variables anywhere in
 /// the `t`.
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 6f7ca48..a181ae0 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -475,10 +475,9 @@
                             .opaque_type_data
                             .alloc(ImplTrait { predicates: Vec::default() });
 
-                        // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal
                         let impl_trait_id = origin.either(
-                            |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())),
-                            |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())),
+                            |f| ImplTraitId::ReturnTypeImplTrait(f, idx),
+                            |a| ImplTraitId::TypeAliasImplTrait(a, idx),
                         );
                         let opaque_ty_id: SolverDefId =
                             self.db.intern_impl_trait_id(impl_trait_id).into();
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 4eb4aa9..f242115 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -636,16 +636,13 @@
     );
 }
 
-#[ignore = "
-FIXME(next-solver):
-This does not work currently because I replaced homemade selection with selection by the trait solver;
-This will work once we implement `Interner::impl_specializes()` properly.
-"]
 #[test]
 fn specialization_array_clone() {
     check_pass(
         r#"
 //- minicore: copy, derive, slice, index, coerce_unsized, panic
+#![feature(min_specialization)]
+
 impl<T: Clone, const N: usize> Clone for [T; N] {
     #[inline]
     fn clone(&self) -> Self {
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
index 1fb9a82..8c52a84 100644
--- a/crates/hir-ty/src/next_solver.rs
+++ b/crates/hir-ty/src/next_solver.rs
@@ -11,7 +11,6 @@
 pub(crate) mod inspect;
 pub mod interner;
 mod ir_print;
-pub mod mapping;
 pub mod normalize;
 pub mod obligation_ctxt;
 mod opaques;
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index 928e132..0ff0b08 100644
--- a/crates/hir-ty/src/next_solver/def_id.rs
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -211,7 +211,7 @@
 
         impl std::fmt::Debug for $name {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-                std::fmt::Debug::fmt(&self.0, f)
+                std::fmt::Debug::fmt(&SolverDefId::from(self.0), f)
             }
         }
 
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs
index 24f22bc..90bd44a 100644
--- a/crates/hir-ty/src/next_solver/generic_arg.rs
+++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -446,7 +446,6 @@
                 signature_parts_ty,
                 tupled_upvars_ty,
                 coroutine_captures_by_ref_ty,
-                _coroutine_witness_ty,
             ] => rustc_type_ir::CoroutineClosureArgsParts {
                 parent_args: GenericArgs::new_from_iter(
                     DbInterner::conjure(),
diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs
index d5a9a6f..4d164a7 100644
--- a/crates/hir-ty/src/next_solver/generics.rs
+++ b/crates/hir-ty/src/next_solver/generics.rs
@@ -2,10 +2,7 @@
 
 use hir_def::{
     ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
-    hir::generics::{
-        GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData,
-        TypeParamProvenance,
-    },
+    hir::generics::{GenericParams, TypeOrConstParamData},
 };
 
 use crate::{db::HirDatabase, generics::parent_generic_def};
@@ -67,27 +64,6 @@
                 crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => {
                     (Some(type_alias_id.into()), Vec::new())
                 }
-                crate::ImplTraitId::AsyncBlockTypeImplTrait(_def, _) => {
-                    let param = TypeOrConstParamData::TypeParamData(TypeParamData {
-                        name: None,
-                        default: None,
-                        provenance: TypeParamProvenance::TypeParamList,
-                    });
-                    // Yes, there is a parent but we don't include it in the generics
-                    // FIXME: It seems utterly sensitive to fake a generic param here.
-                    // Also, what a horrible mess!
-                    (
-                        None,
-                        vec![mk_ty(
-                            GenericDefId::FunctionId(salsa::plumbing::FromId::from_id(unsafe {
-                                salsa::Id::from_index(salsa::Id::MAX_U32 - 1)
-                            })),
-                            0,
-                            LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)),
-                            &param,
-                        )],
-                    )
-                }
             }
         }
         _ => panic!("No generics for {def:?}"),
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index a509fd8..c1ccbaf 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -17,8 +17,8 @@
 use rustc_hash::FxHashSet;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_type_ir::{
-    AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex, EarlyBinder,
-    FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
+    AliasTermKind, AliasTyKind, BoundVar, CollectAndApply, CoroutineWitnessTypes, DebruijnIndex,
+    EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy, Interner, TraitRef,
     TypeVisitableExt, UniverseIndex, Upcast, Variance,
     elaborate::elaborate,
     error::TypeError,
@@ -29,7 +29,7 @@
 
 use crate::{
     FnAbi,
-    db::HirDatabase,
+    db::{HirDatabase, InternedCoroutine},
     method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
     next_solver::{
         AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
@@ -47,7 +47,6 @@
     abi::Safety,
     fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
     generics::{Generics, generics},
-    mapping::ChalkToNextSolver,
     region::{
         BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region,
     },
@@ -1206,12 +1205,28 @@
         self.db().callable_item_signature(def_id.0)
     }
 
-    fn coroutine_movability(self, _def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
-        unimplemented!()
+    fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
+        // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
+        // the current infer query, except with revealed opaques - is it rare enough to not matter?
+        let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        let expr = &body[expr_id];
+        match *expr {
+            hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind {
+                hir_def::hir::ClosureKind::Coroutine(movability) => match movability {
+                    hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static,
+                    hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
+                },
+                hir_def::hir::ClosureKind::Async => rustc_ast_ir::Movability::Static,
+                _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+            },
+            hir_def::hir::Expr::Async { .. } => rustc_ast_ir::Movability::Static,
+            _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+        }
     }
 
-    fn coroutine_for_closure(self, _def_id: Self::CoroutineId) -> Self::CoroutineId {
-        unimplemented!()
+    fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId {
+        def_id
     }
 
     fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
@@ -1726,23 +1741,39 @@
         panic!("Bug encountered in next-trait-solver: {}", msg.to_string())
     }
 
-    fn is_general_coroutine(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
-        true
+    fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool {
+        // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
+        // the current infer query, except with revealed opaques - is it rare enough to not matter?
+        let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        matches!(
+            body[expr_id],
+            hir_def::hir::Expr::Closure {
+                closure_kind: hir_def::hir::ClosureKind::Coroutine(_),
+                ..
+            }
+        )
     }
 
-    fn coroutine_is_async(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
-        true
+    fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool {
+        // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
+        // the current infer query, except with revealed opaques - is it rare enough to not matter?
+        let InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+        let body = self.db.body(owner);
+        matches!(
+            body[expr_id],
+            hir_def::hir::Expr::Closure { closure_kind: hir_def::hir::ClosureKind::Async, .. }
+                | hir_def::hir::Expr::Async { .. }
+        )
     }
 
     fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
+        // We don't handle gen coroutines yet.
         false
     }
 
     fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
-        // FIXME(next-solver)
+        // We don't handle gen coroutines yet.
         false
     }
 
@@ -1883,10 +1914,9 @@
                 match impl_trait_id {
                     crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         let infer = self.db().infer(func.into());
-                        EarlyBinder::bind(infer.type_of_rpit[idx.to_nextsolver(self)])
+                        EarlyBinder::bind(infer.type_of_rpit[idx])
                     }
-                    crate::ImplTraitId::TypeAliasImplTrait(..)
-                    | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+                    crate::ImplTraitId::TypeAliasImplTrait(..) => {
                         // FIXME(next-solver)
                         EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed))
                     }
@@ -1899,10 +1929,12 @@
     fn coroutine_hidden_types(
         self,
         _def_id: Self::CoroutineId,
-    ) -> EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::CoroutineWitnessTypes<Self>>>
-    {
-        // FIXME(next-solver)
-        unimplemented!()
+    ) -> EarlyBinder<Self, Binder<'db, CoroutineWitnessTypes<Self>>> {
+        // FIXME: Actually implement this.
+        EarlyBinder::bind(Binder::dummy(CoroutineWitnessTypes {
+            types: Tys::default(),
+            assumptions: RegionAssumptions::default(),
+        }))
     }
 
     fn is_default_trait(self, def_id: Self::TraitId) -> bool {
@@ -1923,10 +1955,14 @@
 
     fn impl_specializes(
         self,
-        _specializing_impl_def_id: Self::ImplId,
-        _parent_impl_def_id: Self::ImplId,
+        specializing_impl_def_id: Self::ImplId,
+        parent_impl_def_id: Self::ImplId,
     ) -> bool {
-        false
+        crate::specialization::specializes(
+            self.db,
+            specializing_impl_def_id.0,
+            parent_impl_def_id.0,
+        )
     }
 
     fn next_trait_solver_globally(self) -> bool {
diff --git a/crates/hir-ty/src/next_solver/mapping.rs b/crates/hir-ty/src/next_solver/mapping.rs
deleted file mode 100644
index 2b29561..0000000
--- a/crates/hir-ty/src/next_solver/mapping.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! Things useful for mapping to/from Chalk and next-trait-solver types.
-
-use crate::next_solver::interner::DbInterner;
-
-pub(crate) trait ChalkToNextSolver<'db, Out> {
-    fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out;
-}
-
-impl<'db> ChalkToNextSolver<'db, crate::lower::ImplTraitIdx<'db>> for crate::ImplTraitIdx {
-    fn to_nextsolver(&self, _interner: DbInterner<'db>) -> crate::lower::ImplTraitIdx<'db> {
-        crate::lower::ImplTraitIdx::from_raw(self.into_raw())
-    }
-}
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index 3abbd28..b8406fe 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -11,10 +11,10 @@
 use rustc_abi::{Float, Integer, Size};
 use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
 use rustc_type_ir::{
-    AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags,
-    FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable,
-    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast,
-    WithCachedTypeInfo,
+    AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, CoroutineArgs, CoroutineArgsParts,
+    DebruijnIndex, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, Interner,
+    TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
     inherent::{
         AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _,
         IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _,
@@ -26,13 +26,12 @@
 
 use crate::{
     ImplTraitId,
-    db::HirDatabase,
+    db::{HirDatabase, InternedCoroutine},
     next_solver::{
         AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
         CoroutineIdWrapper, FnSig, GenericArg, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
         abi::Safety,
         interner::InternedWrapperNoDebug,
-        mapping::ChalkToNextSolver,
         util::{CoroutineArgsExt, IntegerTypeExt},
     },
 };
@@ -405,6 +404,40 @@
                 .split_closure_args_untupled()
                 .closure_sig_as_fn_ptr_ty
                 .callable_sig(interner),
+            TyKind::CoroutineClosure(coroutine_id, args) => {
+                Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
+                    let unit_ty = Ty::new_unit(interner);
+                    let return_ty = Ty::new_coroutine(
+                        interner,
+                        coroutine_id,
+                        CoroutineArgs::new(
+                            interner,
+                            CoroutineArgsParts {
+                                parent_args: args.as_coroutine_closure().parent_args(),
+                                kind_ty: unit_ty,
+                                resume_ty: unit_ty,
+                                yield_ty: unit_ty,
+                                return_ty: sig.return_ty,
+                                // FIXME: Deduce this from the coroutine closure's upvars.
+                                tupled_upvars_ty: unit_ty,
+                            },
+                        )
+                        .args,
+                    );
+                    FnSig {
+                        inputs_and_output: Tys::new_from_iter(
+                            interner,
+                            sig.tupled_inputs_ty
+                                .tuple_fields()
+                                .iter()
+                                .chain(std::iter::once(return_ty)),
+                        ),
+                        c_variadic: sig.c_variadic,
+                        safety: sig.safety,
+                        abi: sig.abi,
+                    }
+                }))
+            }
             _ => None,
         }
     }
@@ -533,39 +566,20 @@
                 match db.lookup_intern_impl_trait_id(opaque_ty.def_id.expect_opaque_ty()) {
                     ImplTraitId::ReturnTypeImplTrait(func, idx) => {
                         db.return_type_impl_traits(func).map(|it| {
-                            let data = (*it).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data =
+                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
                                 .collect()
                         })
                     }
                     ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         db.type_alias_impl_traits(alias).map(|it| {
-                            let data = (*it).as_ref().map_bound(|rpit| {
-                                &rpit.impl_traits[idx.to_nextsolver(interner)].predicates
-                            });
+                            let data =
+                                (*it).as_ref().map_bound(|rpit| &rpit.impl_traits[idx].predicates);
                             data.iter_instantiated_copied(interner, opaque_ty.args.as_slice())
                                 .collect()
                         })
                     }
-                    ImplTraitId::AsyncBlockTypeImplTrait(def, _) => {
-                        let krate = def.module(db).krate();
-                        if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
-                            // This is only used by type walking.
-                            // Parameters will be walked outside, and projection predicate is not used.
-                            // So just provide the Future trait.
-                            let impl_bound = TraitRef::new(
-                                interner,
-                                future_trait.into(),
-                                GenericArgs::new_from_iter(interner, []),
-                            )
-                            .upcast(interner);
-                            Some(vec![impl_bound])
-                        } else {
-                            None
-                        }
-                    }
                 }
             }
             TyKind::Param(param) => {
@@ -595,6 +609,24 @@
                     _ => None,
                 }
             }
+            TyKind::Coroutine(coroutine_id, _args) => {
+                let InternedCoroutine(owner, _) = coroutine_id.0.loc(db);
+                let krate = owner.module(db).krate();
+                if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) {
+                    // This is only used by type walking.
+                    // Parameters will be walked outside, and projection predicate is not used.
+                    // So just provide the Future trait.
+                    let impl_bound = TraitRef::new(
+                        interner,
+                        future_trait.into(),
+                        GenericArgs::new_from_iter(interner, []),
+                    )
+                    .upcast(interner);
+                    Some(vec![impl_bound])
+                } else {
+                    None
+                }
+            }
             _ => None,
         }
     }
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index bb0d055..d113f76 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -7,11 +7,10 @@
 
 use base_db::Crate;
 use hir_def::{BlockId, HasModule, lang_item::LangItem};
-use intern::sym;
 use la_arena::Idx;
 use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions};
 use rustc_type_ir::{
-    ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, GenericArgKind, INNERMOST, IntTy, Interner,
+    ConstKind, CoroutineArgs, DebruijnIndex, FloatTy, INNERMOST, IntTy, Interner,
     PredicatePolarity, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
     TypeVisitableExt, TypeVisitor, UintTy, UniverseIndex,
     inherent::{
@@ -32,9 +31,8 @@
 };
 
 use super::{
-    AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds,
-    Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArgs, Predicate,
-    PredicateKind, ProjectionPredicate, Region, SolverDefId, Term, TraitPredicate, TraitRef, Ty,
+    Binder, BoundRegion, BoundTy, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder,
+    GenericArgs, Predicate, PredicateKind, Region, SolverDefId, TraitPredicate, TraitRef, Ty,
     TyKind,
     fold::{BoundVarReplacer, FnMutDelegate},
 };
@@ -578,98 +576,9 @@
                     let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())];
                     EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone()))
                 }
-                crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
-                    if let Some((future_trait, future_output)) = LangItem::Future
-                        .resolve_trait(db, interner.krate.expect("Must have interner.krate"))
-                        .and_then(|trait_| {
-                            let alias = trait_.trait_items(db).associated_type_by_name(
-                                &hir_expand::name::Name::new_symbol_root(sym::Output.clone()),
-                            )?;
-                            Some((trait_, alias))
-                        })
-                    {
-                        let args = GenericArgs::identity_for_item(interner, def_id);
-                        let out = args.as_slice()[0];
-                        let mut predicates = vec![];
-
-                        let item_ty = Ty::new_alias(
-                            interner,
-                            rustc_type_ir::AliasTyKind::Opaque,
-                            AliasTy::new_from_args(interner, def_id, args),
-                        );
-
-                        let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
-                            polarity: rustc_type_ir::PredicatePolarity::Positive,
-                            trait_ref: TraitRef::new_from_args(
-                                interner,
-                                future_trait.into(),
-                                GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                            ),
-                        }));
-                        predicates.push(Clause(Predicate::new(
-                            interner,
-                            Binder::bind_with_vars(
-                                kind,
-                                BoundVarKinds::new_from_iter(
-                                    interner,
-                                    [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                ),
-                            ),
-                        )));
-                        let sized_trait = LangItem::Sized
-                            .resolve_trait(db, interner.krate.expect("Must have interner.krate"));
-                        if let Some(sized_trait_) = sized_trait {
-                            let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
-                                polarity: rustc_type_ir::PredicatePolarity::Positive,
-                                trait_ref: TraitRef::new_from_args(
-                                    interner,
-                                    sized_trait_.into(),
-                                    GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                                ),
-                            }));
-                            predicates.push(Clause(Predicate::new(
-                                interner,
-                                Binder::bind_with_vars(
-                                    kind,
-                                    BoundVarKinds::new_from_iter(
-                                        interner,
-                                        [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                    ),
-                                ),
-                            )));
-                        }
-                        let kind =
-                            PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate {
-                                projection_term: AliasTerm::new_from_args(
-                                    interner,
-                                    future_output.into(),
-                                    GenericArgs::new_from_iter(interner, [item_ty.into()]),
-                                ),
-                                term: match out.kind() {
-                                    GenericArgKind::Lifetime(_lt) => panic!(),
-                                    GenericArgKind::Type(ty) => Term::Ty(ty),
-                                    GenericArgKind::Const(const_) => Term::Const(const_),
-                                },
-                            }));
-                        predicates.push(Clause(Predicate::new(
-                            interner,
-                            Binder::bind_with_vars(
-                                kind,
-                                BoundVarKinds::new_from_iter(
-                                    interner,
-                                    [BoundVarKind::Ty(BoundTyKind::Anon)],
-                                ),
-                            ),
-                        )));
-                        EarlyBinder::bind(Clauses::new_from_iter(interner, predicates))
-                    } else {
-                        // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
-                        EarlyBinder::bind(Clauses::new_from_iter(interner, []))
-                    }
-                }
             }
         }
-        _ => panic!("Unexpected GeneridDefId"),
+        _ => panic!("Unexpected GenericDefId"),
     }
 }
 
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
new file mode 100644
index 0000000..611947b
--- /dev/null
+++ b/crates/hir-ty/src/specialization.rs
@@ -0,0 +1,150 @@
+//! Impl specialization related things
+
+use hir_def::{ImplId, nameres::crate_def_map};
+use intern::sym;
+use tracing::debug;
+
+use crate::{
+    db::HirDatabase,
+    next_solver::{
+        DbInterner, TypingMode,
+        infer::{
+            DbInternerInferExt,
+            traits::{Obligation, ObligationCause},
+        },
+        obligation_ctxt::ObligationCtxt,
+    },
+};
+
+// rustc does not have a cycle handling for the `specializes` query, meaning a cycle is a bug,
+// and indeed I was unable to cause cycles even with erroneous code. However, in r-a we can
+// create a cycle if there is an error in the impl's where clauses. I believe well formed code
+// cannot create a cycle, but a cycle handler is required nevertheless.
+fn specializes_cycle(
+    _db: &dyn HirDatabase,
+    _specializing_impl_def_id: ImplId,
+    _parent_impl_def_id: ImplId,
+) -> bool {
+    false
+}
+
+/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`?
+///
+/// For every type that could apply to `specializing_impl_def_id`, we prove that
+/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and
+/// its where-clauses hold).
+///
+/// For the purposes of const traits, we also check that the specializing
+/// impl is not more restrictive than the parent impl. That is, if the
+/// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]`
+/// bounds), then `specializing_impl_def_id` must also be const for the same
+/// set of types.
+#[salsa::tracked(cycle_result = specializes_cycle)]
+pub(crate) fn specializes(
+    db: &dyn HirDatabase,
+    specializing_impl_def_id: ImplId,
+    parent_impl_def_id: ImplId,
+) -> bool {
+    let module = specializing_impl_def_id.loc(db).container;
+
+    // We check that the specializing impl comes from a crate that has specialization enabled.
+    //
+    // We don't really care if the specialized impl (the parent) is in a crate that has
+    // specialization enabled, since it's not being specialized.
+    //
+    // rustc also checks whether the specializing impls comes from a macro marked
+    // `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]`
+    // is an internal feature, std is not using it for specialization nor is likely to
+    // ever use it, and we don't have the span information necessary to replicate that.
+    let def_map = crate_def_map(db, module.krate());
+    if !def_map.is_unstable_feature_enabled(&sym::specialization)
+        && !def_map.is_unstable_feature_enabled(&sym::min_specialization)
+    {
+        return false;
+    }
+
+    let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
+
+    let specializing_impl_signature = db.impl_signature(specializing_impl_def_id);
+    let parent_impl_signature = db.impl_signature(parent_impl_def_id);
+
+    // We determine whether there's a subset relationship by:
+    //
+    // - replacing bound vars with placeholders in impl1,
+    // - assuming the where clauses for impl1,
+    // - instantiating impl2 with fresh inference variables,
+    // - unifying,
+    // - attempting to prove the where clauses for impl2
+    //
+    // The last three steps are encapsulated in `fulfill_implication`.
+    //
+    // See RFC 1210 for more details and justification.
+
+    // Currently we do not allow e.g., a negative impl to specialize a positive one
+    if specializing_impl_signature.is_negative() != parent_impl_signature.is_negative() {
+        return false;
+    }
+
+    // create a parameter environment corresponding to an identity instantiation of the specializing impl,
+    // i.e. the most generic instantiation of the specializing impl.
+    let param_env = db.trait_environment(specializing_impl_def_id.into()).env;
+
+    // Create an infcx, taking the predicates of the specializing impl as assumptions:
+    let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
+
+    let specializing_impl_trait_ref =
+        db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity();
+    let cause = &ObligationCause::dummy();
+    debug!(
+        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
+        param_env, specializing_impl_trait_ref, parent_impl_def_id
+    );
+
+    // Attempt to prove that the parent impl applies, given all of the above.
+
+    let mut ocx = ObligationCtxt::new(&infcx);
+
+    let parent_args = infcx.fresh_args_for_item(parent_impl_def_id.into());
+    let parent_impl_trait_ref = db
+        .impl_trait(parent_impl_def_id)
+        .expect("expected source impl to be a trait impl")
+        .instantiate(interner, parent_args);
+
+    // do the impls unify? If not, no specialization.
+    let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
+    else {
+        return false;
+    };
+
+    // Now check that the source trait ref satisfies all the where clauses of the target impl.
+    // This is not just for correctness; we also need this to constrain any params that may
+    // only be referenced via projection predicates.
+    if let Some(predicates) =
+        db.generic_predicates(parent_impl_def_id.into()).instantiate(interner, parent_args)
+    {
+        ocx.register_obligations(
+            predicates
+                .map(|predicate| Obligation::new(interner, cause.clone(), param_env, predicate)),
+        );
+    }
+
+    let errors = ocx.evaluate_obligations_error_on_ambiguity();
+    if !errors.is_empty() {
+        // no dice!
+        debug!(
+            "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+            specializing_impl_trait_ref, parent_impl_trait_ref, errors, param_env
+        );
+        return false;
+    }
+
+    // FIXME: Check impl constness (when we implement const impls).
+
+    debug!(
+        "fulfill_implication: an impl for {:?} specializes {:?}",
+        specializing_impl_trait_ref, parent_impl_trait_ref
+    );
+
+    true
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 38af7cb..c2392b3 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3856,9 +3856,9 @@
             74..75 'f': F
             80..82 '{}': ()
             94..191 '{     ... }); }': ()
-            100..113 'async_closure': fn async_closure<impl FnOnce(i32)>(impl FnOnce(i32))
+            100..113 'async_closure': fn async_closure<impl AsyncFnOnce(i32)>(impl AsyncFnOnce(i32))
             100..147 'async_...    })': ()
-            114..146 'async ...     }': impl FnOnce(i32)
+            114..146 'async ...     }': impl AsyncFnOnce(i32)
             121..124 'arg': i32
             126..146 '{     ...     }': ()
             136..139 'arg': i32
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 0cf723e..f72ca22 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -85,7 +85,6 @@
 }
 
 #[test]
-#[ignore = "FIXME(next-solver): fix async closures"]
 fn infer_async_closure() {
     check_types(
         r#"
@@ -93,7 +92,7 @@
 async fn test() {
     let f = async move |x: i32| x + 42;
     f;
-//  ^ impl Fn(i32) -> impl Future<Output = i32>
+//  ^ impl AsyncFn(i32) -> i32
     let a = f(4);
     a;
 //  ^ impl Future<Output = i32>
@@ -102,7 +101,7 @@
 //  ^ i32
     let f = async move || 42;
     f;
-//  ^ impl Fn() -> impl Future<Output = i32>
+//  ^ impl AsyncFn() -> i32
     let a = f();
     a;
 //  ^ impl Future<Output = i32>
@@ -119,7 +118,7 @@
     };
     let _: Option<u64> = c().await;
     c;
-//  ^ impl Fn() -> impl Future<Output = Option<u64>>
+//  ^ impl AsyncFn() -> Option<u64>
 }
 "#,
     );
@@ -4930,7 +4929,6 @@
 
 #[test]
 fn async_fn_return_type() {
-    // FIXME(next-solver): Async closures are lowered as closures currently. We should fix that.
     check_infer(
         r#"
 //- minicore: async_fn
@@ -4948,9 +4946,9 @@
             46..53 'loop {}': !
             51..53 '{}': ()
             67..97 '{     ...()); }': ()
-            73..76 'foo': fn foo<impl Fn(), ()>(impl Fn())
+            73..76 'foo': fn foo<impl AsyncFn(), ()>(impl AsyncFn())
             73..94 'foo(as...|| ())': ()
-            77..93 'async ... || ()': impl Fn()
+            77..93 'async ... || ()': impl AsyncFn()
             91..93 '()': ()
         "#]],
     );
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 9418903..2bb2f80 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -75,7 +75,7 @@
     TraitEnvironment, TyDefId, TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef,
     check_orphan_rules,
     consteval::try_const_usize,
-    db::InternedClosureId,
+    db::{InternedClosureId, InternedCoroutineId},
     diagnostics::BodyValidationDiagnostic,
     direct_super_traits, known_const_to_ast,
     layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
@@ -92,7 +92,7 @@
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
     AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitor,
-    inherent::{AdtDef, IntoKind, SliceLike, Term as _, Ty as _},
+    inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
 };
 use smallvec::SmallVec;
 use span::{AstIdNode, Edition, FileId};
@@ -4558,16 +4558,27 @@
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+enum AnyClosureId {
+    ClosureId(InternedClosureId),
+    CoroutineClosureId(InternedCoroutineId),
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Closure<'db> {
-    id: InternedClosureId,
+    id: AnyClosureId,
     subst: GenericArgs<'db>,
 }
 
 impl<'db> Closure<'db> {
     fn as_ty(&self, db: &'db dyn HirDatabase) -> Ty<'db> {
         let interner = DbInterner::new_with(db, None, None);
-        Ty::new_closure(interner, self.id.into(), self.subst)
+        match self.id {
+            AnyClosureId::ClosureId(id) => Ty::new_closure(interner, id.into(), self.subst),
+            AnyClosureId::CoroutineClosureId(id) => {
+                Ty::new_coroutine_closure(interner, id.into(), self.subst)
+            }
+        }
     }
 
     pub fn display_with_id(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
@@ -4585,20 +4596,28 @@
     }
 
     pub fn captured_items(&self, db: &'db dyn HirDatabase) -> Vec<ClosureCapture<'db>> {
-        let owner = db.lookup_intern_closure(self.id).0;
+        let AnyClosureId::ClosureId(id) = self.id else {
+            // FIXME: Infer coroutine closures' captures.
+            return Vec::new();
+        };
+        let owner = db.lookup_intern_closure(id).0;
         let infer = db.infer(owner);
-        let info = infer.closure_info(self.id);
+        let info = infer.closure_info(id);
         info.0
             .iter()
             .cloned()
-            .map(|capture| ClosureCapture { owner, closure: self.id, capture })
+            .map(|capture| ClosureCapture { owner, closure: id, capture })
             .collect()
     }
 
     pub fn capture_types(&self, db: &'db dyn HirDatabase) -> Vec<Type<'db>> {
-        let owner = db.lookup_intern_closure(self.id).0;
+        let AnyClosureId::ClosureId(id) = self.id else {
+            // FIXME: Infer coroutine closures' captures.
+            return Vec::new();
+        };
+        let owner = db.lookup_intern_closure(id).0;
         let infer = db.infer(owner);
-        let (captures, _) = infer.closure_info(self.id);
+        let (captures, _) = infer.closure_info(id);
         let env = db.trait_environment_for_body(owner);
         captures
             .iter()
@@ -4607,10 +4626,22 @@
     }
 
     pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
-        let owner = db.lookup_intern_closure(self.id).0;
-        let infer = db.infer(owner);
-        let info = infer.closure_info(self.id);
-        info.1
+        match self.id {
+            AnyClosureId::ClosureId(id) => {
+                let owner = db.lookup_intern_closure(id).0;
+                let infer = db.infer(owner);
+                let info = infer.closure_info(id);
+                info.1
+            }
+            AnyClosureId::CoroutineClosureId(_id) => {
+                // FIXME: Infer kind for coroutine closures.
+                match self.subst.as_coroutine_closure().kind() {
+                    rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
+                    rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
+                    rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
+                }
+            }
+        }
     }
 }
 
@@ -5124,28 +5155,14 @@
         let interner = DbInterner::new_with(db, None, None);
         let callee = match self.ty.kind() {
             TyKind::Closure(id, subst) => Callee::Closure(id.0, subst),
+            TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst),
             TyKind::FnPtr(..) => Callee::FnPtr,
             TyKind::FnDef(id, _) => Callee::Def(id.0),
-            kind => {
-                // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment
-                let (ty, kind) = if let TyKind::Ref(_, ty, _) = kind {
-                    (ty, ty.kind())
-                } else {
-                    (self.ty, kind)
-                };
-                if let TyKind::Closure(closure, subst) = kind {
-                    let sig = subst
-                        .split_closure_args_untupled()
-                        .closure_sig_as_fn_ptr_ty
-                        .callable_sig(interner)?;
-                    return Some(Callable {
-                        ty: self.clone(),
-                        sig,
-                        callee: Callee::Closure(closure.0, subst),
-                        is_bound_method: false,
-                    });
-                }
-                let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(ty, self.env.clone(), db)?;
+            // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment
+            TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db),
+            _ => {
+                let (fn_trait, sig) =
+                    hir_ty::callable_sig_from_fn_trait(self.ty, self.env.clone(), db)?;
                 return Some(Callable {
                     ty: self.clone(),
                     sig,
@@ -5165,7 +5182,12 @@
 
     pub fn as_closure(&self) -> Option<Closure<'db>> {
         match self.ty.kind() {
-            TyKind::Closure(id, subst) => Some(Closure { id: id.0, subst }),
+            TyKind::Closure(id, subst) => {
+                Some(Closure { id: AnyClosureId::ClosureId(id.0), subst })
+            }
+            TyKind::CoroutineClosure(id, subst) => {
+                Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst })
+            }
             _ => None,
         }
     }
@@ -5824,6 +5846,7 @@
 enum Callee<'db> {
     Def(CallableDefId),
     Closure(InternedClosureId, GenericArgs<'db>),
+    CoroutineClosure(InternedCoroutineId, GenericArgs<'db>),
     FnPtr,
     FnImpl(FnTrait),
 }
@@ -5845,7 +5868,12 @@
             Callee::Def(CallableDefId::EnumVariantId(it)) => {
                 CallableKind::TupleEnumVariant(it.into())
             }
-            Callee::Closure(id, ref subst) => CallableKind::Closure(Closure { id, subst: *subst }),
+            Callee::Closure(id, subst) => {
+                CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst })
+            }
+            Callee::CoroutineClosure(id, subst) => {
+                CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst })
+            }
             Callee::FnPtr => CallableKind::FnPtr,
             Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_),
         }
diff --git a/crates/ide-assists/src/handlers/add_braces.rs b/crates/ide-assists/src/handlers/add_braces.rs
index d855fb7..99ee50f 100644
--- a/crates/ide-assists/src/handlers/add_braces.rs
+++ b/crates/ide-assists/src/handlers/add_braces.rs
@@ -1,14 +1,15 @@
 use either::Either;
 use syntax::{
-    AstNode,
+    AstNode, T,
     ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+    match_ast,
 };
 
 use crate::{AssistContext, AssistId, Assists};
 
 // Assist: add_braces
 //
-// Adds braces to closure bodies and match arm expressions.
+// Adds braces to closure bodies, match arm expressions and assignment bodies.
 //
 // ```
 // fn foo(n: i32) -> i32 {
@@ -29,6 +30,20 @@
 //     }
 // }
 // ```
+// ---
+// ```
+// fn foo(n: i32) -> i32 {
+//     let x =$0 n + 2;
+// }
+// ```
+// ->
+// ```
+// fn foo(n: i32) -> i32 {
+//     let x = {
+//         n + 2
+//     };
+// }
+// ```
 pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let (expr_type, expr) = get_replacement_node(ctx)?;
 
@@ -37,6 +52,7 @@
         match expr_type {
             ParentType::ClosureExpr => "Add braces to this closure body",
             ParentType::MatchArmExpr => "Add braces to this match arm expression",
+            ParentType::Assignment => "Add braces to this assignment expression",
         },
         expr.syntax().text_range(),
         |builder| {
@@ -57,29 +73,38 @@
 enum ParentType {
     MatchArmExpr,
     ClosureExpr,
+    Assignment,
 }
 
 fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> {
-    let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>()?;
-    if let Either::Left(match_arm) = &node {
+    let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>();
+    let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) {
+        let parent = eq_token.parent()?;
+        let body = match_ast! {
+            match parent {
+                ast::LetStmt(it) => it.initializer()?,
+                ast::LetExpr(it) => it.expr()?,
+                ast::Static(it) => it.body()?,
+                ast::Const(it) => it.body()?,
+                _ => return None,
+            }
+        };
+        (ParentType::Assignment, body)
+    } else if let Some(Either::Left(match_arm)) = &node {
         let match_arm_expr = match_arm.expr()?;
-
-        if matches!(match_arm_expr, ast::Expr::BlockExpr(_)) {
-            return None;
-        }
-
-        return Some((ParentType::MatchArmExpr, match_arm_expr));
-    } else if let Either::Right(closure_expr) = &node {
+        (ParentType::MatchArmExpr, match_arm_expr)
+    } else if let Some(Either::Right(closure_expr)) = &node {
         let body = closure_expr.body()?;
+        (ParentType::ClosureExpr, body)
+    } else {
+        return None;
+    };
 
-        if matches!(body, ast::Expr::BlockExpr(_)) {
-            return None;
-        }
-
-        return Some((ParentType::ClosureExpr, body));
+    if matches!(body, ast::Expr::BlockExpr(_)) {
+        return None;
     }
 
-    None
+    Some((parent_type, body))
 }
 
 #[cfg(test)]
@@ -135,6 +160,25 @@
     }
 
     #[test]
+    fn suggest_add_braces_for_assignment() {
+        check_assist(
+            add_braces,
+            r#"
+fn foo() {
+    let x =$0 n + 100;
+}
+"#,
+            r#"
+fn foo() {
+    let x = {
+        n + 100
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
     fn no_assist_for_closures_with_braces() {
         check_assist_not_applicable(
             add_braces,
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 2cda6d6..ca14233 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -805,7 +805,6 @@
         );
     }
 
-    #[ignore = "FIXME(next-solver): Fix async closures"]
     #[test]
     fn replaces_async_closure_with_async_fn() {
         check_assist(
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index c7ae441..e8582aa 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -28,6 +28,25 @@
 }
 
 #[test]
+fn doctest_add_braces_1() {
+    check_doc_test(
+        "add_braces",
+        r#####"
+fn foo(n: i32) -> i32 {
+    let x =$0 n + 2;
+}
+"#####,
+        r#####"
+fn foo(n: i32) -> i32 {
+    let x = {
+        n + 2
+    };
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_add_explicit_enum_discriminant() {
     check_doc_test(
         "add_explicit_enum_discriminant",
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index 0ce81d0..dcddc24 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -42,6 +42,11 @@
         }
     }
 
+    if pattern_ctx.after_if_expr {
+        add_keyword("else", "else {\n    $0\n}");
+        add_keyword("else if", "else if $1 {\n    $0\n}");
+    }
+
     if pattern_ctx.record_pat.is_some() {
         return;
     }
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 91f19f6..2f166b7 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -279,6 +279,7 @@
     pub(crate) param_ctx: Option<ParamContext>,
     pub(crate) has_type_ascription: bool,
     pub(crate) should_suggest_name: bool,
+    pub(crate) after_if_expr: bool,
     pub(crate) parent_pat: Option<ast::Pat>,
     pub(crate) ref_token: Option<SyntaxToken>,
     pub(crate) mut_token: Option<SyntaxToken>,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index d39bff1..42772ef 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -999,10 +999,6 @@
             }
         }
     };
-    let after_if_expr = |node: SyntaxNode| {
-        let prev_expr = prev_expr(node);
-        matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
-    };
     let after_incomplete_let = |node: SyntaxNode| {
         prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
     };
@@ -1242,7 +1238,7 @@
         let it = expr.syntax();
         let in_block_expr = is_in_block(it);
         let (in_loop_body, innermost_breakable) = is_in_breakable(it).unzip();
-        let after_if_expr = after_if_expr(it.clone());
+        let after_if_expr = is_after_if_expr(it.clone());
         let ref_expr_parent =
             path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
         let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
@@ -1763,6 +1759,7 @@
         param_ctx,
         has_type_ascription,
         should_suggest_name,
+        after_if_expr: is_after_if_expr(pat.syntax().clone()),
         parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
         mut_token,
         ref_token,
@@ -1933,6 +1930,18 @@
     }
 }
 
+fn is_after_if_expr(node: SyntaxNode) -> bool {
+    let node = match node.parent().and_then(Either::<ast::ExprStmt, ast::MatchArm>::cast) {
+        Some(stmt) => stmt.syntax().clone(),
+        None => node,
+    };
+    let prev_sibling =
+        non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node);
+    iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node())
+        .find_map(ast::IfExpr::cast)
+        .is_some()
+}
+
 fn next_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> {
     let mut token = match e.into() {
         SyntaxElement::Node(n) => n.last_token()?,
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index b32a895..83606d2 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -246,11 +246,10 @@
     let (db, position) = position(ra_fixture_before);
     let completions: Vec<CompletionItem> =
         hir::attach_db(&db, || crate::completions(&db, &config, position, None).unwrap());
-    let (completion,) = completions
-        .iter()
-        .filter(|it| it.lookup() == what)
-        .collect_tuple()
-        .unwrap_or_else(|| panic!("can't find {what:?} completion in {completions:#?}"));
+    let Some((completion,)) = completions.iter().filter(|it| it.lookup() == what).collect_tuple()
+    else {
+        panic!("can't find {what:?} completion in {completions:#?}")
+    };
     let mut actual = db.file_text(position.file_id).text(&db).to_string();
 
     let mut combined_edit = completion.text_edit.clone();
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 09af635..67c84f4 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1869,6 +1869,298 @@
             sn ppd
         "#]],
     );
+    check(
+        r#"
+fn foo() { [if foo {} $0]}
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { [if foo {} el$0]}
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { 2 + if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { -if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { &mut if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { return if foo {} $0 }
+"#,
+        expect![[r#"
+            fn foo()  fn()
+            bt u32     u32
+            kw async
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw impl for
+            kw let
+            kw letm
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0 } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0, } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    check(
+        r#"
+fn foo() { match () { () => if foo {} $0, _ => (), } }
+"#,
+        expect![[r#"
+            kw else
+            kw else if
+            kw mut
+            kw ref
+        "#]],
+    );
+    // FIXME: support else completion after ast::RecordExprField
 }
 
 #[test]
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 920bdd9..756377f 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -517,4 +517,6 @@
     precision,
     width,
     never_type_fallback,
+    specialization,
+    min_specialization,
 }