diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bd1e8278d5..54f35cf0a6 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2440,7 +2440,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug)] pub struct Type { - krate: CrateId, + krate: CrateId, // FIXME this is probably redundant with the TraitEnvironment env: Arc, ty: Ty, } @@ -2533,12 +2533,9 @@ impl Type { /// Checks that particular type `ty` implements `std::future::Future`. /// This function is used in `.await` syntax completion. pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { - // No special case for the type of async block, since Chalk can figure it out. - - let krate = self.krate; - - let std_future_trait = - db.lang_item(krate, SmolStr::new_inline("future_trait")).and_then(|it| it.as_trait()); + let std_future_trait = db + .lang_item(self.krate, SmolStr::new_inline("future_trait")) + .and_then(|it| it.as_trait()); let std_future_trait = match std_future_trait { Some(it) => it, None => return false, @@ -2546,13 +2543,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait( - &canonical_ty, - db, - self.env.clone(), - krate, - std_future_trait, - ) + method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. @@ -2560,9 +2551,7 @@ impl Type { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { - let krate = self.krate; - - let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { + let fnonce_trait = match FnTrait::FnOnce.get_id(db, self.krate) { Some(it) => it, None => return false, }; @@ -2573,7 +2562,6 @@ impl Type { &canonical_ty, db, self.env.clone(), - krate, fnonce_trait, ) } @@ -2744,9 +2732,8 @@ impl Type { pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let environment = self.env.env.clone(); - let ty = InEnvironment { goal: canonical, environment }; - autoderef(db, Some(self.krate), ty).map(|canonical| canonical.value) + let environment = self.env.clone(); + autoderef(db, environment, canonical).map(|canonical| canonical.value) } // This would be nicer if it just returned an iterator, but that runs into @@ -2801,24 +2788,26 @@ impl Type { pub fn iterate_method_candidates( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, + // FIXME this can be retrieved from `scope`, except autoimport uses this + // to specify a different set, so the method needs to be split traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - mut callback: impl FnMut(Type, Function) -> Option, + mut callback: impl FnMut(Function) -> Option, ) -> Option { let _p = profile::span("iterate_method_candidates"); let mut slot = None; self.iterate_method_candidates_dyn( db, - krate, + scope, traits_in_scope, with_local_impls, name, - &mut |ty, assoc_item_id| { + &mut |assoc_item_id| { if let AssocItemId::FunctionId(func) = assoc_item_id { - if let Some(res) = callback(self.derived(ty.clone()), func.into()) { + if let Some(res) = callback(func.into()) { slot = Some(res); return ControlFlow::Break(()); } @@ -2832,50 +2821,55 @@ impl Type { fn iterate_method_candidates_dyn( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, ) { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let env = self.env.clone(); - let krate = krate.id; + let krate = match scope.krate() { + Some(k) => k, + None => return, + }; + let environment = scope.resolver().generic_def().map_or_else( + || Arc::new(TraitEnvironment::empty(krate.id)), + |d| db.trait_environment(d), + ); method_resolution::iterate_method_candidates_dyn( &canonical, db, - env, - krate, + environment, traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::MethodCall, - &mut |ty, id| callback(&ty.value, id), + &mut |_adj, id| callback(id), ); } pub fn iterate_path_candidates( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - mut callback: impl FnMut(Type, AssocItem) -> Option, + mut callback: impl FnMut(AssocItem) -> Option, ) -> Option { let _p = profile::span("iterate_path_candidates"); let mut slot = None; self.iterate_path_candidates_dyn( db, - krate, + scope, traits_in_scope, with_local_impls, name, - &mut |ty, assoc_item_id| { - if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) { + &mut |assoc_item_id| { + if let Some(res) = callback(assoc_item_id.into()) { slot = Some(res); return ControlFlow::Break(()); } @@ -2888,27 +2882,31 @@ impl Type { fn iterate_path_candidates_dyn( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, ) { let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let env = self.env.clone(); - let krate = krate.id; + let krate = match scope.krate() { + Some(k) => k, + None => return, + }; + let environment = scope.resolver().generic_def().map_or_else( + || Arc::new(TraitEnvironment::empty(krate.id)), + |d| db.trait_environment(d), + ); - method_resolution::iterate_method_candidates_dyn( + method_resolution::iterate_path_candidates( &canonical, db, - env, - krate, + environment, traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, - method_resolution::LookupMode::Path, - &mut |ty, id| callback(&ty.value, id), + &mut |id| callback(id), ); } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 20c360e302..2e0dbf82b7 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1230,6 +1230,10 @@ impl<'a> SemanticsScope<'a> { Some(Crate { id: self.resolver.krate()? }) } + pub(crate) fn resolver(&self) -> &Resolver { + &self.resolver + } + /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type pub fn visible_traits(&self) -> FxHashSet { let resolver = &self.resolver; diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 6266554ecf..dffb36b5de 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -3,20 +3,16 @@ //! reference to a type with the field `bar`. This is an approximation of the //! logic in rustc (which lives in librustc_typeck/check/autoderef.rs). -use std::iter::successors; +use std::sync::Arc; -use base_db::CrateId; -use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind}; -use hir_def::lang_item::LangItemTarget; +use chalk_ir::cast::Cast; use hir_expand::name::name; use limit::Limit; use syntax::SmolStr; -use tracing::{info, warn}; use crate::{ - db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, - ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner, - ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind, + db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt, + TraitEnvironment, Ty, TyBuilder, TyKind, }; static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); @@ -26,40 +22,34 @@ pub(crate) enum AutoderefKind { Overloaded, } -pub(crate) struct Autoderef<'db> { - db: &'db dyn HirDatabase, - ty: Canonical, +pub(crate) struct Autoderef<'a, 'db> { + pub(crate) table: &'a mut InferenceTable<'db>, + ty: Ty, at_start: bool, - krate: Option, - environment: Environment, steps: Vec<(AutoderefKind, Ty)>, } -impl<'db> Autoderef<'db> { - pub(crate) fn new( - db: &'db dyn HirDatabase, - krate: Option, - ty: InEnvironment>, - ) -> Self { - let InEnvironment { goal: ty, environment } = ty; - Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() } +impl<'a, 'db> Autoderef<'a, 'db> { + pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self { + let ty = table.resolve_ty_shallow(&ty); + Autoderef { table, ty, at_start: true, steps: Vec::new() } } pub(crate) fn step_count(&self) -> usize { self.steps.len() } - pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty)] { + pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { &self.steps } pub(crate) fn final_ty(&self) -> Ty { - self.ty.value.clone() + self.ty.clone() } } -impl Iterator for Autoderef<'_> { - type Item = (Canonical, usize); +impl Iterator for Autoderef<'_, '_> { + type Item = (Ty, usize); fn next(&mut self) -> Option { if self.at_start { @@ -71,54 +61,42 @@ impl Iterator for Autoderef<'_> { return None; } - let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) { - ( - AutoderefKind::Builtin, - Canonical { value: derefed.clone(), binders: self.ty.binders.clone() }, - ) - } else { - ( - AutoderefKind::Overloaded, - deref_by_trait( - self.db, - self.krate?, - InEnvironment { goal: &self.ty, environment: self.environment.clone() }, - )?, - ) - }; + let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?; - self.steps.push((kind, self.ty.value.clone())); + self.steps.push((kind, self.ty.clone())); self.ty = new_ty; Some((self.ty.clone(), self.step_count())) } } +pub(crate) fn autoderef_step(table: &mut InferenceTable, ty: Ty) -> Option<(AutoderefKind, Ty)> { + if let Some(derefed) = builtin_deref(&ty) { + Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) + } else { + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + } +} + // FIXME: replace uses of this with Autoderef above pub fn autoderef<'a>( db: &'a dyn HirDatabase, - krate: Option, - ty: InEnvironment>, + env: Arc, + ty: Canonical, ) -> impl Iterator> + 'a { - let InEnvironment { goal: ty, environment } = ty; - successors(Some(ty), move |ty| { - deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() }) - }) - .take(AUTODEREF_RECURSION_LIMIT.inner()) + let mut table = InferenceTable::new(db, env); + let ty = table.instantiate_canonical(ty); + let mut autoderef = Autoderef::new(&mut table, ty); + let mut v = Vec::new(); + while let Some((ty, _steps)) = autoderef.next() { + v.push(autoderef.table.canonicalize(ty).value); + } + v.into_iter() } -pub(crate) fn deref( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment<&Canonical>, -) -> Option> { +pub(crate) fn deref(table: &mut InferenceTable, ty: Ty) -> Option { let _p = profile::span("deref"); - match builtin_deref(&ty.goal.value) { - Some(derefed) => { - Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() }) - } - None => deref_by_trait(db, krate, ty), - } + autoderef_step(table, ty).map(|(_, ty)| ty) } fn builtin_deref(ty: &Ty) -> Option<&Ty> { @@ -129,16 +107,12 @@ fn builtin_deref(ty: &Ty) -> Option<&Ty> { } } -fn deref_by_trait( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment<&Canonical>, -) -> Option> { +fn deref_by_trait(table: &mut InferenceTable, ty: Ty) -> Option { + let db = table.db; let _p = profile::span("deref_by_trait"); - let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? { - LangItemTarget::TraitId(it) => it, - _ => return None, - }; + let deref_trait = db + .lang_item(table.trait_env.krate, SmolStr::new_inline("deref")) + .and_then(|l| l.as_trait())?; let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?; let projection = { @@ -148,114 +122,16 @@ fn deref_by_trait( // namely Deref's Self type return None; } - b.push(ty.goal.value.clone()).build() + b.push(ty).build() }; - // FIXME make the Canonical / bound var handling nicer - // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); - let implements_goal = Canonical { - binders: ty.goal.binders.clone(), - value: InEnvironment { - goal: trait_ref.cast(Interner), - environment: ty.environment.clone(), - }, - }; - if db.trait_solve(krate, implements_goal).is_none() { - return None; - } + let implements_goal: Goal = trait_ref.cast(Interner); + table.try_obligation(implements_goal.clone())?; - // Now do the assoc type projection - let alias_eq = AliasEq { - alias: AliasTy::Projection(projection), - ty: TyKind::BoundVar(BoundVar::new( - DebruijnIndex::INNERMOST, - ty.goal.binders.len(Interner), - )) - .intern(Interner), - }; + table.register_obligation(implements_goal); - let in_env = InEnvironment { goal: alias_eq.cast(Interner), environment: ty.environment }; - - let canonical = Canonical { - value: in_env, - binders: CanonicalVarKinds::from_iter( - Interner, - ty.goal.binders.iter(Interner).cloned().chain(Some(chalk_ir::WithKind::new( - VariableKind::Ty(chalk_ir::TyVariableKind::General), - chalk_ir::UniverseIndex::ROOT, - ))), - ), - }; - - let solution = db.trait_solve(krate, canonical)?; - - match &solution { - Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders }) - | Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => { - // FIXME: vars may contain solutions for any inference variables - // that happened to be inside ty. To correctly handle these, we - // would have to pass the solution up to the inference context, but - // that requires a larger refactoring (especially if the deref - // happens during method resolution). So for the moment, we just - // check that we're not in the situation where we would actually - // need to handle the values of the additional variables, i.e. - // they're just being 'passed through'. In the 'standard' case where - // we have `impl Deref for Foo { Target = T }`, that should be - // the case. - - // FIXME: if the trait solver decides to truncate the type, these - // assumptions will be broken. We would need to properly introduce - // new variables in that case - - for i in 1..binders.len(Interner) { - if subst.at(Interner, i - 1).assert_ty_ref(Interner).kind(Interner) - != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) - { - warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); - return None; - } - } - // FIXME: we remove lifetime variables here since they can confuse - // the method resolution code later - Some(fixup_lifetime_variables(Canonical { - value: subst.at(Interner, subst.len(Interner) - 1).assert_ty_ref(Interner).clone(), - binders: binders.clone(), - })) - } - Solution::Ambig(_) => { - info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); - None - } - } -} - -fn fixup_lifetime_variables + HasInterner>( - c: Canonical, -) -> Canonical { - // Removes lifetime variables from the Canonical, replacing them by static lifetimes. - let mut i = 0; - let subst = Substitution::from_iter( - Interner, - c.binders.iter(Interner).map(|vk| match vk.kind { - VariableKind::Ty(_) => { - let index = i; - i += 1; - BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(Interner).cast(Interner) - } - VariableKind::Lifetime => static_lifetime().cast(Interner), - VariableKind::Const(_) => unimplemented!(), - }), - ); - let binders = CanonicalVarKinds::from_iter( - Interner, - c.binders.iter(Interner).filter(|vk| match vk.kind { - VariableKind::Ty(_) => true, - VariableKind::Lifetime => false, - VariableKind::Const(_) => true, - }), - ); - let value = subst.apply(c.value, Interner); - Canonical { binders, value } + let result = table.normalize_projection_ty(projection); + Some(table.resolve_ty_shallow(&result)) } diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0a50df493f..c84604d69b 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -13,8 +13,8 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. +use std::ops::Index; use std::sync::Arc; -use std::{collections::hash_map::Entry, ops::Index}; use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use hir_def::{ @@ -46,7 +46,7 @@ use crate::{ pub use unify::could_unify; pub(crate) use unify::unify; -mod unify; +pub(crate) mod unify; mod path; mod expr; mod pat; @@ -228,7 +228,7 @@ pub enum Adjust { /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct OverloadedDeref(Mutability); +pub struct OverloadedDeref(pub Mutability); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AutoBorrow { @@ -455,16 +455,6 @@ impl<'a> InferenceContext<'a> { self.result.method_resolutions.insert(expr, (func, subst)); } - fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) { - self.result.field_resolutions.insert(expr, field); - } - - fn write_field_resolution_if_empty(&mut self, expr: ExprId, field: FieldId) { - if let Entry::Vacant(entry) = self.result.field_resolutions.entry(expr) { - entry.insert(field); - } - } - fn write_variant_resolution(&mut self, id: ExprOrPatId, variant: VariantId) { self.result.variant_resolutions.insert(id, variant); } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index bddb79c501..c24772f29b 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -259,27 +259,19 @@ impl<'a> InferenceContext<'a> { // details of coercion errors though, so I think it's useful to leave // the structure like it is. - let canonicalized = self.canonicalize(from_ty.clone()); - let mut autoderef = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), - }, - ); + let snapshot = self.table.snapshot(); + + let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone()); let mut first_error = None; let mut found = None; - for (referent_ty, autoderefs) in autoderef.by_ref() { + while let Some((referent_ty, autoderefs)) = autoderef.next() { if autoderefs == 0 { // Don't let this pass, otherwise it would cause // &T to autoref to &&T. continue; } - let referent_ty = canonicalized.decanonicalize_ty(&mut self.table, referent_ty); - // At this point, we have deref'd `a` to `referent_ty`. So // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. // In the autoderef loop for `&'a mut Vec`, we would get @@ -304,7 +296,7 @@ impl<'a> InferenceContext<'a> { // from `&mut T` to `&U`. let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner); - match self.table.try_unify(&derefd_from_ty, to_ty) { + match autoderef.table.try_unify(&derefd_from_ty, to_ty) { Ok(result) => { found = Some(result.map(|()| derefd_from_ty)); break; @@ -325,6 +317,7 @@ impl<'a> InferenceContext<'a> { let InferOk { value: ty, goals } = match found { Some(d) => d, None => { + self.table.rollback_to(snapshot); let err = first_error.expect("coerce_borrowed_pointer had no error"); return Err(err); } @@ -345,29 +338,13 @@ impl<'a> InferenceContext<'a> { return success(vec![], ty, goals); } - let mut adjustments = self.auto_deref_adjust_steps(&autoderef); + let mut adjustments = auto_deref_adjust_steps(&autoderef); adjustments .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }); success(adjustments, ty, goals) } - pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec { - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); - steps - .iter() - .map(|(kind, _source)| match kind { - // We do not know what kind of deref we require at this point yet - AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), - AutoderefKind::Builtin => None, - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect() - } - /// Attempts to coerce from the type of a Rust function item into a function pointer. fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult { match to_ty.kind(Interner) { @@ -620,3 +597,19 @@ fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError (Mutability::Not, Mutability::Mut) => Err(TypeError), } } + +pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec { + let steps = autoderef.steps(); + let targets = + steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); + steps + .iter() + .map(|(kind, _source)| match kind { + // We do not know what kind of deref we require at this point yet + AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), + AutoderefKind::Builtin => None, + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) + .collect() +} diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index b08a9618d8..c305af2e91 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -1,6 +1,7 @@ //! Type inference for expressions. use std::{ + collections::hash_map::Entry, iter::{repeat, repeat_with}, mem, sync::Arc, @@ -26,15 +27,14 @@ use crate::{ method_resolution, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, - traits::FnTrait, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, - ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, + Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ - find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, - InferenceDiagnostic, TypeMismatch, + coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges, + Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, }; impl<'a> InferenceContext<'a> { @@ -77,51 +77,6 @@ impl<'a> InferenceContext<'a> { } } - fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { - let krate = self.resolver.krate()?; - let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; - let output_assoc_type = - self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - - let mut arg_tys = vec![]; - let arg_ty = TyBuilder::tuple(num_args) - .fill(repeat_with(|| { - let arg = self.table.new_type_var(); - arg_tys.push(arg.clone()); - arg - })) - .build(); - - let projection = { - let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type); - if b.remaining() != 2 { - return None; - } - b.push(ty.clone()).push(arg_ty).build() - }; - - let trait_env = self.trait_env.env.clone(); - let obligation = InEnvironment { - goal: projection.trait_ref(self.db).cast(Interner), - environment: trait_env, - }; - let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { - self.push_obligation(obligation.goal); - let return_ty = self.table.normalize_projection_ty(projection); - Some((arg_tys, return_ty)) - } else { - None - } - } - - pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { - match ty.callable_sig(self.db) { - Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())), - None => self.callable_sig_from_fn_trait(ty, num_args), - } - } - fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { self.db.unwind_if_cancelled(); @@ -319,22 +274,19 @@ impl<'a> InferenceContext<'a> { } Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); - let canonicalized = self.canonicalize(callee_ty.clone()); - let mut derefs = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.table.trait_env.env.clone(), - }, - ); - let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| { - let ty = &canonicalized.decanonicalize_ty(&mut self.table, callee_deref_ty); - self.callable_sig(ty, args.len()) - }); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone()); + let mut res = None; + // manual loop to be able to access `derefs.table` + while let Some((callee_deref_ty, _)) = derefs.next() { + res = derefs.table.callable_sig(&callee_deref_ty, args.len()); + if res.is_some() { + break; + } + } let (param_tys, ret_ty): (Vec, Ty) = match res { Some(res) => { - self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs)); + let adjustments = auto_deref_adjust_steps(&derefs); + self.write_expr_adj(*callee, adjustments); res } None => (Vec::new(), self.err_ty()), @@ -489,88 +441,67 @@ impl<'a> InferenceContext<'a> { } Expr::Field { expr, name } => { let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); - let canonicalized = self.canonicalize(receiver_ty); - let mut autoderef = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), - }, - ); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty); let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| { - let module = self.resolver.module(); - let db = self.db; - let is_visible = |field_id: &FieldId| { - module - .map(|mod_id| { - db.field_visibilities(field_id.parent)[field_id.local_id] - .is_visible_from(db.upcast(), mod_id) - }) - .unwrap_or(true) - }; - match canonicalized - .decanonicalize_ty(&mut self.table, derefed_ty) - .kind(Interner) - { - TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| { - substs - .as_slice(Interner) - .get(idx) - .map(|a| a.assert_ty_ref(Interner)) - .cloned() - }), + let (field_id, parameters) = match derefed_ty.kind(Interner) { + TyKind::Tuple(_, substs) => { + return name.as_tuple_index().and_then(|idx| { + substs + .as_slice(Interner) + .get(idx) + .map(|a| a.assert_ty_ref(Interner)) + .cloned() + }); + } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { let local_id = self.db.struct_data(*s).variant_data.field(name)?; let field = FieldId { parent: (*s).into(), local_id }; - if is_visible(&field) { - self.write_field_resolution(tgt_expr, field); - Some( - self.db.field_types((*s).into())[field.local_id] - .clone() - .substitute(Interner, ¶meters), - ) - } else { - // Write down the first field resolution even if it is not visible - // This aids IDE features for private fields like goto def and in - // case of autoderef finding an applicable field, this will be - // overwritten in a following cycle - self.write_field_resolution_if_empty(tgt_expr, field); - None - } + (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { let local_id = self.db.union_data(*u).variant_data.field(name)?; let field = FieldId { parent: (*u).into(), local_id }; - if is_visible(&field) { - self.write_field_resolution(tgt_expr, field); - Some( - self.db.field_types((*u).into())[field.local_id] - .clone() - .substitute(Interner, ¶meters), - ) - } else { - // Write down the first field resolution even if it is not visible - // This aids IDE features for private fields like goto def and in - // case of autoderef finding an applicable field, this will be - // overwritten in a following cycle - self.write_field_resolution_if_empty(tgt_expr, field); - None - } + (field, parameters.clone()) } - _ => None, + _ => return None, + }; + let module = self.resolver.module(); + let is_visible = module + .map(|mod_id| { + self.db.field_visibilities(field_id.parent)[field_id.local_id] + .is_visible_from(self.db.upcast(), mod_id) + }) + .unwrap_or(true); + if !is_visible { + // Write down the first field resolution even if it is not visible + // This aids IDE features for private fields like goto def and in + // case of autoderef finding an applicable field, this will be + // overwritten in a following cycle + if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr) + { + entry.insert(field_id); + } + return None; } + // can't have `write_field_resolution` here because `self.table` is borrowed :( + self.result.field_resolutions.insert(tgt_expr, field_id); + let ty = self.db.field_types(field_id.parent)[field_id.local_id] + .clone() + .substitute(Interner, ¶meters); + Some(ty) }); let ty = match ty { Some(ty) => { - self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef)); + let adjustments = auto_deref_adjust_steps(&autoderef); + self.write_expr_adj(*expr, adjustments); + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); ty } - None => self.err_ty(), + _ => self.err_ty(), }; - let ty = self.insert_type_vars(ty); - self.normalize_associated_types_in(ty) + ty } Expr::Await { expr } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); @@ -628,25 +559,9 @@ impl<'a> InferenceContext<'a> { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); let inner_ty = self.resolve_ty_shallow(&inner_ty); match op { - UnaryOp::Deref => match self.resolver.krate() { - Some(krate) => { - let canonicalized = self.canonicalize(inner_ty); - match autoderef::deref( - self.db, - krate, - InEnvironment { - goal: &canonicalized.value, - environment: self.trait_env.env.clone(), - }, - ) { - Some(derefed_ty) => { - canonicalized.decanonicalize_ty(&mut self.table, derefed_ty) - } - None => self.err_ty(), - } - } - None => self.err_ty(), - }, + UnaryOp::Deref => { + autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty()) + } UnaryOp::Neg => { match inner_ty.kind(Interner) { // Fast path for builtins @@ -732,20 +647,19 @@ impl<'a> InferenceContext<'a> { let base_ty = self.infer_expr_inner(*base, &Expectation::none()); let index_ty = self.infer_expr(*index, &Expectation::none()); - if let (Some(index_trait), Some(krate)) = - (self.resolve_ops_index(), self.resolver.krate()) - { - let canonicalized = self.canonicalize(base_ty); - let self_ty = method_resolution::resolve_indexing_op( + if let Some(index_trait) = self.resolve_ops_index() { + let canonicalized = self.canonicalize(base_ty.clone()); + let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, - &canonicalized.value, self.trait_env.clone(), - krate, + canonicalized.value, index_trait, ); - let self_ty = self_ty.map_or(self.err_ty(), |t| { - canonicalized.decanonicalize_ty(&mut self.table, t) - }); + let (self_ty, adj) = receiver_adjustments + .map_or((self.err_ty(), Vec::new()), |adj| { + adj.apply(&mut self.table, base_ty) + }); + self.write_expr_adj(*base, adj); self.resolve_associated_type_with_params( self_ty, self.resolve_ops_index_output(), @@ -992,22 +906,20 @@ impl<'a> InferenceContext<'a> { let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); - let resolved = self.resolver.krate().and_then(|krate| { - method_resolution::lookup_method( - &canonicalized_receiver.value, - self.db, - self.trait_env.clone(), - krate, - &traits_in_scope, - self.resolver.module().into(), - method_name, - ) - }); + let resolved = method_resolution::lookup_method( + &canonicalized_receiver.value, + self.db, + self.trait_env.clone(), + &traits_in_scope, + self.resolver.module().into(), + method_name, + ); let (receiver_ty, method_ty, substs) = match resolved { - Some((ty, func)) => { - let ty = canonicalized_receiver.decanonicalize_ty(&mut self.table, ty); + Some((adjust, func)) => { + let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); let generics = generics(self.db.upcast(), func.into()); - let substs = self.substs_for_method_call(generics, generic_args, &ty); + let substs = self.substs_for_method_call(generics, generic_args); + self.write_expr_adj(receiver, adjustments); self.write_method_resolution(tgt_expr, func, substs.clone()); (ty, self.db.value_ty(func.into()), substs) } @@ -1120,20 +1032,15 @@ impl<'a> InferenceContext<'a> { &mut self, def_generics: Generics, generic_args: Option<&GenericArgs>, - receiver_ty: &Ty, ) -> Substitution { let (parent_params, self_params, type_params, impl_trait_params) = def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param let total_len = parent_params + type_params + impl_trait_params; let mut substs = Vec::with_capacity(total_len); - // Parent arguments are unknown, except for the receiver type - for (_id, param) in def_generics.iter_parent() { - if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { - substs.push(receiver_ty.clone()); - } else { - substs.push(self.table.new_type_var()); - } + // Parent arguments are unknown + for _ in def_generics.iter_parent() { + substs.push(self.table.new_type_var()); } // handle provided type arguments if let Some(generic_args) = generic_args { diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index b63ef2ffdc..0d6c8f12d2 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -218,14 +218,12 @@ impl<'a> InferenceContext<'a> { } let canonical_ty = self.canonicalize(ty.clone()); - let krate = self.resolver.krate()?; let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); method_resolution::iterate_method_candidates( &canonical_ty.value, self.db, self.table.trait_env.clone(), - krate, &traits_in_scope, self.resolver.module().into(), Some(name), diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index bb7cdb677e..21b48b9d80 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{fmt, mem, sync::Arc}; +use std::{fmt, iter, mem, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution, @@ -8,12 +8,14 @@ use chalk_ir::{ }; use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; +use hir_expand::name; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, Const, - DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, - ProjectionTy, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, + db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar, + Canonical, Const, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, + Interner, Lifetime, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -24,32 +26,20 @@ impl<'a> InferenceContext<'a> { where T::Result: HasInterner, { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.resolve_obligations_as_possible(); self.table.canonicalize(t) } } #[derive(Debug, Clone)] -pub(super) struct Canonicalized +pub(crate) struct Canonicalized where T: HasInterner, { - pub(super) value: Canonical, + pub(crate) value: Canonical, free_vars: Vec, } impl> Canonicalized { - /// this method is wrong and shouldn't exist - pub(super) fn decanonicalize_ty(&self, table: &mut InferenceTable, ty: Canonical) -> Ty { - let mut vars = self.free_vars.clone(); - while ty.binders.len(Interner) > vars.len() { - vars.push(table.new_type_var().cast(Interner)); - } - chalk_ir::Substitute::apply(&vars, ty.value, Interner) - } - pub(super) fn apply_solution( &self, ctx: &mut InferenceTable, @@ -203,13 +193,16 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(super) fn canonicalize + HasInterner>( + pub(crate) fn canonicalize + HasInterner>( &mut self, t: T, ) -> Canonicalized where T::Result: HasInterner, { + // try to resolve obligations before canonicalizing, since this might + // result in new knowledge about variables + self.resolve_obligations_as_possible(); let result = self.var_unification_table.canonicalize(Interner, t); let free_vars = result .free_vars @@ -225,7 +218,7 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { fold_tys( ty, |ty, _| match ty.kind(Interner) { @@ -238,7 +231,7 @@ impl<'a> InferenceTable<'a> { ) } - pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { + pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { let var = self.new_type_var(); let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; let obligation = alias_eq.cast(Interner); @@ -299,6 +292,13 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) } + pub(crate) fn instantiate_canonical(&mut self, canonical: Canonical) -> T::Result + where + T: HasInterner + Fold + std::fmt::Debug, + { + self.var_unification_table.instantiate_canonical(Interner, canonical) + } + fn resolve_with_fallback_inner( &mut self, var_stack: &mut Vec, @@ -351,6 +351,7 @@ impl<'a> InferenceTable<'a> { /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + self.resolve_obligations_as_possible(); self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) } @@ -363,6 +364,16 @@ impl<'a> InferenceTable<'a> { self.var_unification_table.rollback_to(snapshot.var_table_snapshot); } + /// Checks an obligation without registering it. Useful mostly to check + /// whether a trait *might* be implemented before deciding to 'lock in' the + /// choice (during e.g. method resolution or deref). + pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option { + let in_env = InEnvironment::new(&self.trait_env.env, goal); + let canonicalized = self.canonicalize(in_env); + let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value); + solution + } + pub(crate) fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) @@ -522,6 +533,51 @@ impl<'a> InferenceTable<'a> { } } } + + pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { + match ty.callable_sig(self.db) { + Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())), + None => self.callable_sig_from_fn_trait(ty, num_args), + } + } + + fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { + let krate = self.trait_env.krate; + let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; + let output_assoc_type = + self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + + let mut arg_tys = vec![]; + let arg_ty = TyBuilder::tuple(num_args) + .fill(iter::repeat_with(|| { + let arg = self.new_type_var(); + arg_tys.push(arg.clone()); + arg + })) + .build(); + + let projection = { + let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type); + if b.remaining() != 2 { + return None; + } + b.push(ty.clone()).push(arg_ty).build() + }; + + let trait_env = self.trait_env.env.clone(); + let obligation = InEnvironment { + goal: projection.trait_ref(self.db).cast(Interner), + environment: trait_env, + }; + let canonical = self.canonicalize(obligation.clone()); + if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { + self.register_obligation(obligation.goal); + let return_ty = self.normalize_projection_ty(projection); + Some((arg_tys, return_ty)) + } else { + None + } + } } impl<'a> fmt::Debug for InferenceTable<'a> { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index c91b6f2e82..44ece57a8e 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -17,10 +17,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::never; use crate::{ - autoderef, + autoderef::{self, AutoderefKind}, consteval::{self, ConstExt}, db::HirDatabase, from_foreign_def_id, + infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, primitive::{self, FloatTy, IntTy, UintTy}, static_lifetime, utils::all_super_traits, @@ -429,28 +430,25 @@ pub fn def_crates( Some(res) } -/// Look up the method with the given name, returning the actual autoderefed -/// receiver type (but without autoref applied yet). +/// Look up the method with the given name. pub(crate) fn lookup_method( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: &Name, -) -> Option<(Canonical, FunctionId)> { +) -> Option<(ReceiverAdjustments, FunctionId)> { iterate_method_candidates( ty, db, env, - krate, traits_in_scope, visible_from_module, Some(name), LookupMode::MethodCall, - |ty, f| match f { - AssocItemId::FunctionId(f) => Some((ty.clone(), f)), + |adjustments, f| match f { + AssocItemId::FunctionId(f) => Some((adjustments, f)), _ => None, }, ) @@ -496,33 +494,89 @@ impl From> for VisibleFromModule { } } +#[derive(Debug, Clone, Default)] +pub struct ReceiverAdjustments { + autoref: Option, + autoderefs: usize, + unsize_array: bool, +} + +impl ReceiverAdjustments { + pub(crate) fn apply(&self, table: &mut InferenceTable, ty: Ty) -> (Ty, Vec) { + let mut ty = ty; + let mut adjust = Vec::new(); + for _ in 0..self.autoderefs { + match autoderef::autoderef_step(table, ty.clone()) { + None => { + never!("autoderef not possible for {:?}", ty); + ty = TyKind::Error.intern(Interner); + break; + } + Some((kind, new_ty)) => { + ty = new_ty.clone(); + adjust.push(Adjustment { + kind: Adjust::Deref(match kind { + // FIXME should we know the mutability here? + AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), + AutoderefKind::Builtin => None, + }), + target: new_ty, + }); + } + } + } + if self.unsize_array { + ty = match ty.kind(Interner) { + TyKind::Array(inner, _) => TyKind::Slice(inner.clone()).intern(Interner), + _ => { + never!("unsize_array with non-array {:?}", ty); + ty + } + }; + // FIXME this is kind of wrong since the unsize needs to happen to a pointer/reference + adjust.push(Adjustment { + kind: Adjust::Pointer(PointerCast::Unsize), + target: ty.clone(), + }); + } + if let Some(m) = self.autoref { + ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); + adjust + .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() }); + } + (ty, adjust) + } + + fn with_autoref(&self, m: Mutability) -> ReceiverAdjustments { + Self { autoref: Some(m), ..*self } + } +} + // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? -pub fn iterate_method_candidates( +pub(crate) fn iterate_method_candidates( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - mut callback: impl FnMut(&Canonical, AssocItemId) -> Option, + mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option, ) -> Option { let mut slot = None; iterate_method_candidates_dyn( ty, db, env, - krate, traits_in_scope, visible_from_module, name, mode, - &mut |ty, item| { + &mut |adj, item| { assert!(slot.is_none()); - if let Some(it) = callback(ty, item) { + if let Some(it) = callback(adj, item) { slot = Some(it); return ControlFlow::Break(()); } @@ -532,28 +586,45 @@ pub fn iterate_method_candidates( slot } +pub fn iterate_path_candidates( + ty: &Canonical, + db: &dyn HirDatabase, + env: Arc, + traits_in_scope: &FxHashSet, + visible_from_module: VisibleFromModule, + name: Option<&Name>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, +) -> ControlFlow<()> { + iterate_method_candidates_dyn( + ty, + db, + env, + traits_in_scope, + visible_from_module, + name, + LookupMode::Path, + // the adjustments are not relevant for path lookup + &mut |_, id| callback(id), + ) +} + pub fn iterate_method_candidates_dyn( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { match mode { LookupMode::MethodCall => { - // For method calls, rust first does any number of autoderef, and then one - // autoref (i.e. when the method takes &self or &mut self). We just ignore - // the autoref currently -- when we find a method matching the given name, - // we assume it fits. - - // Also note that when we've got a receiver like &S, even if the method we - // find in the end takes &self, we still do the autoderef step (just as - // rustc does an autoderef and then autoref again). - let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; + // For method calls, rust first does any number of autoderef, and + // then one autoref (i.e. when the method takes &self or &mut self). + // Note that when we've got a receiver like &S, even if the method + // we find in the end takes &self, we still do the autoderef step + // (just as rustc does an autoderef and then autoref again). // We have to be careful about the order we're looking at candidates // in here. Consider the case where we're resolving `x.clone()` @@ -568,29 +639,31 @@ pub fn iterate_method_candidates_dyn( // the methods by autoderef order of *receiver types*, not *self // types*. - let deref_chain = autoderef_method_receiver(db, krate, ty); - let mut deref_chains = stdx::slice_tails(&deref_chain); + let mut table = InferenceTable::new(db, env.clone()); + let ty = table.instantiate_canonical(ty.clone()); + let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty); + let deref_chains = stdx::slice_tails(&deref_chain); - deref_chains.try_for_each(|deref_chain| { + let result = deref_chains.zip(adj).try_for_each(|(deref_chain, adj)| { iterate_method_candidates_with_autoref( deref_chain, + adj, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, callback, ) - }) + }); + result } LookupMode::Path => { // No autoderef for path lookups iterate_method_candidates_for_self_ty( ty, db, - env, - krate, + env.clone(), traits_in_scope, visible_from_module, name, @@ -602,27 +675,27 @@ pub fn iterate_method_candidates_dyn( fn iterate_method_candidates_with_autoref( deref_chain: &[Canonical], + first_adjustment: ReceiverAdjustments, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { let (receiver_ty, rest) = match deref_chain.split_first() { - Some((rec, rest)) => (rec.clone(), rest), + Some((rec, rest)) => (rec, rest), None => { never!("received empty deref-chain"); return ControlFlow::Break(()); } }; iterate_method_candidates_by_receiver( - &receiver_ty, + receiver_ty, + first_adjustment.clone(), &rest, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, @@ -630,17 +703,17 @@ fn iterate_method_candidates_with_autoref( )?; let refed = Canonical { - binders: receiver_ty.binders.clone(), value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) .intern(Interner), + binders: receiver_ty.binders.clone(), }; iterate_method_candidates_by_receiver( &refed, + first_adjustment.with_autoref(Mutability::Not), deref_chain, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, @@ -648,16 +721,17 @@ fn iterate_method_candidates_with_autoref( )?; let ref_muted = Canonical { - binders: receiver_ty.binders, - value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value).intern(Interner), + value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) + .intern(Interner), + binders: receiver_ty.binders.clone(), }; iterate_method_candidates_by_receiver( &ref_muted, + first_adjustment.with_autoref(Mutability::Mut), deref_chain, db, - env, - krate, + env.clone(), traits_in_scope, visible_from_module, name, @@ -667,14 +741,14 @@ fn iterate_method_candidates_with_autoref( fn iterate_method_candidates_by_receiver( receiver_ty: &Canonical, + receiver_adjustments: ReceiverAdjustments, rest_of_deref_chain: &[Canonical], db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through @@ -686,7 +760,7 @@ fn iterate_method_candidates_by_receiver( env.clone(), name, Some(receiver_ty), - krate, + Some(receiver_adjustments.clone()), visible_from_module, &mut callback, )? @@ -697,10 +771,10 @@ fn iterate_method_candidates_by_receiver( self_ty, db, env.clone(), - krate, traits_in_scope, name, Some(receiver_ty), + Some(receiver_adjustments.clone()), &mut callback, )? } @@ -712,11 +786,10 @@ fn iterate_method_candidates_for_self_ty( self_ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { iterate_inherent_methods( self_ty, @@ -724,24 +797,24 @@ fn iterate_method_candidates_for_self_ty( env.clone(), name, None, - krate, + None, visible_from_module, &mut callback, )?; - iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) + iterate_trait_method_candidates(self_ty, db, env, traits_in_scope, name, None, None, callback) } fn iterate_trait_method_candidates( self_ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, name: Option<&Name>, receiver_ty: Option<&Canonical>, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + receiver_adjustments: Option, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { - let receiver_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..)); + let self_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..)); // if ty is `dyn Trait`, the trait doesn't need to be in scope let inherent_trait = self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); @@ -763,10 +836,10 @@ fn iterate_trait_method_candidates( // 2021. // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for // arrays. - if data.skip_array_during_method_dispatch && receiver_is_array { + if data.skip_array_during_method_dispatch && self_is_array { // FIXME: this should really be using the edition of the method name's span, in case it // comes from a macro - if db.crate_graph()[krate].edition < Edition::Edition2021 { + if db.crate_graph()[env.krate].edition < Edition::Edition2021 { continue; } } @@ -782,14 +855,13 @@ fn iterate_trait_method_candidates( continue; } if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); - if db.trait_solve(krate, goal.cast(Interner)).is_none() { + let goal = generic_implements_goal(db, env.clone(), t, self_ty); + if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { continue 'traits; } } known_implemented = true; - // FIXME: we shouldn't be ignoring the binders here - callback(self_ty, item)? + callback(receiver_adjustments.clone().unwrap_or_default(), item)?; } } ControlFlow::Continue(()) @@ -824,11 +896,11 @@ fn iterate_inherent_methods( env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, - krate: CrateId, + receiver_adjustments: Option, visible_from_module: VisibleFromModule, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { - let def_crates = match def_crates(db, &self_ty.value, krate) { + let def_crates = match def_crates(db, &self_ty.value, env.krate) { Some(k) => k, None => return ControlFlow::Continue(()), }; @@ -848,6 +920,7 @@ fn iterate_inherent_methods( env.clone(), name, receiver_ty, + receiver_adjustments.clone(), module, callback, )?; @@ -856,7 +929,17 @@ fn iterate_inherent_methods( for krate in def_crates { let impls = db.inherent_impls_in_crate(krate); - impls_for_self_ty(&impls, self_ty, db, env.clone(), name, receiver_ty, module, callback)?; + impls_for_self_ty( + &impls, + self_ty, + db, + env.clone(), + name, + receiver_ty, + receiver_adjustments.clone(), + module, + callback, + )?; } return ControlFlow::Continue(()); @@ -867,8 +950,9 @@ fn iterate_inherent_methods( env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, + receiver_adjustments: Option, visible_from_module: Option, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { let impls_for_self_ty = filter_inherent_impls_for_self_ty(impls, &self_ty.value); for &impl_def in impls_for_self_ty { @@ -889,33 +973,32 @@ fn iterate_inherent_methods( // already happens in `is_valid_candidate` above; if not, we // check it here if receiver_ty.is_none() - && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none() + && inherent_impl_substs(db, env.clone(), impl_def, &self_ty).is_none() { cov_mark::hit!(impl_self_type_match_without_receiver); continue; } - let receiver_ty = receiver_ty.unwrap_or(self_ty); - callback(receiver_ty, item)?; + callback(receiver_adjustments.clone().unwrap_or_default(), item)?; } } ControlFlow::Continue(()) } } -/// Returns the self type for the index trait call. +/// Returns the receiver type for the index trait call. pub fn resolve_indexing_op( db: &dyn HirDatabase, - ty: &Canonical, env: Arc, - krate: CrateId, + ty: Canonical, index_trait: TraitId, -) -> Option> { - let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; - let deref_chain = autoderef_method_receiver(db, krate, ty); - for ty in deref_chain { - let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); - if db.trait_solve(krate, goal.cast(Interner)).is_some() { - return Some(ty); +) -> Option { + let mut table = InferenceTable::new(db, env.clone()); + let ty = table.instantiate_canonical(ty); + let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty); + for (ty, adj) in deref_chain.into_iter().zip(adj) { + let goal = generic_implements_goal(db, env.clone(), index_trait, &ty); + if db.trait_solve(env.krate, goal.cast(Interner)).is_some() { + return Some(adj); } } None @@ -1067,11 +1150,10 @@ pub fn implements_trait( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal.cast(Interner)); + let goal = generic_implements_goal(db, env.clone(), trait_, &ty); + let solution = db.trait_solve(env.krate, goal.cast(Interner)); solution.is_some() } @@ -1080,11 +1162,10 @@ pub fn implements_trait_unique( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal.cast(Interner)); + let goal = generic_implements_goal(db, env.clone(), trait_, &ty); + let solution = db.trait_solve(env.krate, goal.cast(Interner)); matches!(solution, Some(crate::Solution::Unique(_))) } @@ -1095,11 +1176,11 @@ fn generic_implements_goal( db: &dyn HirDatabase, env: Arc, trait_: TraitId, - self_ty: Canonical, + self_ty: &Canonical, ) -> Canonical> { let mut kinds = self_ty.binders.interned().to_vec(); let trait_ref = TyBuilder::trait_ref(db, trait_) - .push(self_ty.value) + .push(self_ty.value.clone()) .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) .build(); kinds.extend( @@ -1117,17 +1198,27 @@ fn generic_implements_goal( } fn autoderef_method_receiver( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment>, -) -> Vec> { - let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); - // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) - if let Some(TyKind::Array(parameters, _)) = deref_chain.last().map(|ty| ty.value.kind(Interner)) - { - let kinds = deref_chain.last().unwrap().binders.clone(); - let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); - deref_chain.push(Canonical { value: unsized_ty, binders: kinds }) + table: &mut InferenceTable, + ty: Ty, +) -> (Vec>, Vec) { + let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new()); + let mut autoderef = autoderef::Autoderef::new(table, ty); + while let Some((ty, derefs)) = autoderef.next() { + deref_chain.push(autoderef.table.canonicalize(ty).value); + adjustments.push(ReceiverAdjustments { + autoref: None, + autoderefs: derefs, + unsize_array: false, + }); } - deref_chain + // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) + if let (Some((TyKind::Array(parameters, _), binders)), Some(adj)) = ( + deref_chain.last().map(|ty| (ty.value.kind(Interner), ty.binders.clone())), + adjustments.last().cloned(), + ) { + let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); + deref_chain.push(Canonical { value: unsized_ty, binders }); + adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj }); + } + (deref_chain, adjustments) } diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 29250dca00..7385da5662 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs @@ -100,6 +100,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour .trim_start_matches("adjustments: ") .split(',') .map(|it| it.trim().to_string()) + .filter(|it| !it.is_empty()) .collect(), ); } else { diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index dd3b86f050..c0dddb608e 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -242,6 +242,45 @@ fn test() { ); } +#[test] +fn coerce_autoderef_implication_1() { + check_no_mismatches( + r" +//- minicore: deref +struct Foo; +impl core::ops::Deref for Foo { type Target = (); } + +fn takes_ref_foo(x: &Foo) {} +fn test() { + let foo = Foo; + //^^^ type: Foo<{unknown}> + takes_ref_foo(&foo); + + let foo = Foo; + //^^^ type: Foo + let _: &() = &foo; +}", + ); +} + +#[test] +fn coerce_autoderef_implication_2() { + check( + r" +//- minicore: deref +struct Foo; +impl core::ops::Deref for Foo { type Target = (); } + +fn takes_ref_foo(x: &Foo) {} +fn test() { + let foo = Foo; + //^^^ type: Foo<{unknown}> + let _: &u32 = &Foo; + //^^^^ expected &u32, got &Foo<{unknown}> +}", + ); +} + #[test] fn closure_return_coerce() { check_no_mismatches( diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 9c0c00da3b..c118ae24cf 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1460,3 +1460,121 @@ fn main() { "#, ); } + +#[test] +fn deref_fun_1() { + check_types( + r#" +//- minicore: deref + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &C { loop {} } +} + +impl C { + fn thing(&self) -> T { self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), make()); + let _: usize = (*a1).0; + a1; + //^^ A, u32> + + let a2 = A(make(), make()); + a2.thing(); + //^^^^^^^^^^ isize + a2; + //^^ A, u32> +} +"#, + ); +} + +#[test] +fn deref_fun_2() { + check_types( + r#" +//- minicore: deref + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &C { loop {} } +} + +impl core::ops::Deref for A, i32> { + type Target = C; + fn deref(&self) -> &C { &self.0 } +} + +impl C { + fn thing(&self) -> T { self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), 1u32); + a1.thing(); + a1; + //^^ A, u32> + + let a2 = A(make(), 1i32); + let _: &str = a2.thing(); + a2; + //^^ A, i32> +} +"#, + ); +} + +#[test] +fn receiver_adjustment_autoref() { + check( + r#" +struct Foo; +impl Foo { + fn foo(&self) {} +} +fn test() { + Foo.foo(); + //^^^ adjustments: Borrow(Ref(Not)) + (&Foo).foo(); + // ^^^^ adjustments: , +} +"#, + ); +} + +#[test] +fn receiver_adjustment_unsize_array() { + // FIXME not quite correct + check( + r#" +//- minicore: slice +fn test() { + let a = [1, 2, 3]; + a.len(); +} //^ adjustments: Pointer(Unsize), Borrow(Ref(Not)) +"#, + ); +} diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index c2669646e2..04d8b91e35 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -540,6 +540,52 @@ fn test() { ); } +#[test] +fn infer_ops_index_field() { + check_types( + r#" +//- minicore: index +struct Bar; +struct Foo { + field: u32; +} + +impl core::ops::Index for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = a[1u32].field; + b; +} //^ u32 +"#, + ); +} + +#[test] +fn infer_ops_index_field_autoderef() { + check_types( + r#" +//- minicore: index +struct Bar; +struct Foo { + field: u32; +} + +impl core::ops::Index for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = (&a[1u32]).field; + b; +} //^ u32 +"#, + ); +} + #[test] fn infer_ops_index_int() { check_types( diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index bd280ba774..b139edbee9 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -40,8 +40,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TraitEnvironment { pub krate: CrateId, - // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, - // but for now it's too annoying... + // FIXME make this a BTreeMap pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, pub env: chalk_ir::Environment, } diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs index 0d2daa8dc3..4a4ad984db 100644 --- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs @@ -154,11 +154,11 @@ fn is_ref_and_impls_iter_method( let has_wanted_method = ty .iterate_method_candidates( sema.db, - krate, + &scope, &traits_in_scope, None, Some(&wanted_method), - |_, func| { + |func| { if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) { return Some(()); } diff --git a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs index 5f361f01dc..ac17e3f057 100644 --- a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs @@ -371,7 +371,7 @@ fn handle_ref_field_usage(ctx: &AssistContext, field_expr: &FieldExpr) -> RefDat fn is_auto_ref(ctx: &AssistContext, call_expr: &MethodCallExpr) -> bool { fn impl_(ctx: &AssistContext, call_expr: &MethodCallExpr) -> Option { let rec = call_expr.receiver()?; - let rec_ty = ctx.sema.type_of_expr(&rec)?.adjusted(); + let rec_ty = ctx.sema.type_of_expr(&rec)?.original(); // input must be actual value if rec_ty.is_reference() { return Some(false); diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs index cb3fbed219..db0cfbe2ec 100644 --- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs @@ -90,10 +90,9 @@ fn get_impl_method( let impl_def: hir::Impl = ctx.sema.to_def(impl_)?; let scope = ctx.sema.scope(impl_.syntax()); - let krate = impl_def.module(db).krate(); let ty = impl_def.self_ty(db); let traits_in_scope = scope.visible_traits(); - ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| { + ty.iterate_method_candidates(db, &scope, &traits_in_scope, None, Some(fn_name), |func| { Some(func) }) } diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 3bb3f883cd..981e25b55c 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -74,30 +74,28 @@ fn complete_methods( receiver: &hir::Type, mut f: impl FnMut(hir::Function), ) { - if let Some(krate) = ctx.krate { - let mut seen_methods = FxHashSet::default(); - let mut traits_in_scope = ctx.scope.visible_traits(); + let mut seen_methods = FxHashSet::default(); + let mut traits_in_scope = ctx.scope.visible_traits(); - // Remove drop from the environment as calling `Drop::drop` is not allowed - if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { - cov_mark::hit!(dot_remove_drop_trait); - traits_in_scope.remove(&drop_trait.into()); - } - - receiver.iterate_method_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, func| { - if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { - f(func); - } - None::<()> - }, - ); + // Remove drop from the environment as calling `Drop::drop` is not allowed + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { + cov_mark::hit!(dot_remove_drop_trait); + traits_in_scope.remove(&drop_trait.into()); } + + receiver.iterate_method_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |func| { + if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { + f(func); + } + None::<()> + }, + ); } #[cfg(test)] diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index f1b4fa7205..c8a9cf21da 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -134,39 +134,37 @@ fn pattern_path_completion( .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); } res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => { - if let Some(krate) = ctx.krate { - let ty = match res { - hir::PathResolution::TypeParam(param) => param.ty(ctx.db), - hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), - _ => return, - }; + let ty = match res { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - e.variants(ctx.db) - .into_iter() - .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); - } - - let traits_in_scope = ctx.scope.visible_traits(); - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, item| { - // Note associated consts cannot be referenced in patterns - if let AssocItem::TypeAlias(ta) = item { - // We might iterate candidates of a trait multiple times here, so deduplicate them. - if seen.insert(item) { - acc.add_type_alias(ctx, ta); - } - } - None::<()> - }, - ); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |item| { + // Note associated consts cannot be referenced in patterns + if let AssocItem::TypeAlias(ta) = item { + // We might iterate candidates of a trait multiple times here, so deduplicate them. + if seen.insert(item) { + acc.add_type_alias(ctx, ta); + } + } + None::<()> + }, + ); } _ => {} } diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index cf78f7c1ad..d63aacbadb 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -138,11 +138,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let traits_in_scope = ctx.scope.visible_traits(); ty.iterate_path_candidates( ctx.db, - krate, + &ctx.scope, &traits_in_scope, ctx.module, None, - |_ty, item| { + |item| { add_assoc_item(acc, ctx, item); None::<()> }, @@ -164,35 +164,33 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon } } hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { - if let Some(krate) = ctx.krate { - let ty = match resolution { - hir::PathResolution::TypeParam(param) => param.ty(ctx.db), - hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), - _ => return, - }; + let ty = match resolution { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - add_enum_variants(acc, ctx, e); - } - - let traits_in_scope = ctx.scope.visible_traits(); - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, ctx, item); - } - None::<()> - }, - ); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + add_enum_variants(acc, ctx, e); } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + add_assoc_item(acc, ctx, item); + } + None::<()> + }, + ); } _ => {} } diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 319a217352..d91627e0a9 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -1,7 +1,7 @@ //! Look up accessible paths for items. use hir::{ AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, - ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, + ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type, }; use itertools::Itertools; use rustc_hash::FxHashSet; @@ -239,7 +239,6 @@ impl ImportAssets { let _p = profile::span("import_assets::search_for"); let scope_definitions = self.scope_definitions(sema); - let current_crate = self.module_with_candidate.krate(); let mod_path = |item| { get_mod_path( sema.db, @@ -249,15 +248,18 @@ impl ImportAssets { ) }; + let krate = self.module_with_candidate.krate(); + let scope = sema.scope(&self.candidate_node); + match &self.import_candidate { ImportCandidate::Path(path_candidate) => { - path_applicable_imports(sema, current_crate, path_candidate, mod_path) + path_applicable_imports(sema, krate, path_candidate, mod_path) } ImportCandidate::TraitAssocItem(trait_candidate) => { - trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path) + trait_applicable_items(sema, krate, &scope, trait_candidate, true, mod_path) } ImportCandidate::TraitMethod(trait_candidate) => { - trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path) + trait_applicable_items(sema, krate, &scope, trait_candidate, false, mod_path) } } .into_iter() @@ -447,6 +449,7 @@ fn module_with_segment_name( fn trait_applicable_items( sema: &Semantics, current_crate: Crate, + scope: &SemanticsScope, trait_candidate: &TraitImportCandidate, trait_assoc_item: bool, mod_path: impl Fn(ItemInNs) -> Option, @@ -484,11 +487,11 @@ fn trait_applicable_items( if trait_assoc_item { trait_candidate.receiver_ty.iterate_path_candidates( db, - current_crate, + scope, &trait_candidates, None, None, - |_, assoc| { + |assoc| { if required_assoc_items.contains(&assoc) { if let AssocItem::Function(f) = assoc { if f.self_param(db).is_some() { @@ -511,11 +514,11 @@ fn trait_applicable_items( } else { trait_candidate.receiver_ty.iterate_method_candidates( db, - current_crate, + scope, &trait_candidates, None, None, - |_, function| { + |function| { let assoc = function.as_assoc_item(db)?; if required_assoc_items.contains(&assoc) { let located_trait = assoc.containing_trait(db)?; diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 844b19779a..e15cb874ef 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs @@ -222,11 +222,11 @@ impl<'db> ResolutionScope<'db> { let module = self.scope.module()?; adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, - module.krate(), + &self.scope, &self.scope.visible_traits(), Some(module), None, - |_ty, assoc_item| { + |assoc_item| { let item_name = assoc_item.name(self.scope.db)?; if item_name.to_smol_str().as_str() == name.text() { Some(hir::PathResolution::AssocItem(assoc_item))