From 16ee779483a1d96ccd2258b43c8477744773b314 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 14 Sep 2019 12:28:35 +0200 Subject: Adapt some tests --- crates/ra_hir/src/ty/tests.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (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 1bd677cab..b5d3c0180 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2613,18 +2613,18 @@ fn infer_call_trait_method_on_generic_param_1() { assert_snapshot!( infer(r#" trait Trait { - fn method() -> u32; + fn method(&self) -> u32; } fn test(t: T) { t.method(); } "#), @r###" - - [59; 60) 't': T - [65; 84) '{ ...d(); }': () - [71; 72) 't': T - [71; 81) 't.method()': {unknown} + [30; 34) 'self': &Self + [64; 65) 't': T + [70; 89) '{ ...d(); }': () + [76; 77) 't': T + [76; 86) 't.method()': u32 "### ); } @@ -2634,18 +2634,18 @@ fn infer_call_trait_method_on_generic_param_2() { assert_snapshot!( infer(r#" trait Trait { - fn method() -> T; + fn method(&self) -> T; } fn test>(t: T) { t.method(); } "#), @r###" - - [66; 67) 't': T - [72; 91) '{ ...d(); }': () - [78; 79) 't': T - [78; 88) 't.method()': {unknown} + [33; 37) 'self': &Self + [71; 72) 't': T + [77; 96) '{ ...d(); }': () + [83; 84) 't': T + [83; 93) 't.method()': [missing name] "### ); } -- cgit v1.2.3 From 913ab1ec0ad10873134ca429c1496806a9261206 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 14 Sep 2019 13:25:05 +0200 Subject: Resolve assoc types on type parameters E.g. `fn foo() -> T::Item`. It seems that rustc does this only for type parameters and only based on their bounds, so we also only consider traits from bounds. --- crates/ra_hir/src/ty/lower.rs | 79 +++++++++++++++++++++++++++++++------------ crates/ra_hir/src/ty/tests.rs | 6 ++-- 2 files changed, 61 insertions(+), 24 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 6ead3846a..62e4ed0f4 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -93,7 +93,7 @@ impl Ty { None => return Ty::Unknown, }; - let typable: TypableDef = match resolution { + let ty = match resolution { TypeNs::Trait(trait_) => { let segment = match remaining_index { None => path.segments.last().expect("resolved path has at least one element"), @@ -115,12 +115,12 @@ impl Ty { }) } None => { - // associated type not found + // associated type not found (FIXME: report error) Ty::Unknown } } } else { - // FIXME more than one segment remaining, is this possible? + // FIXME report error (ambiguous associated type) Ty::Unknown } } else { @@ -128,34 +128,71 @@ impl Ty { }; } TypeNs::GenericParam(idx) => { - if remaining_index.is_some() { - // e.g. T::Item - return Ty::Unknown; - } - return Ty::Param { - idx, - // FIXME: maybe return name in resolution? - name: path + // FIXME: maybe return name in resolution? + let name = match remaining_index { + None => path .as_ident() .expect("generic param should be single-segment path") .clone(), + Some(idx) => path.segments[idx - 1].name.clone(), }; + Ty::Param { idx, name } } - TypeNs::SelfType(impl_block) => { - if remaining_index.is_some() { - // e.g. Self::Item - return Ty::Unknown; - } - return impl_block.target_ty(db); - } + TypeNs::SelfType(impl_block) => impl_block.target_ty(db), - TypeNs::Adt(it) => it.into(), - TypeNs::BuiltinType(it) => it.into(), - TypeNs::TypeAlias(it) => it.into(), + TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), + TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), + TypeNs::TypeAlias(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), // FIXME: report error TypeNs::EnumVariant(_) => return Ty::Unknown, }; + if let Some(remaining_index) = remaining_index { + // resolve unselected assoc types + if remaining_index == path.segments.len() - 1 { + let segment = &path.segments[remaining_index]; + Ty::select_associated_type(db, resolver, ty, segment) + } else { + // FIXME report error (ambiguous associated type) + Ty::Unknown + } + } else { + ty + } + } + + fn select_associated_type( + db: &impl HirDatabase, + resolver: &Resolver, + self_ty: Ty, + segment: &PathSegment, + ) -> Ty { + let env = trait_env(db, resolver); + let traits_from_env = env.trait_predicates_for_self_ty(&self_ty).map(|tr| tr.trait_); + let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); + let mut result = Ty::Unknown; + for t in traits { + if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { + let generics = t.generic_params(db); + let mut substs = Vec::new(); + substs.push(self_ty.clone()); + substs.extend( + iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1), + ); + // FIXME handle type parameters on the segment + result = Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); + break; + } + } + result + } + + fn from_hir_path_inner( + db: &impl HirDatabase, + resolver: &Resolver, + path: &Path, + typable: TypableDef, + ) -> Ty { let ty = db.type_for_def(typable, Namespace::Types); let substs = Ty::substs_from_path(db, resolver, path, typable); ty.subst(&substs) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index b5d3c0180..dc4e8683d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2732,9 +2732,9 @@ fn test() { [147; 148) 't': T [178; 180) '{}': () [191; 236) '{ ...(S); }': () - [201; 202) 'x': {unknown} - [205; 209) 'foo1': fn foo1(T) -> {unknown} - [205; 212) 'foo1(S)': {unknown} + [201; 202) 'x': u32 + [205; 209) 'foo1': fn foo1(T) -> ::Item + [205; 212) 'foo1(S)': u32 [210; 211) 'S': S [222; 223) 'y': u32 [226; 230) 'foo2': fn foo2(T) -> ::Item -- cgit v1.2.3 From 828d60574f8ecbc33fe4987913c6f713e41af1ae Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 14 Sep 2019 16:26:03 +0200 Subject: Refactor a bit to prepare for resolving trait assoc items --- crates/ra_hir/src/ty/infer.rs | 57 ++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 30 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index bf9609d8c..6aaf61c0e 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -48,7 +48,7 @@ use crate::{ resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - Adt, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path, + Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path, StructField, }; @@ -121,7 +121,7 @@ pub struct InferenceResult { /// For each struct literal, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to - assoc_resolutions: FxHashMap, + assoc_resolutions: FxHashMap, diagnostics: Vec, pub(super) type_of_expr: ArenaMap, pub(super) type_of_pat: ArenaMap, @@ -141,10 +141,10 @@ impl InferenceResult { pub fn variant_resolution_for_pat(&self, id: PatId) -> Option { self.variant_resolutions.get(&id.into()).copied() } - pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option { + pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option { self.assoc_resolutions.get(&id.into()).copied() } - pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { + pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option { self.assoc_resolutions.get(&id.into()).copied() } pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { @@ -235,7 +235,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.result.variant_resolutions.insert(id, variant); } - fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: ImplItem) { + fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItem) { self.result.assoc_resolutions.insert(id, item); } @@ -560,8 +560,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = mem::replace(&mut ty, Ty::Unknown); def_or_ty = ty.iterate_impl_items(self.db, krate, |item| { match item { - crate::ImplItem::Method(_) => None, - crate::ImplItem::Const(_) => None, + crate::ImplItem::Method(_) | crate::ImplItem::Const(_) => None, // FIXME: Resolve associated types crate::ImplItem::TypeAlias(_) => { @@ -573,34 +572,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } let segment = path.segments.last().unwrap(); - let def = ty.clone().iterate_impl_items(self.db, krate, |item| { - let matching_def: Option = match item { - crate::ImplItem::Method(func) => { - if segment.name == func.name(self.db) { - Some(ValueNs::Function(func)) - } else { - None - } + let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { + crate::ImplItem::Method(func) => { + if segment.name == func.name(self.db) { + Some(ValueNs::Function(func)) + } else { + None } + } - crate::ImplItem::Const(konst) => { - let data = konst.data(self.db); - if Some(&segment.name) == data.name() { - Some(ValueNs::Const(konst)) - } else { - None - } - } - crate::ImplItem::TypeAlias(_) => None, - }; - match matching_def { - Some(_) => { - self.write_assoc_resolution(id, item); - matching_def + crate::ImplItem::Const(konst) => { + if konst.name(self.db).map_or(false, |n| n == segment.name) { + Some(ValueNs::Const(konst)) + } else { + None } - None => None, } + crate::ImplItem::TypeAlias(_) => None, })?; + self.write_assoc_resolution( + id, + match def { + ValueNs::Function(f) => AssocItem::Function(f), + ValueNs::Const(c) => AssocItem::Const(c), + _ => unreachable!(), + }, + ); let self_types = self.find_self_types(&def, ty); Some((def, self_types)) } -- cgit v1.2.3 From 406280e52f20e25af609d947efbed8b352ca1249 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 15 Sep 2019 12:50:57 +0200 Subject: Refactor associated item resolution more When resolving an associated item in value namespace, use the `Ty` lowering code for the segments before the last instead of replicating it. --- crates/ra_hir/src/ty/infer.rs | 125 ++++++++++++++++++------------------------ crates/ra_hir/src/ty/lower.rs | 119 ++++++++++++++++++++++------------------ 2 files changed, 120 insertions(+), 124 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 6aaf61c0e..f33479dc4 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -48,7 +48,7 @@ use crate::{ resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path, + Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, Name, Path, StructField, }; @@ -508,7 +508,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_assoc_item( &mut self, - mut def_or_ty: Either, + // mut def_or_ty: Either, + def: TypeNs, path: &Path, remaining_index: usize, id: ExprOrPatId, @@ -516,80 +517,63 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { assert!(remaining_index < path.segments.len()); let krate = self.resolver.krate()?; - let mut ty = Ty::Unknown; - - // resolve intermediate segments - for (i, segment) in path.segments[remaining_index..].iter().enumerate() { - let is_last_segment = i == path.segments[remaining_index..].len() - 1; - ty = match def_or_ty { - Either::A(def) => { - let typable: TypableDef = match def { - TypeNs::Adt(it) => it.into(), - TypeNs::TypeAlias(it) => it.into(), - TypeNs::BuiltinType(it) => it.into(), - // FIXME associated item of traits, generics, and Self - TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => { - return None; - } - // FIXME: report error here - TypeNs::EnumVariant(_) => return None, - }; - - let ty = self.db.type_for_def(typable, Namespace::Types); - - // For example, this substs will take `Gen::**::make` - assert!(remaining_index > 0); - let substs = Ty::substs_from_path_segment( - self.db, - &self.resolver, - &path.segments[remaining_index + i - 1], - typable, - ); - ty.subst(&substs) - } - Either::B(ty) => ty, - }; - if is_last_segment { - break; - } + // there may be more intermediate segments between the resolved one and + // the end. Only the last segment needs to be resolved to a value; from + // the segments before that, we need to get either a type or a trait ref. - // Attempt to find an impl_item for the type which has a name matching - // the current segment - log::debug!("looking for path segment: {:?}", segment); + let resolved_segment = &path.segments[remaining_index - 1]; + let remaining_segments = &path.segments[remaining_index..]; + let is_before_last = remaining_segments.len() == 1; - let ty = mem::replace(&mut ty, Ty::Unknown); - def_or_ty = ty.iterate_impl_items(self.db, krate, |item| { - match item { - crate::ImplItem::Method(_) | crate::ImplItem::Const(_) => None, + let (def, substs) = match (def, is_before_last) { + (TypeNs::Trait(_trait), true) => { + // Associated item of trait, e.g. `Default::default` + // FIXME + return None; + } + (def, _) => { + // Either we already have a type (e.g. `Vec::new`), or we have a + // trait but it's not the last segment, so the next segment + // should resolve to an associated type of that trait (e.g. `::Item::default`) + let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1]; + let ty = Ty::from_partly_resolved_hir_path( + self.db, + &self.resolver, + def, + resolved_segment, + remaining_segments_for_ty, + ); + if let Ty::Unknown = ty { + return None; + } - // FIXME: Resolve associated types - crate::ImplItem::TypeAlias(_) => { - // Some(Either::A(TypeNs::TypeAlias(..))) - None + let segment = + remaining_segments.last().expect("there should be at least one segment here"); + // Find impl + let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { + crate::ImplItem::Method(func) => { + if segment.name == func.name(self.db) { + Some(ValueNs::Function(func)) + } else { + None + } } - } - })?; - } - let segment = path.segments.last().unwrap(); - let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { - crate::ImplItem::Method(func) => { - if segment.name == func.name(self.db) { - Some(ValueNs::Function(func)) - } else { - None - } + crate::ImplItem::Const(konst) => { + if segment.name == konst.name(self.db) { + Some(ValueNs::Const(konst)) + } else { + None + } + } + crate::ImplItem::TypeAlias(_) => None, + })?; + let self_types = self.find_self_types(&def, ty); + (def, self_types) } + }; - crate::ImplItem::Const(konst) => { - if konst.name(self.db).map_or(false, |n| n == segment.name) { - Some(ValueNs::Const(konst)) - } else { - None - } - } - crate::ImplItem::TypeAlias(_) => None, - })?; self.write_assoc_resolution( id, match def { @@ -598,8 +582,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { _ => unreachable!(), }, ); - let self_types = self.find_self_types(&def, ty); - Some((def, self_types)) + Some((def, substs)) } fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 62e4ed0f4..e29b68f1a 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -86,81 +86,94 @@ impl Ty { } } - pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty { - // Resolve the path (in type namespace) - let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { - Some(it) => it, - None => return Ty::Unknown, - }; - + pub(crate) fn from_partly_resolved_hir_path( + db: &impl HirDatabase, + resolver: &Resolver, + resolution: TypeNs, + resolved_segment: &PathSegment, + remaining_segments: &[PathSegment], + ) -> Ty { let ty = match resolution { TypeNs::Trait(trait_) => { - let segment = match remaining_index { - None => path.segments.last().expect("resolved path has at least one element"), - Some(i) => &path.segments[i - 1], - }; - let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None); - return if let Some(remaining_index) = remaining_index { - if remaining_index == path.segments.len() - 1 { - let segment = &path.segments[remaining_index]; - match trait_ref - .trait_ - .associated_type_by_name_including_super_traits(db, &segment.name) - { - Some(associated_ty) => { - // FIXME handle type parameters on the segment - Ty::Projection(ProjectionTy { - associated_ty, - parameters: trait_ref.substs, - }) - } - None => { - // associated type not found (FIXME: report error) - Ty::Unknown - } + let trait_ref = + TraitRef::from_resolved_path(db, resolver, trait_, resolved_segment, None); + return if remaining_segments.len() == 1 { + let segment = &remaining_segments[0]; + match trait_ref + .trait_ + .associated_type_by_name_including_super_traits(db, &segment.name) + { + Some(associated_ty) => { + // FIXME handle type parameters on the segment + Ty::Projection(ProjectionTy { + associated_ty, + parameters: trait_ref.substs, + }) + } + None => { + // associated type not found (FIXME: report error) + Ty::Unknown } - } else { - // FIXME report error (ambiguous associated type) - Ty::Unknown } + } else if remaining_segments.len() > 1 { + // FIXME report error (ambiguous associated type) + Ty::Unknown } else { Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)])) }; } TypeNs::GenericParam(idx) => { // FIXME: maybe return name in resolution? - let name = match remaining_index { - None => path - .as_ident() - .expect("generic param should be single-segment path") - .clone(), - Some(idx) => path.segments[idx - 1].name.clone(), - }; + let name = resolved_segment.name.clone(); Ty::Param { idx, name } } TypeNs::SelfType(impl_block) => impl_block.target_ty(db), - TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), - TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), - TypeNs::TypeAlias(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), + TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()), + TypeNs::BuiltinType(it) => { + Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()) + } + TypeNs::TypeAlias(it) => { + Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()) + } // FIXME: report error TypeNs::EnumVariant(_) => return Ty::Unknown, }; - if let Some(remaining_index) = remaining_index { + if remaining_segments.len() == 1 { // resolve unselected assoc types - if remaining_index == path.segments.len() - 1 { - let segment = &path.segments[remaining_index]; - Ty::select_associated_type(db, resolver, ty, segment) - } else { - // FIXME report error (ambiguous associated type) - Ty::Unknown - } + let segment = &remaining_segments[0]; + Ty::select_associated_type(db, resolver, ty, segment) + } else if remaining_segments.len() > 1 { + // FIXME report error (ambiguous associated type) + Ty::Unknown } else { ty } } + pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty { + // Resolve the path (in type namespace) + let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { + Some(it) => it, + None => return Ty::Unknown, + }; + let (resolved_segment, remaining_segments) = match remaining_index { + None => ( + path.segments.last().expect("resolved path has at least one element"), + &[] as &[PathSegment], + ), + Some(i) => (&path.segments[i - 1], &path.segments[i..]), + }; + Ty::from_partly_resolved_hir_path( + db, + resolver, + resolution, + resolved_segment, + remaining_segments, + ) + } + fn select_associated_type( db: &impl HirDatabase, resolver: &Resolver, @@ -190,11 +203,11 @@ impl Ty { fn from_hir_path_inner( db: &impl HirDatabase, resolver: &Resolver, - path: &Path, + segment: &PathSegment, typable: TypableDef, ) -> Ty { let ty = db.type_for_def(typable, Namespace::Types); - let substs = Ty::substs_from_path(db, resolver, path, typable); + let substs = Ty::substs_from_path_segment(db, resolver, segment, typable); ty.subst(&substs) } -- cgit v1.2.3 From fe1dfd2b20b256b99f40f6f6421f7c7e12c23e41 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 16 Sep 2019 21:38:27 +0200 Subject: Refactor some more Type-relative paths (`::foo`) also need to work in type context, for example `::Item` is legal. So rather than returning the type ref from the resolver function, just check it before. --- crates/ra_hir/src/ty/infer.rs | 100 ++++++++++++++++++++++++++---------------- crates/ra_hir/src/ty/lower.rs | 34 +++++++++----- 2 files changed, 85 insertions(+), 49 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index f33479dc4..181be0fcc 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -48,8 +48,7 @@ use crate::{ resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, Name, Path, - StructField, + Adt, AssocItem, ConstData, DefWithBody, FnData, Function, HasBody, Name, Path, StructField, }; mod unify; @@ -468,16 +467,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option { - let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; - - let (value, self_subst) = match value_or_partial { - ResolveValueResult::ValueNs(it) => (it, None), - ResolveValueResult::Partial(def, remaining_index) => { - self.resolve_assoc_item(Either::A(def), path, remaining_index, id)? + let (value, self_subst) = if let crate::PathKind::Type(type_ref) = &path.kind { + if path.segments.is_empty() { + // This can't actually happen syntax-wise + return None; } - ResolveValueResult::TypeRef(type_ref) => { - let ty = self.make_ty(type_ref); - self.resolve_assoc_item(Either::B(ty), path, 0, id)? + let ty = self.make_ty(type_ref); + let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1]; + 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"), + id, + )? + } else { + let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; + + match value_or_partial { + ResolveValueResult::ValueNs(it) => (it, None), + ResolveValueResult::Partial(def, remaining_index) => { + self.resolve_assoc_item(def, path, remaining_index, id)? + } } }; @@ -508,15 +518,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_assoc_item( &mut self, - // mut def_or_ty: Either, def: TypeNs, path: &Path, remaining_index: usize, id: ExprOrPatId, ) -> Option<(ValueNs, Option)> { assert!(remaining_index < path.segments.len()); - let krate = self.resolver.krate()?; - // there may be more intermediate segments between the resolved one and // the end. Only the last segment needs to be resolved to a value; from // the segments before that, we need to get either a type or a trait ref. @@ -525,11 +532,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let remaining_segments = &path.segments[remaining_index..]; let is_before_last = remaining_segments.len() == 1; - let (def, substs) = match (def, is_before_last) { + match (def, is_before_last) { (TypeNs::Trait(_trait), true) => { - // Associated item of trait, e.g. `Default::default` - // FIXME - return None; + // FIXME Associated item of trait, e.g. `Default::default` + None } (def, _) => { // Either we already have a type (e.g. `Vec::new`), or we have a @@ -550,29 +556,45 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let segment = remaining_segments.last().expect("there should be at least one segment here"); - // Find impl - let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { - crate::ImplItem::Method(func) => { - if segment.name == func.name(self.db) { - Some(ValueNs::Function(func)) - } else { - None - } - } - crate::ImplItem::Const(konst) => { - if segment.name == konst.name(self.db) { - Some(ValueNs::Const(konst)) - } else { - None - } - } - crate::ImplItem::TypeAlias(_) => None, - })?; - let self_types = self.find_self_types(&def, ty); - (def, self_types) + self.resolve_ty_assoc_item(ty, segment, id) } - }; + } + } + + fn resolve_ty_assoc_item( + &mut self, + ty: Ty, + segment: &crate::path::PathSegment, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + if let Ty::Unknown = ty { + return None; + } + + let krate = self.resolver.krate()?; + + // Find impl + // FIXME: consider trait candidates + let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { + crate::ImplItem::Method(func) => { + if segment.name == func.name(self.db) { + Some(ValueNs::Function(func)) + } else { + None + } + } + + crate::ImplItem::Const(konst) => { + if konst.name(self.db).map_or(false, |n| n == segment.name) { + Some(ValueNs::Const(konst)) + } else { + None + } + } + crate::ImplItem::TypeAlias(_) => None, + })?; + let substs = self.find_self_types(&def, ty); self.write_assoc_resolution( id, diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index e29b68f1a..e6cd5d0be 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -86,6 +86,24 @@ impl Ty { } } + pub(crate) fn from_type_relative_path( + db: &impl HirDatabase, + resolver: &Resolver, + ty: Ty, + remaining_segments: &[PathSegment], + ) -> Ty { + if remaining_segments.len() == 1 { + // resolve unselected assoc types + let segment = &remaining_segments[0]; + Ty::select_associated_type(db, resolver, ty, segment) + } else if remaining_segments.len() > 1 { + // FIXME report error (ambiguous associated type) + Ty::Unknown + } else { + ty + } + } + pub(crate) fn from_partly_resolved_hir_path( db: &impl HirDatabase, resolver: &Resolver, @@ -140,20 +158,16 @@ impl Ty { TypeNs::EnumVariant(_) => return Ty::Unknown, }; - if remaining_segments.len() == 1 { - // resolve unselected assoc types - let segment = &remaining_segments[0]; - Ty::select_associated_type(db, resolver, ty, segment) - } else if remaining_segments.len() > 1 { - // FIXME report error (ambiguous associated type) - Ty::Unknown - } else { - ty - } + Ty::from_type_relative_path(db, resolver, ty, remaining_segments) } pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty { // Resolve the path (in type namespace) + if let crate::PathKind::Type(type_ref) = &path.kind { + let ty = Ty::from_hir(db, resolver, &type_ref); + let remaining_segments = &path.segments[..]; + return Ty::from_type_relative_path(db, resolver, ty, remaining_segments); + } let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { Some(it) => it, None => return Ty::Unknown, -- cgit v1.2.3 From 35d1c038967b9d69f2e501e24fec8351f3948779 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 16 Sep 2019 21:46:51 +0200 Subject: Add test for `::Item` --- crates/ra_hir/src/ty/tests.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (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 dc4e8683d..fdcb3c37f 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2685,6 +2685,7 @@ fn test() { #[test] fn infer_project_associated_type() { + // y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234 assert_snapshot!( infer(r#" trait Iterable { @@ -2696,16 +2697,19 @@ fn test() { let x: ::Item = 1; let y: ::Item = no_matter; let z: T::Item = no_matter; + let a: ::Item = no_matter; } "#), @r###" - [108; 227) '{ ...ter; }': () + [108; 261) '{ ...ter; }': () [118; 119) 'x': u32 [145; 146) '1': u32 [156; 157) 'y': {unknown} [183; 192) 'no_matter': {unknown} [202; 203) 'z': {unknown} [215; 224) 'no_matter': {unknown} + [234; 235) 'a': {unknown} + [249; 258) 'no_matter': {unknown} "### ); } @@ -2721,9 +2725,11 @@ struct S; impl Iterable for S { type Item = u32; } fn foo1(t: T) -> T::Item {} fn foo2(t: T) -> ::Item {} +fn foo3(t: T) -> ::Item {} fn test() { let x = foo1(S); let y = foo2(S); + let z = foo3(S); } "#), @r###" @@ -2731,15 +2737,21 @@ fn test() { [123; 125) '{}': () [147; 148) 't': T [178; 180) '{}': () - [191; 236) '{ ...(S); }': () - [201; 202) 'x': u32 - [205; 209) 'foo1': fn foo1(T) -> ::Item - [205; 212) 'foo1(S)': u32 - [210; 211) 'S': S - [222; 223) 'y': u32 - [226; 230) 'foo2': fn foo2(T) -> ::Item - [226; 233) 'foo2(S)': u32 - [231; 232) 'S': S + [202; 203) 't': T + [221; 223) '{}': () + [234; 300) '{ ...(S); }': () + [244; 245) 'x': u32 + [248; 252) 'foo1': fn foo1(T) -> ::Item + [248; 255) 'foo1(S)': u32 + [253; 254) 'S': S + [265; 266) 'y': u32 + [269; 273) 'foo2': fn foo2(T) -> ::Item + [269; 276) 'foo2(S)': u32 + [274; 275) 'S': S + [286; 287) 'z': u32 + [290; 294) 'foo3': fn foo3(T) -> ::Item + [290; 297) 'foo3(S)': u32 + [295; 296) 'S': S "### ); } -- cgit v1.2.3 From 53a932509dbb9dfdaabb27a0d7fc3fe87e44ec55 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 16 Sep 2019 21:48:46 +0200 Subject: Small review improvements --- crates/ra_hir/src/ty/lower.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index e6cd5d0be..35fdd4a37 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -129,7 +129,7 @@ impl Ty { }) } None => { - // associated type not found (FIXME: report error) + // FIXME: report error (associated type not found) Ty::Unknown } } @@ -197,7 +197,6 @@ impl Ty { let env = trait_env(db, resolver); let traits_from_env = env.trait_predicates_for_self_ty(&self_ty).map(|tr| tr.trait_); let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); - let mut result = Ty::Unknown; for t in traits { if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { let generics = t.generic_params(db); @@ -207,11 +206,10 @@ impl Ty { iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1), ); // FIXME handle type parameters on the segment - result = Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); - break; + return Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); } } - result + Ty::Unknown } fn from_hir_path_inner( -- cgit v1.2.3 From a040fde3ae5e339199237f24a7461812e74475bf Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 16 Sep 2019 22:01:13 +0200 Subject: Remove TraitItem and ImplItem in favor of AssocItem --- crates/ra_hir/src/ty/infer.rs | 26 ++++++++++++-------------- crates/ra_hir/src/ty/method_resolution.rs | 11 +++++------ crates/ra_hir/src/ty/traits/chalk.rs | 6 +++--- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 181be0fcc..70da7f311 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -576,34 +576,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // Find impl // FIXME: consider trait candidates - let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { - crate::ImplItem::Method(func) => { + let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { + AssocItem::Function(func) => { if segment.name == func.name(self.db) { - Some(ValueNs::Function(func)) + Some(AssocItem::Function(func)) } else { None } } - crate::ImplItem::Const(konst) => { + AssocItem::Const(konst) => { if konst.name(self.db).map_or(false, |n| n == segment.name) { - Some(ValueNs::Const(konst)) + Some(AssocItem::Const(konst)) } else { None } } - crate::ImplItem::TypeAlias(_) => 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, - match def { - ValueNs::Function(f) => AssocItem::Function(f), - ValueNs::Const(c) => AssocItem::Const(c), - _ => unreachable!(), - }, - ); + 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 be63806d4..8b46b11a9 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -11,13 +11,12 @@ use super::{autoderef, lower, Canonical, InEnvironment, TraitEnvironment, TraitR use crate::{ db::HirDatabase, generics::HasGenericParams, - impl_block::{ImplBlock, ImplId, ImplItem}, + impl_block::{ImplBlock, ImplId}, nameres::CrateModuleId, resolve::Resolver, - traits::TraitItem, ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy}, ty::{Ty, TypeCtor}, - Crate, Function, Module, Name, Trait, + AssocItem, Crate, Function, Module, Name, Trait, }; /// This is used as a key for indexing impls. @@ -232,7 +231,7 @@ fn iterate_trait_method_candidates( // iteration let mut known_implemented = inherently_implemented; for item in data.items() { - if let TraitItem::Function(m) = *item { + 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 { @@ -264,7 +263,7 @@ fn iterate_inherent_methods( for impl_block in impls.lookup_impl_blocks(&ty.value) { for item in impl_block.items(db) { - if let ImplItem::Method(f) = item { + 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) { @@ -304,7 +303,7 @@ impl Ty { self, db: &impl HirDatabase, krate: Crate, - mut callback: impl FnMut(ImplItem) -> Option, + mut callback: impl FnMut(AssocItem) -> Option, ) -> Option { for krate in def_crates(db, krate, &self)? { let impls = db.impls_in_crate(krate); diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index c748e9d84..693d9b28f 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -21,7 +21,7 @@ use crate::{ ApplicationTy, CallableDef, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, }, - Crate, HasGenericParams, ImplBlock, ImplItem, Trait, TypeAlias, + AssocItem, Crate, HasGenericParams, ImplBlock, Trait, TypeAlias, }; /// This represents a trait whose name we could not resolve. @@ -496,7 +496,7 @@ pub(crate) fn trait_datum_query( .items(db) .into_iter() .filter_map(|trait_item| match trait_item { - crate::traits::TraitItem::TypeAlias(type_alias) => Some(type_alias), + crate::AssocItem::TypeAlias(type_alias) => Some(type_alias), _ => None, }) .map(|type_alias| type_alias.to_chalk(db)) @@ -616,7 +616,7 @@ pub(crate) fn impl_datum_query( .items(db) .into_iter() .filter_map(|item| match item { - ImplItem::TypeAlias(t) => Some(t), + AssocItem::TypeAlias(t) => Some(t), _ => None, }) .filter_map(|t| { -- cgit v1.2.3 From 69c8cfc4c17e9f4533437c757e754a140c0a9ebc Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 17 Sep 2019 23:06:05 +0200 Subject: Add test for `T::Item` cycles --- crates/ra_hir/src/ty/tests.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 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 fdcb3c37f..9a3faa9ab 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -4062,6 +4062,48 @@ fn test u64>(f: F) { ); } +#[test] +fn unselected_projection_in_trait_env() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +trait Trait2 { + fn foo(&self) -> u32; +} + +fn test() where T::Item: Trait2 { + let x: T::Item = no_matter; + x.foo()<|>; +} +"#, + ); + assert_eq!(t, "u32"); +} + +#[test] +fn unselected_projection_in_trait_env_cycle() { + let t = type_at( + r#" +//- /main.rs +trait Trait { + type Item; +} + +trait Trait2 {} + +fn test() where T: Trait2 { + let x: T::Item = no_matter<|>; +} +"#, + ); + // this is a legitimate cycle + assert_eq!(t, "{unknown}"); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); -- cgit v1.2.3 From c2f9558e1af8dbf73ff86eeffcb9ea6940947dd6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 17 Sep 2019 23:11:20 +0200 Subject: Remove assoc type selection code for now to fix crashes --- crates/ra_hir/src/ty/lower.rs | 23 ++++------------------- crates/ra_hir/src/ty/tests.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 25 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 35fdd4a37..a83842b0f 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -189,26 +189,11 @@ impl Ty { } fn select_associated_type( - db: &impl HirDatabase, - resolver: &Resolver, - self_ty: Ty, - segment: &PathSegment, + _db: &impl HirDatabase, + _resolver: &Resolver, + _self_ty: Ty, + _segment: &PathSegment, ) -> Ty { - let env = trait_env(db, resolver); - let traits_from_env = env.trait_predicates_for_self_ty(&self_ty).map(|tr| tr.trait_); - let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); - for t in traits { - if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { - let generics = t.generic_params(db); - let mut substs = Vec::new(); - substs.push(self_ty.clone()); - substs.extend( - iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1), - ); - // FIXME handle type parameters on the segment - return Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); - } - } Ty::Unknown } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 9a3faa9ab..09c17fdf4 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2740,17 +2740,17 @@ fn test() { [202; 203) 't': T [221; 223) '{}': () [234; 300) '{ ...(S); }': () - [244; 245) 'x': u32 - [248; 252) 'foo1': fn foo1(T) -> ::Item - [248; 255) 'foo1(S)': u32 + [244; 245) 'x': {unknown} + [248; 252) 'foo1': fn foo1(T) -> {unknown} + [248; 255) 'foo1(S)': {unknown} [253; 254) 'S': S [265; 266) 'y': u32 [269; 273) 'foo2': fn foo2(T) -> ::Item [269; 276) 'foo2(S)': u32 [274; 275) 'S': S - [286; 287) 'z': u32 - [290; 294) 'foo3': fn foo3(T) -> ::Item - [290; 297) 'foo3(S)': u32 + [286; 287) 'z': {unknown} + [290; 294) 'foo3': fn foo3(T) -> {unknown} + [290; 297) 'foo3(S)': {unknown} [295; 296) 'S': S "### ); -- cgit v1.2.3