From 0ad7317b24dc90c3787482f9ec563e7830d499fc Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 1 May 2019 17:57:56 +0200 Subject: Canonicalize before doing method resolution --- crates/ra_hir/src/ty/method_resolution.rs | 219 +++++++++++++++--------------- 1 file changed, 109 insertions(+), 110 deletions(-) (limited to 'crates/ra_hir/src/ty/method_resolution.rs') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index ea7da0b62..bc5033be6 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -16,7 +16,7 @@ use crate::{ generics::HasGenericParams, ty::primitive::{UncertainIntTy, UncertainFloatTy} }; -use super::{TraitRef, infer::Canonical, Substs}; +use super::{TraitRef, Canonical}; /// This is used as a key for indexing impls. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -130,132 +130,123 @@ fn def_crate(db: &impl HirDatabase, cur_crate: Crate, ty: &Ty) -> Option } } -impl Ty { - /// Look up the method with the given name, returning the actual autoderefed - /// receiver type (but without autoref applied yet). - pub(crate) fn lookup_method( - self, - db: &impl HirDatabase, - name: &Name, - resolver: &Resolver, - ) -> Option<(Ty, Function)> { - self.iterate_method_candidates(db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) - } +/// Look up the method with the given name, returning the actual autoderefed +/// receiver type (but without autoref applied yet). +pub(crate) fn lookup_method( + ty: &Canonical, + db: &impl HirDatabase, + name: &Name, + resolver: &Resolver, +) -> Option<(Ty, Function)> { + iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) +} - // This would be nicer if it just returned an iterator, but that runs into - // lifetime problems, because we need to borrow temp `CrateImplBlocks`. - pub(crate) fn iterate_method_candidates( - self, - db: &impl HirDatabase, - resolver: &Resolver, - name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, - ) -> Option { - // 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. +// This would be nicer if it just returned an iterator, but that runs into +// lifetime problems, because we need to borrow temp `CrateImplBlocks`. +pub(crate) fn iterate_method_candidates( + ty: &Canonical, + db: &impl HirDatabase, + resolver: &Resolver, + name: Option<&Name>, + mut callback: impl FnMut(&Ty, Function) -> Option, +) -> Option { + // 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). + // 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 krate = resolver.krate()?; - for derefed_ty in self.autoderef(db) { - if let Some(result) = - derefed_ty.iterate_inherent_methods(db, name, krate, &mut callback) - { - return Some(result); - } - if let Some(result) = - derefed_ty.iterate_trait_method_candidates(db, resolver, name, &mut callback) - { - return Some(result); - } + let krate = resolver.krate()?; + for derefed_ty in ty.value.clone().autoderef(db) { + let derefed_ty = Canonical { value: derefed_ty, num_vars: ty.num_vars }; + if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) + { + return Some(result); + } + if let Some(result) = + iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) + { + return Some(result); } - None } + None +} - fn iterate_trait_method_candidates( - &self, - db: &impl HirDatabase, - resolver: &Resolver, - name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, - ) -> Option { - let krate = resolver.krate()?; - 'traits: for t in resolver.traits_in_scope() { - let data = t.trait_data(db); - // we'll be lazy about checking whether the type implements the - // trait, but if we find out it doesn't, we'll skip the rest of the - // iteration - let mut known_implemented = false; - for item in data.items() { - match item { - &TraitItem::Function(m) => { - let sig = m.signature(db); - if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() { - if !known_implemented { - // TODO the self type may contain type - // variables, so we need to do proper - // canonicalization here - let trait_ref = TraitRef { - trait_: t, - substs: fresh_substs_for_trait(db, t, self.clone()), - }; - let canonical = Canonical { - num_vars: trait_ref.substs.len(), - value: trait_ref, - }; - // FIXME cache this implements check (without solution) in a query? - if super::traits::implements(db, krate, canonical).is_none() { - continue 'traits; - } - } - known_implemented = true; - if let Some(result) = callback(self, m) { - return Some(result); +fn iterate_trait_method_candidates( + ty: &Canonical, + db: &impl HirDatabase, + resolver: &Resolver, + name: Option<&Name>, + mut callback: impl FnMut(&Ty, Function) -> Option, +) -> Option { + let krate = resolver.krate()?; + 'traits: for t in resolver.traits_in_scope() { + let data = t.trait_data(db); + // we'll be lazy about checking whether the type implements the + // trait, but if we find out it doesn't, we'll skip the rest of the + // iteration + let mut known_implemented = false; + for item in data.items() { + match item { + &TraitItem::Function(m) => { + let sig = m.signature(db); + if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() { + if !known_implemented { + let trait_ref = canonical_trait_ref(db, t, ty.clone()); + // FIXME cache this implements check (without solution) in a query? + if super::traits::implements(db, krate, trait_ref).is_none() { + continue 'traits; } } + known_implemented = true; + // TODO the self type is now canonicalized... + if let Some(result) = callback(&ty.value, m) { + return Some(result); + } } - _ => {} } + _ => {} } } - None } + None +} - fn iterate_inherent_methods( - &self, - db: &impl HirDatabase, - name: Option<&Name>, - krate: Crate, - mut callback: impl FnMut(&Ty, Function) -> Option, - ) -> Option { - let krate = match def_crate(db, krate, self) { - Some(krate) => krate, - None => return None, - }; - let impls = db.impls_in_crate(krate); +fn iterate_inherent_methods( + ty: &Canonical, + db: &impl HirDatabase, + name: Option<&Name>, + krate: Crate, + mut callback: impl FnMut(&Ty, Function) -> Option, +) -> Option { + let krate = match def_crate(db, krate, &ty.value) { + Some(krate) => krate, + None => return None, + }; + let impls = db.impls_in_crate(krate); - for impl_block in impls.lookup_impl_blocks(self) { - for item in impl_block.items(db) { - match item { - ImplItem::Method(f) => { - let sig = f.signature(db); - if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() { - if let Some(result) = callback(self, f) { - return Some(result); - } + for impl_block in impls.lookup_impl_blocks(&ty.value) { + for item in impl_block.items(db) { + match item { + ImplItem::Method(f) => { + let sig = f.signature(db); + if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() { + if let Some(result) = callback(&ty.value, f) { + return Some(result); } } - _ => {} } + _ => {} } } - None } + None +} +impl Ty { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplBlocks`. pub fn iterate_impl_items( @@ -280,17 +271,25 @@ impl Ty { /// This creates Substs for a trait with the given Self type and type variables /// for all other parameters, to query Chalk with it. -fn fresh_substs_for_trait(db: &impl HirDatabase, tr: Trait, self_ty: Ty) -> Substs { +fn canonical_trait_ref( + db: &impl HirDatabase, + trait_: Trait, + self_ty: Canonical, +) -> Canonical { let mut substs = Vec::new(); - let generics = tr.generic_params(db); - substs.push(self_ty); + let generics = trait_.generic_params(db); + let num_vars = self_ty.num_vars; + substs.push(self_ty.value); substs.extend( generics .params_including_parent() .into_iter() .skip(1) .enumerate() - .map(|(i, _p)| Ty::Bound(i as u32)), + .map(|(i, _p)| Ty::Bound((i + num_vars) as u32)), ); - substs.into() + Canonical { + num_vars: substs.len() - 1 + self_ty.num_vars, + value: TraitRef { trait_, substs: substs.into() }, + } } -- cgit v1.2.3