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)), - ¶m, - )], - ) - } } } _ => 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, }