From b634ba41e0439cbbb89b12a3d340c8463b35b93e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 29 Oct 2019 11:04:42 +0100 Subject: Get trait assoc item resolution mostly working --- crates/ra_hir/src/ty/infer/path.rs | 106 +++++++++++++++++++++++-- crates/ra_hir/src/ty/tests.rs | 153 ++++++++++++++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 11 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 77aa35ce1..885588174 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -6,9 +6,11 @@ use super::{ExprOrPatId, InferenceContext, TraitRef}; use crate::{ db::HirDatabase, resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, + ty::{lower, traits::TraitEnvironment, Canonical}, ty::{Substs, Ty, TypableDef, TypeWalk}, - AssocItem, HasGenericParams, Namespace, Path, + AssocItem, HasGenericParams, Name, Namespace, Path, Trait, }; +use std::sync::Arc; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn infer_path( @@ -39,7 +41,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty); self.resolve_ty_assoc_item( ty, - path.segments.last().expect("path had at least one segment"), + &path.segments.last().expect("path had at least one segment").name, id, )? } else { @@ -125,7 +127,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let segment = remaining_segments.last().expect("there should be at least one segment here"); - self.resolve_ty_assoc_item(ty, segment, id) + self.resolve_ty_assoc_item(ty, &segment.name, id) } } } @@ -162,7 +164,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; let substs = Substs::build_for_def(self.db, item) .use_parent_substs(&trait_ref.substs) - .fill_with_unknown() + .fill_with_params() .build(); self.write_assoc_resolution(id, item); @@ -172,20 +174,29 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_ty_assoc_item( &mut self, ty: Ty, - segment: &PathSegment, + name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Option)> { if let Ty::Unknown = ty { return None; } + self.find_inherent_assoc_candidate(ty.clone(), name, id) + .or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id)) + } + + fn find_inherent_assoc_candidate( + &mut self, + ty: Ty, + name: &Name, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { let krate = self.resolver.krate()?; // Find impl - // FIXME: consider trait candidates let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { AssocItem::Function(func) => { - if segment.name == func.name(self.db) { + if *name == func.name(self.db) { Some(AssocItem::Function(func)) } else { None @@ -193,7 +204,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } AssocItem::Const(konst) => { - if konst.name(self.db).map_or(false, |n| n == segment.name) { + if konst.name(self.db).map_or(false, |n| n == *name) { Some(AssocItem::Const(konst)) } else { None @@ -212,6 +223,65 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Some((def, substs)) } + fn find_trait_assoc_candidate( + &mut self, + ty: Ty, + name: &Name, + _id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + let krate = self.resolver.krate()?; + + let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); + + let env = lower::trait_env(self.db, &self.resolver); + // if we have `T: Trait` in the param env, the trait doesn't need to be in scope + let traits_from_env = env + .trait_predicates_for_self_ty(&ty) + .map(|tr| tr.trait_) + .flat_map(|t| t.all_super_traits(self.db)); + let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db)); + + 'traits: for t in traits { + let data = t.trait_data(self.db); + let mut known_implemented = false; + for item in data.items() { + if let AssocItem::Function(f) = *item { + if f.name(self.db) == *name { + if !known_implemented { + let goal = generic_implements_goal( + self.db, + env.clone(), + t, + canonical_ty.value.clone(), + ); + if self.db.trait_solve(krate, goal).is_none() { + continue 'traits; + } + } + known_implemented = true; + + // we're picking this method + let trait_substs = Substs::build_for_def(self.db, t) + .push(ty.clone()) + .fill(std::iter::repeat_with(|| self.new_type_var())) + .build(); + let substs = Substs::build_for_def(self.db, f) + .use_parent_substs(&trait_substs) + .fill_with_params() + .build(); + self.obligations.push(super::Obligation::Trait(TraitRef { + trait_: t, + substs: trait_substs, + })); + return Some((ValueNs::Function(f), Some(substs))); + } + } + } + } + + None + } + fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { if let ValueNs::Function(func) = def { // We only do the infer if parent has generic params @@ -242,3 +312,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } + +// TODO remove duplication +/// This creates Substs for a trait with the given Self type and type variables +/// for all other parameters, to query Chalk with it. +fn generic_implements_goal( + db: &impl HirDatabase, + env: Arc, + trait_: Trait, + self_ty: Canonical, +) -> Canonical> { + let num_vars = self_ty.num_vars; + let substs = super::Substs::build_for_def(db, trait_) + .push(self_ty.value) + .fill_with_bound_vars(num_vars as u32) + .build(); + let num_vars = substs.len() - 1 + self_ty.num_vars; + let trait_ref = TraitRef { trait_, substs }; + let obligation = super::Obligation::Trait(trait_ref); + Canonical { num_vars, value: super::InEnvironment::new(env, obligation) } +} diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c12326643..7183b205c 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2782,9 +2782,9 @@ fn test() { [97; 99) 's1': S [105; 121) 'Defaul...efault': fn default() -> Self [105; 123) 'Defaul...ault()': S - [133; 135) 's2': {unknown} - [138; 148) 'S::default': {unknown} - [138; 150) 'S::default()': {unknown} + [133; 135) 's2': S + [138; 148) 'S::default': fn default() -> Self + [138; 150) 'S::default()': S [160; 162) 's3': S [165; 188) '() -> Self [165; 190) ' { + fn make() -> T; +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make(); + let b = G::::make(); + let c: f64 = G::make(); +} +"#), + @r###" + [127; 211) '{ ...e(); }': () + [137; 138) 'a': u32 + [141; 148) 'S::make': fn make() -> T + [141; 150) 'S::make()': u32 + [160; 161) 'b': u64 + [164; 178) 'G::::make': fn make, u64>() -> T + [164; 180) 'G::, f64>() -> T + [199; 208) 'G::make()': f64 + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_2() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (T, U); +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make::(); + let b: (_, i64) = S::make(); + let c = G::::make::(); + let d: (u32, _) = G::make::(); + let e: (u32, i64) = G::make(); +} +"#), + @r###" + [135; 313) '{ ...e(); }': () + [145; 146) 'a': (u32, i64) + [149; 163) 'S::make::': fn make() -> (T, U) + [149; 165) 'S::mak...i64>()': (u32, i64) + [175; 176) 'b': (u32, i64) + [189; 196) 'S::make': fn make() -> (T, U) + [189; 198) 'S::make()': (u32, i64) + [208; 209) 'c': (u32, i64) + [212; 233) 'G::': fn make, u32, i64>() -> (T, U) + [212; 235) 'G::()': (u32, i64) + [245; 246) 'd': (u32, i64) + [259; 273) 'G::make::': fn make, u32, i64>() -> (T, U) + [259; 275) 'G::mak...i64>()': (u32, i64) + [285; 286) 'e': (u32, i64) + [301; 308) 'G::make': fn make, u32, i64>() -> (T, U) + [301; 310) 'G::make()': (u32, i64) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_3() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +fn test() { + let a = S::make(); +} +"#), + @r###" + [101; 127) '{ ...e(); }': () + [111; 112) 'a': {unknown} + [115; 122) 'S::make': {unknown} + [115; 124) 'S::make()': {unknown} + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_4() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +impl Trait for S {} +fn test() { + let a: (Self, _) = S::make(); + let b: (_, u32) = S::make(); +} +"#), + @r###" + [131; 206) '{ ...e(); }': () + [141; 142) 'a': ({unknown}, {unknown}) + [161; 168) 'S::make': {unknown} + [161; 170) 'S::make()': ({unknown}, {unknown}) + [180; 181) 'b': ({unknown}, u32) + [194; 201) 'S::make': {unknown} + [194; 203) 'S::make()': ({unknown}, u32) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_5() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T, U); +} +struct S; +impl Trait for S {} +fn test() { + let a = >::make::(); + let b: (S, _, _) = Trait::::make::(); +} +"#), + @r###" + [107; 211) '{ ...>(); }': () + [117; 118) 'a': (S, i64, u8) + [121; 150) '': fn make, i64, u8>() -> (Self, T, U) + [121; 152) '()': (S, i64, u8) + [162; 163) 'b': (S, i64, u8) + [182; 206) 'Trait:...::': fn make, i64, u8>() -> (Self, T, U) + [182; 208) 'Trait:...()': (S, i64, u8) + "### + ); +} + #[test] fn infer_from_bound_1() { assert_snapshot!( -- cgit v1.2.3 From 44279ef1df49585d0b151dcfb5cefade012dbc7a Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 30 Oct 2019 20:35:01 +0100 Subject: Add another unrelated, currently not working test --- crates/ra_hir/src/ty/tests.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 7183b205c..3af926279 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3450,6 +3450,22 @@ fn test() { S.foo()<|>; } assert_eq!(t, "u128"); } +#[ignore] +#[test] +fn method_resolution_by_value_before_autoref() { + let t = type_at( + r#" +//- /main.rs +trait Clone { fn clone(&self) -> Self; } +struct S; +impl Clone for S {} +impl Clone for &S {} +fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; } +"#, + ); + assert_eq!(t, "(S, S, &S)"); +} + #[test] fn method_resolution_trait_before_autoderef() { let t = type_at( -- cgit v1.2.3 From 7b7133ec58818894d3d56df021ae70159e2c3252 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 30 Oct 2019 21:22:46 +0100 Subject: Insert type vars before doing assoc item resolution --- crates/ra_hir/src/ty/infer/path.rs | 3 +++ crates/ra_hir/src/ty/tests.rs | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 885588174..0cde77265 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -124,6 +124,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { return None; } + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + let segment = remaining_segments.last().expect("there should be at least one segment here"); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3af926279..e071e4d4e 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2879,9 +2879,9 @@ fn test() { "#), @r###" [101; 127) '{ ...e(); }': () - [111; 112) 'a': {unknown} - [115; 122) 'S::make': {unknown} - [115; 124) 'S::make()': {unknown} + [111; 112) 'a': (S, i64) + [115; 122) 'S::make': fn make, i64>() -> (Self, T) + [115; 124) 'S::make()': (S, i64) "### ); } @@ -2897,18 +2897,18 @@ struct S; impl Trait for S {} impl Trait for S {} fn test() { - let a: (Self, _) = S::make(); - let b: (_, u32) = S::make(); + let a: (S, _) = S::make(); + let b: (_, i32) = S::make(); } "#), @r###" - [131; 206) '{ ...e(); }': () - [141; 142) 'a': ({unknown}, {unknown}) - [161; 168) 'S::make': {unknown} - [161; 170) 'S::make()': ({unknown}, {unknown}) - [180; 181) 'b': ({unknown}, u32) - [194; 201) 'S::make': {unknown} - [194; 203) 'S::make()': ({unknown}, u32) + [131; 203) '{ ...e(); }': () + [141; 142) 'a': (S, i64) + [158; 165) 'S::make': fn make, i64>() -> (Self, T) + [158; 167) 'S::make()': (S, i64) + [177; 178) 'b': (S, i32) + [191; 198) 'S::make': fn make, i32>() -> (Self, T) + [191; 200) 'S::make()': (S, i32) "### ); } -- cgit v1.2.3 From c7cedea270c492e9a2c8b81c1312fda44fd8217e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 31 Oct 2019 15:13:52 +0100 Subject: Record assoc item resolution --- crates/ra_hir/src/ty/infer/path.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 0cde77265..c58564b22 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -230,7 +230,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &mut self, ty: Ty, name: &Name, - _id: ExprOrPatId, + id: ExprOrPatId, ) -> Option<(ValueNs, Option)> { let krate = self.resolver.krate()?; @@ -276,6 +276,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { trait_: t, substs: trait_substs, })); + + self.write_assoc_resolution(id, *item); return Some((ValueNs::Function(f), Some(substs))); } } -- cgit v1.2.3 From 1173c3dab5f77a1afd367d547790dd82c558fe0d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 31 Oct 2019 19:28:33 +0100 Subject: Refactor to unify with method resolution --- crates/ra_hir/src/ty/infer/path.rs | 135 ++++++------------------------ crates/ra_hir/src/ty/method_resolution.rs | 90 +++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 4 +- 3 files changed, 96 insertions(+), 133 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index c58564b22..1946bf608 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef}; use crate::{ db::HirDatabase, resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, - ty::{lower, traits::TraitEnvironment, Canonical}, - ty::{Substs, Ty, TypableDef, TypeWalk}, - AssocItem, HasGenericParams, Name, Namespace, Path, Trait, + ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk}, + AssocItem, Container, HasGenericParams, Name, Namespace, Path, }; -use std::sync::Arc; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn infer_path( @@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { return None; } - self.find_inherent_assoc_candidate(ty.clone(), name, id) - .or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id)) - } - - fn find_inherent_assoc_candidate( - &mut self, - ty: Ty, - name: &Name, - id: ExprOrPatId, - ) -> Option<(ValueNs, Option)> { - let krate = self.resolver.krate()?; - - // Find impl - let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { - AssocItem::Function(func) => { - if *name == func.name(self.db) { - Some(AssocItem::Function(func)) - } else { - None - } - } - - AssocItem::Const(konst) => { - if konst.name(self.db).map_or(false, |n| n == *name) { - Some(AssocItem::Const(konst)) - } else { - None - } - } - AssocItem::TypeAlias(_) => None, - })?; - let def = match item { - AssocItem::Function(f) => ValueNs::Function(f), - AssocItem::Const(c) => ValueNs::Const(c), - AssocItem::TypeAlias(_) => unreachable!(), - }; - let substs = self.find_self_types(&def, ty); - - self.write_assoc_resolution(id, item); - Some((def, substs)) - } - - fn find_trait_assoc_candidate( - &mut self, - ty: Ty, - name: &Name, - id: ExprOrPatId, - ) -> Option<(ValueNs, Option)> { - let krate = self.resolver.krate()?; - let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); - let env = lower::trait_env(self.db, &self.resolver); - // if we have `T: Trait` in the param env, the trait doesn't need to be in scope - let traits_from_env = env - .trait_predicates_for_self_ty(&ty) - .map(|tr| tr.trait_) - .flat_map(|t| t.all_super_traits(self.db)); - let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db)); - - 'traits: for t in traits { - let data = t.trait_data(self.db); - let mut known_implemented = false; - for item in data.items() { - if let AssocItem::Function(f) = *item { - if f.name(self.db) == *name { - if !known_implemented { - let goal = generic_implements_goal( - self.db, - env.clone(), - t, - canonical_ty.value.clone(), - ); - if self.db.trait_solve(krate, goal).is_none() { - continue 'traits; - } - } - known_implemented = true; - + method_resolution::iterate_method_candidates( + &canonical_ty.value, + self.db, + &self.resolver.clone(), + Some(name), + method_resolution::LookupMode::Path, + move |_ty, item| { + let def = match item { + AssocItem::Function(f) => ValueNs::Function(f), + AssocItem::Const(c) => ValueNs::Const(c), + AssocItem::TypeAlias(_) => unreachable!(), + }; + match item.container(self.db) { + Container::ImplBlock(_) => { + let substs = self.find_self_types(&def, ty.clone()); + + self.write_assoc_resolution(id, item); + Some((def, substs)) + } + Container::Trait(t) => { // we're picking this method let trait_substs = Substs::build_for_def(self.db, t) .push(ty.clone()) .fill(std::iter::repeat_with(|| self.new_type_var())) .build(); - let substs = Substs::build_for_def(self.db, f) + let substs = Substs::build_for_def(self.db, item) .use_parent_substs(&trait_substs) .fill_with_params() .build(); @@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { substs: trait_substs, })); - self.write_assoc_resolution(id, *item); - return Some((ValueNs::Function(f), Some(substs))); + self.write_assoc_resolution(id, item); + Some((def, Some(substs))) } } - } - } - - None + }, + ) } fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { @@ -317,23 +256,3 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } } } - -// TODO remove duplication -/// This creates Substs for a trait with the given Self type and type variables -/// for all other parameters, to query Chalk with it. -fn generic_implements_goal( - db: &impl HirDatabase, - env: Arc, - trait_: Trait, - self_ty: Canonical, -) -> Canonical> { - let num_vars = self_ty.num_vars; - let substs = super::Substs::build_for_def(db, trait_) - .push(self_ty.value) - .fill_with_bound_vars(num_vars as u32) - .build(); - let num_vars = substs.len() - 1 + self_ty.num_vars; - let trait_ref = TraitRef { trait_, substs }; - let obligation = super::Obligation::Trait(trait_ref); - Canonical { num_vars, value: super::InEnvironment::new(env, obligation) } -} diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6..ee0c7b00f 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -166,7 +166,19 @@ pub(crate) fn lookup_method( name: &Name, resolver: &Resolver, ) -> Option<(Ty, Function)> { - iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) + iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| { + if let AssocItem::Function(f) = f { + Some((ty.clone(), f)) + } else { + None + } + }) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum LookupMode { + MethodCall, + Path, } // This would be nicer if it just returned an iterator, but that runs into @@ -176,7 +188,8 @@ pub(crate) fn iterate_method_candidates( db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> 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 @@ -188,13 +201,15 @@ pub(crate) fn iterate_method_candidates( // rustc does an autoderef and then autoref again). let krate = resolver.krate()?; + // TODO no autoderef in LookupMode::Path for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { - if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) + if let Some(result) = + iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) { return Some(result); } if let Some(result) = - iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) + iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback) { return Some(result); } @@ -207,7 +222,8 @@ fn iterate_trait_method_candidates( db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { let krate = resolver.krate()?; // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) @@ -231,21 +247,35 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = inherently_implemented; - for item in data.items() { - if let AssocItem::Function(m) = *item { - let data = m.data(db); - if name.map_or(true, |name| data.name() == name) && data.has_self_param() { - if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); - if db.trait_solve(krate, goal).is_none() { - continue 'traits; - } + for &item in data.items() { + // TODO unify with the impl case + match item { + AssocItem::Function(m) => { + let data = m.data(db); + if !name.map_or(true, |name| data.name() == name) + || (!data.has_self_param() && mode != LookupMode::Path) + { + continue; } - known_implemented = true; - if let Some(result) = callback(&ty.value, m) { - return Some(result); + } + AssocItem::Const(c) => { + if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) + || (mode != LookupMode::Path) + { + continue; } } + _ => {} + }; + if !known_implemented { + let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); + if db.trait_solve(krate, goal).is_none() { + continue 'traits; + } + } + known_implemented = true; + if let Some(result) = callback(&ty.value, item) { + return Some(result); } } } @@ -256,21 +286,35 @@ fn iterate_inherent_methods( ty: &Canonical, db: &impl HirDatabase, name: Option<&Name>, + mode: LookupMode, krate: Crate, - mut callback: impl FnMut(&Ty, Function) -> Option, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { for krate in def_crates(db, krate, &ty.value)? { let impls = db.impls_in_crate(krate); for impl_block in impls.lookup_impl_blocks(&ty.value) { for item in impl_block.items(db) { - if let AssocItem::Function(f) = item { - let data = f.data(db); - if name.map_or(true, |name| data.name() == name) && data.has_self_param() { - if let Some(result) = callback(&ty.value, f) { - return Some(result); + match item { + AssocItem::Function(f) => { + let data = f.data(db); + if !name.map_or(true, |name| data.name() == name) + || (!data.has_self_param() && mode != LookupMode::Path) + { + continue; } } + AssocItem::Const(c) => { + if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) + || (mode != LookupMode::Path) + { + continue; + } + } + _ => {} + } + if let Some(result) = callback(&ty.value, item) { + return Some(result); } } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e071e4d4e..bfef48b16 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -1841,8 +1841,8 @@ fn test() { [243; 254) 'Struct::FOO': u32 [264; 265) 'y': u32 [268; 277) 'Enum::BAR': u32 - [287; 288) 'z': {unknown} - [291; 304) 'TraitTest::ID': {unknown} + [287; 288) 'z': u32 + [291; 304) 'TraitTest::ID': u32 "### ); } -- cgit v1.2.3 From f4181deb0dd2cce2184df0057cf349628a70f2e9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 31 Oct 2019 20:01:46 +0100 Subject: Don't do autoderef for path resolution --- crates/ra_hir/src/ty/method_resolution.rs | 57 +++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 18 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index ee0c7b00f..332bb14b2 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -191,27 +191,48 @@ pub(crate) fn iterate_method_candidates( mode: LookupMode, mut callback: impl FnMut(&Ty, AssocItem) -> 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. + let krate = resolver.krate()?; + 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). + // 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()?; - // TODO no autoderef in LookupMode::Path - for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { - if let Some(result) = - iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) - { - return Some(result); + for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { + if let Some(result) = + iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) + { + return Some(result); + } + if let Some(result) = iterate_trait_method_candidates( + &derefed_ty, + db, + resolver, + name, + mode, + &mut callback, + ) { + return Some(result); + } + } } - if let Some(result) = - iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback) - { - return Some(result); + LookupMode::Path => { + // No autoderef for path lookups + if let Some(result) = + iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback) + { + return Some(result); + } + if let Some(result) = + iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) + { + return Some(result); + } } } None -- cgit v1.2.3 From b0bf1deb7c99d1c5b597b9b43b79a8fd3d24ddd7 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 31 Oct 2019 20:37:46 +0100 Subject: Refactor a bit --- crates/ra_hir/src/ty/method_resolution.rs | 61 +++++++++++++------------------ 1 file changed, 25 insertions(+), 36 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 332bb14b2..43b485ec0 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -269,25 +269,9 @@ fn iterate_trait_method_candidates( // iteration let mut known_implemented = inherently_implemented; for &item in data.items() { - // TODO unify with the impl case - match item { - AssocItem::Function(m) => { - let data = m.data(db); - if !name.map_or(true, |name| data.name() == name) - || (!data.has_self_param() && mode != LookupMode::Path) - { - continue; - } - } - AssocItem::Const(c) => { - if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) - || (mode != LookupMode::Path) - { - continue; - } - } - _ => {} - }; + if !is_valid_candidate(db, name, mode, item) { + continue; + } if !known_implemented { let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); if db.trait_solve(krate, goal).is_none() { @@ -316,23 +300,8 @@ fn iterate_inherent_methods( for impl_block in impls.lookup_impl_blocks(&ty.value) { for item in impl_block.items(db) { - match item { - AssocItem::Function(f) => { - let data = f.data(db); - if !name.map_or(true, |name| data.name() == name) - || (!data.has_self_param() && mode != LookupMode::Path) - { - continue; - } - } - AssocItem::Const(c) => { - if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) - || (mode != LookupMode::Path) - { - continue; - } - } - _ => {} + if !is_valid_candidate(db, name, mode, item) { + continue; } if let Some(result) = callback(&ty.value, item) { return Some(result); @@ -343,6 +312,26 @@ fn iterate_inherent_methods( None } +fn is_valid_candidate( + db: &impl HirDatabase, + name: Option<&Name>, + mode: LookupMode, + item: AssocItem, +) -> bool { + match item { + AssocItem::Function(m) => { + let data = m.data(db); + name.map_or(true, |name| data.name() == name) + && (data.has_self_param() || mode == LookupMode::Path) + } + AssocItem::Const(c) => { + name.map_or(true, |name| Some(name) == c.name(db).as_ref()) + && (mode == LookupMode::Path) + } + _ => false, + } +} + pub(crate) fn implements_trait( ty: &Canonical, db: &impl HirDatabase, -- cgit v1.2.3 From 79cb0a0dab5fd8e3e84cf4a3b927ec29d2b6e65c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 31 Oct 2019 21:21:48 +0100 Subject: Complete trait assoc items --- crates/ra_hir/src/ty/method_resolution.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 43b485ec0..9caff422f 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -176,7 +176,7 @@ pub(crate) fn lookup_method( } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum LookupMode { +pub enum LookupMode { MethodCall, Path, } -- cgit v1.2.3 From dc4066ebed57d43068035f574244e3abd18ee67f Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 1 Nov 2019 11:53:29 +0100 Subject: Add some doc comments --- crates/ra_hir/src/ty/method_resolution.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 9caff422f..8d682bb18 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -175,9 +175,15 @@ pub(crate) fn lookup_method( }) } +/// Whether we're looking up a dotted method call (like `v.len()`) or a path +/// (like `Vec::new`). #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LookupMode { + /// Looking up a method call like `v.len()`: We only consider candidates + /// that have a `self` parameter, and do autoderef. MethodCall, + /// Looking up a path like `Vec::new` or `Vec::default`: We consider all + /// candidates including associated constants, but don't do autoderef. Path, } -- cgit v1.2.3 From b29092ade31d7ff37532649dfbe1dc811edf3651 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 1 Nov 2019 19:56:56 +0100 Subject: Various review fixes --- crates/ra_hir/src/ty/infer/path.rs | 18 +++++++----------- crates/ra_hir/src/ty/method_resolution.rs | 11 +++++------ 2 files changed, 12 insertions(+), 17 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 1946bf608..59b7f7eb6 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -196,13 +196,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { AssocItem::Const(c) => ValueNs::Const(c), AssocItem::TypeAlias(_) => unreachable!(), }; - match item.container(self.db) { - Container::ImplBlock(_) => { - let substs = self.find_self_types(&def, ty.clone()); - - self.write_assoc_resolution(id, item); - Some((def, substs)) - } + let substs = match item.container(self.db) { + Container::ImplBlock(_) => self.find_self_types(&def, ty.clone()), Container::Trait(t) => { // we're picking this method let trait_substs = Substs::build_for_def(self.db, t) @@ -217,11 +212,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { trait_: t, substs: trait_substs, })); - - self.write_assoc_resolution(id, item); - Some((def, Some(substs))) + Some(substs) } - } + }; + + self.write_assoc_resolution(id, item); + Some((def, substs)) }, ) } diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 8d682bb18..8c3d32d09 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -166,12 +166,10 @@ pub(crate) fn lookup_method( name: &Name, resolver: &Resolver, ) -> Option<(Ty, Function)> { - iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| { - if let AssocItem::Function(f) = f { - Some((ty.clone(), f)) - } else { - None - } + iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f + { + AssocItem::Function(f) => Some((ty.clone(), f)), + _ => None, }) } @@ -189,6 +187,7 @@ pub enum LookupMode { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplBlocks`. +// FIXME add a context type here? pub(crate) fn iterate_method_candidates( ty: &Canonical, db: &impl HirDatabase, -- cgit v1.2.3