From 3a9a0bc968d9bb97c80f18b4323b3ad75cc8bbad Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 14 Jul 2019 14:19:00 +0200 Subject: Add another test for assoc type resolution --- crates/ra_hir/src/ty/tests.rs | 52 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d5f7a4d25..9d412ff61 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2508,15 +2508,55 @@ struct S; impl Iterable for S { type Item = u32; } fn test() { let x: ::Item = 1; - let y: T::Item = no_matter; + let y: ::Item = no_matter; + let z: T::Item = no_matter; } "#), @r###" -[108; 181) '{ ...ter; }': () -[118; 119) 'x': i32 -[145; 146) '1': i32 -[156; 157) 'y': {unknown} -[169; 178) 'no_matter': {unknown}"### + ⋮ + ⋮[108; 227) '{ ...ter; }': () + ⋮[118; 119) 'x': i32 + ⋮[145; 146) '1': i32 + ⋮[156; 157) 'y': {unknown} + ⋮[183; 192) 'no_matter': {unknown} + ⋮[202; 203) 'z': {unknown} + ⋮[215; 224) 'no_matter': {unknown} + "### + ); +} + +#[test] +fn infer_return_associated_type() { + assert_snapshot_matches!( + infer(r#" +trait Iterable { + type Item; +} +struct S; +impl Iterable for S { type Item = u32; } +fn foo1(t: T) -> T::Item {} +fn foo2(t: T) -> ::Item {} +fn test() { + let x = foo1(S); + let y = foo2(S); +} +"#), + @r###" + ⋮ + ⋮[106; 107) 't': T + ⋮[123; 125) '{}': () + ⋮[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} + ⋮[210; 211) 'S': S + ⋮[222; 223) 'y': {unknown} + ⋮[226; 230) 'foo2': fn foo2(T) -> {unknown} + ⋮[226; 233) 'foo2(S)': {unknown} + ⋮[231; 232) 'S': S + "### ); } -- cgit v1.2.3 From 6cfdfdecbaed38534397f16e1ea1cda38b0b9395 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 5 Aug 2019 21:13:34 +0200 Subject: Add representations of associated types This adds three different representations, copied from the Chalk model: - `Ty::Projection` is an associated type projection written somewhere in the code, like `::Bar`. - `Ty::UnselectedProjection` is similar, but we don't know the trait yet (`Foo::Bar`). - The above representations are normalized to their actual types during type inference. When that isn't possible, for example for `T::Item` inside an `fn foo`, the type is normalized to an application type with `TypeCtor::AssociatedType`. --- crates/ra_hir/src/code_model.rs | 4 ++ crates/ra_hir/src/ty.rs | 86 ++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 23 ++++++++++ 3 files changed, 113 insertions(+) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 779764590..89fc1d1a1 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -838,6 +838,10 @@ impl TypeAlias { self.id.module(db) } + pub fn krate(self, db: &impl DefDatabase) -> Option { + self.module(db).krate(db) + } + /// The containing impl block, if this is a method. pub fn impl_block(self, db: &impl DefDatabase) -> Option { let module_impls = db.impls_in_module(self.module(db)); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 82589e504..f9cf3ec72 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -94,6 +94,12 @@ pub enum TypeCtor { /// A tuple type. For example, `(i32, bool)`. Tuple { cardinality: u16 }, + + /// Represents an associated item like `Iterator::Item`. This is used + /// when we have tried to normalize a projection like `T::Item` but + /// couldn't find a better representation. In that case, we generate + /// an **application type** like `(Iterator::Item)`. + AssociatedType(TypeAlias), } /// A nominal type with (maybe 0) type parameters. This might be a primitive @@ -114,6 +120,12 @@ pub struct ProjectionTy { pub parameters: Substs, } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct UnselectedProjectionTy { + pub type_name: Name, + pub parameters: Substs, +} + /// A type. /// /// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents @@ -127,6 +139,18 @@ pub enum Ty { /// several other things. Apply(ApplicationTy), + /// A "projection" type corresponds to an (unnormalized) + /// projection like `>::Foo`. Note that the + /// trait and all its parameters are fully known. + Projection(ProjectionTy), + + /// This is a variant of a projection in which the trait is + /// **not** known. It corresponds to a case where people write + /// `T::Item` without specifying the trait. We would then try to + /// figure out the trait by looking at all the traits that are in + /// scope. + UnselectedProjection(UnselectedProjectionTy), + /// A type parameter; for example, `T` in `fn f(x: T) {} Param { /// The index of the parameter (starting with parameters from the @@ -352,6 +376,16 @@ impl Ty { t.walk(f); } } + Ty::Projection(p_ty) => { + for t in p_ty.parameters.iter() { + t.walk(f); + } + } + Ty::UnselectedProjection(p_ty) => { + for t in p_ty.parameters.iter() { + t.walk(f); + } + } Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -362,6 +396,12 @@ impl Ty { Ty::Apply(a_ty) => { a_ty.parameters.walk_mut(f); } + Ty::Projection(p_ty) => { + p_ty.parameters.walk_mut(f); + } + Ty::UnselectedProjection(p_ty) => { + p_ty.parameters.walk_mut(f); + } Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} } f(self); @@ -572,7 +612,51 @@ impl HirDisplay for ApplicationTy { write!(f, ">")?; } } + TypeCtor::AssociatedType(type_alias) => { + let trait_name = type_alias + .parent_trait(f.db) + .and_then(|t| t.name(f.db)) + .unwrap_or_else(Name::missing); + let name = type_alias.name(f.db); + write!(f, "{}::{}", trait_name, name)?; + if self.parameters.len() > 0 { + write!(f, "<")?; + f.write_joined(&*self.parameters.0, ", ")?; + write!(f, ">")?; + } + } + } + Ok(()) + } +} + +impl HirDisplay for ProjectionTy { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + let trait_name = self + .associated_ty + .parent_trait(f.db) + .and_then(|t| t.name(f.db)) + .unwrap_or_else(Name::missing); + write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; + if self.parameters.len() > 1 { + write!(f, "<")?; + f.write_joined(&self.parameters[1..], ", ")?; + write!(f, ">")?; + } + write!(f, ">::{}", self.associated_ty.name(f.db))?; + Ok(()) + } +} + +impl HirDisplay for UnselectedProjectionTy { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + write!(f, "{}", self.parameters[0].display(f.db))?; + if self.parameters.len() > 1 { + write!(f, "<")?; + f.write_joined(&self.parameters[1..], ", ")?; + write!(f, ">")?; } + write!(f, "::{}", self.type_name)?; Ok(()) } } @@ -581,6 +665,8 @@ impl HirDisplay for Ty { fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { match self { Ty::Apply(a_ty) => a_ty.hir_fmt(f)?, + Ty::Projection(p_ty) => p_ty.hir_fmt(f)?, + Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?, Ty::Param { name, .. } => write!(f, "{}", name)?, Ty::Bound(idx) => write!(f, "?{}", idx)?, Ty::Unknown => write!(f, "{{unknown}}")?, diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 9e7ae0724..21055dcfd 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -50,6 +50,19 @@ impl ToChalk for Ty { let parameters = apply_ty.parameters.to_chalk(db); chalk_ir::ApplicationTy { name, parameters }.cast() } + Ty::Projection(proj_ty) => { + let associated_ty_id = proj_ty.associated_ty.to_chalk(db); + let parameters = proj_ty.parameters.to_chalk(db); + chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast() + } + Ty::UnselectedProjection(proj_ty) => { + let type_name = lalrpop_intern::intern(&proj_ty.type_name.to_string()); + let parameters = proj_ty.parameters.to_chalk(db); + chalk_ir::Ty::UnselectedProjection(chalk_ir::UnselectedProjectionTy { + type_name, + parameters, + }) + } Ty::Param { idx, .. } => { PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty() } @@ -529,6 +542,16 @@ pub(crate) fn struct_datum_query( adt.krate(db) != Some(krate), ) } + TypeCtor::AssociatedType(type_alias) => { + let generic_params = type_alias.generic_params(db); + let bound_vars = Substs::bound_vars(&generic_params); + let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); + ( + generic_params.count_params_including_parent(), + where_clauses, + type_alias.krate(db) != Some(krate), + ) + } }; let flags = chalk_rust_ir::StructFlags { upstream, -- cgit v1.2.3 From 22724f37f3ae73983bf700d10d80a8dbd4fa4073 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 5 Aug 2019 22:42:38 +0200 Subject: Lower fully qualified associated type paths I.e. `::Foo`. --- crates/ra_hir/src/path.rs | 38 ++++++++++++++++++- crates/ra_hir/src/ty/lower.rs | 88 +++++++++++++++++++++++++++++++++++-------- crates/ra_hir/src/ty/tests.rs | 14 +++---- 3 files changed, 117 insertions(+), 23 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 882db7681..5ee71e421 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -25,6 +25,12 @@ pub struct PathSegment { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericArgs { pub args: Vec, + /// This specifies whether the args contain a Self type as the first + /// element. This is the case for path segments like ``, where + /// `T` is actually a type parameter for the path `Trait` specifying the + /// Self type. Otherwise, when we have a path `Trait`, the Self type + /// is left out. + pub has_self_type: bool, // someday also bindings } @@ -74,6 +80,28 @@ impl Path { let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; segments.push(segment); } + ast::PathSegmentKind::Type { type_ref, trait_ref } => { + assert!(path.qualifier().is_none()); // this can only occur at the first segment + + // FIXME: handle syntax (type segments without trait) + + // >::Foo desugars to Trait::Foo + let path = Path::from_ast(trait_ref?.path()?)?; + kind = path.kind; + let mut prefix_segments = path.segments; + prefix_segments.reverse(); + segments.extend(prefix_segments); + // Insert the type reference (T in the above example) as Self parameter for the trait + let self_type = TypeRef::from_ast(type_ref?); + let mut last_segment = segments.last_mut()?; + if last_segment.args_and_bindings.is_none() { + last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty())); + }; + let args = last_segment.args_and_bindings.as_mut().unwrap(); + let mut args_inner = Arc::make_mut(args); + args_inner.has_self_type = true; + args_inner.args.insert(0, GenericArg::Type(self_type)); + } ast::PathSegmentKind::CrateKw => { kind = PathKind::Crate; break; @@ -144,11 +172,15 @@ impl GenericArgs { } // lifetimes and assoc type args ignored for now if !args.is_empty() { - Some(GenericArgs { args }) + Some(GenericArgs { args, has_self_type: false }) } else { None } } + + pub(crate) fn empty() -> GenericArgs { + GenericArgs { args: Vec::new(), has_self_type: false } + } } impl From for Path { @@ -236,6 +268,10 @@ fn convert_path(prefix: Option, path: ast::Path) -> Option { } Path { kind: PathKind::Super, segments: Vec::new() } } + ast::PathSegmentKind::Type { .. } => { + // not allowed in imports + return None; + } }; Some(res) } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 894ba0695..24ec77fcf 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -8,7 +8,7 @@ use std::iter; use std::sync::Arc; -use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor}; +use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor}; use crate::{ adt::VariantDef, generics::HasGenericParams, @@ -64,7 +64,8 @@ impl Ty { pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { // Resolve the path (in type namespace) - let resolution = resolver.resolve_path_without_assoc_items(db, path).take_types(); + let (resolution, remaining_index) = resolver.resolve_path_segments(db, path).into_inner(); + let resolution = resolution.take_types(); let def = match resolution { Some(Resolution::Def(def)) => def, @@ -73,6 +74,10 @@ impl Ty { panic!("path resolved to local binding in type ns"); } Some(Resolution::GenericParam(idx)) => { + if remaining_index.is_some() { + // e.g. T::Item + return Ty::Unknown; + } return Ty::Param { idx, // FIXME: maybe return name in resolution? @@ -83,18 +88,59 @@ impl Ty { }; } Some(Resolution::SelfType(impl_block)) => { + if remaining_index.is_some() { + // e.g. Self::Item + return Ty::Unknown; + } return impl_block.target_ty(db); } - None => return Ty::Unknown, + None => { + // path did not resolve + return Ty::Unknown; + } }; - let typable: TypableDef = match def.into() { - None => return Ty::Unknown, - Some(it) => it, - }; - let ty = db.type_for_def(typable, Namespace::Types); - let substs = Ty::substs_from_path(db, resolver, path, typable); - ty.subst(&substs) + if let ModuleDef::Trait(trait_) = def { + 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); + if let Some(remaining_index) = remaining_index { + if remaining_index == path.segments.len() - 1 { + let segment = &path.segments[remaining_index]; + let associated_ty = + match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) { + Some(t) => t, + None => { + // associated type not found + return Ty::Unknown; + } + }; + eprintln!( + "assoc ty: {:?}, parameters: {:?}", + associated_ty.name(db), + trait_ref.substs + ); + // FIXME handle type parameters on the segment + Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) + } else { + // FIXME more than one segment remaining, is this possible? + Ty::Unknown + } + } else { + // FIXME dyn Trait without the dyn + Ty::Unknown + } + } else { + let typable: TypableDef = match def.into() { + None => return Ty::Unknown, + Some(it) => it, + }; + let ty = db.type_for_def(typable, Namespace::Types); + let substs = Ty::substs_from_path(db, resolver, path, typable); + ty.subst(&substs) + } } pub(super) fn substs_from_path_segment( @@ -219,14 +265,25 @@ impl TraitRef { Resolution::Def(ModuleDef::Trait(tr)) => tr, _ => return None, }; - let mut substs = Self::substs_from_path(db, resolver, path, resolved); + let segment = path.segments.last().expect("path should have at least one segment"); + Some(TraitRef::from_resolved_path(db, resolver, resolved, segment, explicit_self_ty)) + } + + fn from_resolved_path( + db: &impl HirDatabase, + resolver: &Resolver, + resolved: Trait, + segment: &PathSegment, + explicit_self_ty: Option, + ) -> Self { + let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved); if let Some(self_ty) = explicit_self_ty { // FIXME this could be nicer let mut substs_vec = substs.0.to_vec(); substs_vec[0] = self_ty; substs.0 = substs_vec.into(); } - Some(TraitRef { trait_: resolved, substs }) + TraitRef { trait_: resolved, substs } } pub(crate) fn from_hir( @@ -245,11 +302,12 @@ impl TraitRef { fn substs_from_path( db: &impl HirDatabase, resolver: &Resolver, - path: &Path, + segment: &PathSegment, resolved: Trait, ) -> Substs { - let segment = path.segments.last().expect("path should have at least one segment"); - substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true) + let has_self_param = + segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false); + substs_from_path_segment(db, resolver, segment, Some(resolved.into()), !has_self_param) } pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 9d412ff61..01b358335 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2515,10 +2515,10 @@ fn test() { @r###" ⋮ ⋮[108; 227) '{ ...ter; }': () - ⋮[118; 119) 'x': i32 - ⋮[145; 146) '1': i32 - ⋮[156; 157) 'y': {unknown} - ⋮[183; 192) 'no_matter': {unknown} + ⋮[118; 119) 'x': ::Item + ⋮[145; 146) '1': ::Item + ⋮[156; 157) 'y': ::Item + ⋮[183; 192) 'no_matter': ::Item ⋮[202; 203) 'z': {unknown} ⋮[215; 224) 'no_matter': {unknown} "### @@ -2552,9 +2552,9 @@ fn test() { ⋮[205; 209) 'foo1': fn foo1(T) -> {unknown} ⋮[205; 212) 'foo1(S)': {unknown} ⋮[210; 211) 'S': S - ⋮[222; 223) 'y': {unknown} - ⋮[226; 230) 'foo2': fn foo2(T) -> {unknown} - ⋮[226; 233) 'foo2(S)': {unknown} + ⋮[222; 223) 'y': ::Item + ⋮[226; 230) 'foo2': fn foo2(T) -> ::Item + ⋮[226; 233) 'foo2(S)': ::Item ⋮[231; 232) 'S': S "### ); -- cgit v1.2.3 From 6265497523469990ce39e6817423c35a17055a54 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 7 Aug 2019 22:06:09 +0200 Subject: Normalize associated types during inference --- crates/ra_hir/src/ty/infer.rs | 22 +++++++++++++++++++++- crates/ra_hir/src/ty/lower.rs | 5 ----- crates/ra_hir/src/ty/traits/chalk.rs | 7 ++++--- 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 594c5bc79..74fc77cfb 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -245,7 +245,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { &self.resolver, type_ref, ); - self.insert_type_vars(ty) + let ty = self.insert_type_vars(ty); + self.normalize_associated_types_in(ty) } fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool { @@ -411,6 +412,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + ty.fold(&mut |ty| match ty { + Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty), + Ty::UnselectedProjection(proj_ty) => { + // FIXME + Ty::UnselectedProjection(proj_ty) + } + _ => ty, + }) + } + + fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { + let var = self.new_type_var(); + let predicate = ProjectionPredicate { projection_ty: proj_ty.clone(), ty: var.clone() }; + let obligation = Obligation::Projection(predicate); + self.obligations.push(obligation); + var + } + /// Resolves the type completely; type variables without known type are /// replaced by Ty::Unknown. fn resolve_ty_completely(&mut self, tv_stack: &mut Vec, ty: Ty) -> Ty { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 24ec77fcf..debedcbb8 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -117,11 +117,6 @@ impl Ty { return Ty::Unknown; } }; - eprintln!( - "assoc ty: {:?}, parameters: {:?}", - associated_ty.name(db), - trait_ref.substs - ); // FIXME handle type parameters on the segment Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) } else { diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 21055dcfd..e669f835b 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -402,11 +402,12 @@ where &self, projection: &'p chalk_ir::ProjectionTy, ) -> (Arc, &'p [Parameter], &'p [Parameter]) { - debug!("split_projection {:?}", projection); - unimplemented!() + let proj_ty: ProjectionTy = from_chalk(self.db, projection.clone()); + debug!("split_projection {:?} = {}", projection, proj_ty.display(self.db)); + // we don't support GATs, so I think this should always be correct currently + (self.db.associated_ty_data(projection.associated_ty_id), &projection.parameters, &[]) } fn custom_clauses(&self) -> Vec { - debug!("custom_clauses"); vec![] } fn all_structs(&self) -> Vec { -- cgit v1.2.3 From 11b9845afd1daa845ac9d541fd22f1fdf53436c8 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 10 Aug 2019 12:13:39 +0200 Subject: Improve debug logging a bit --- crates/ra_hir/src/ty.rs | 14 ++++++++++++++ crates/ra_hir/src/ty/traits.rs | 8 ++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f9cf3ec72..642dd02cb 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -692,3 +692,17 @@ impl HirDisplay for TraitRef { Ok(()) } } + +impl HirDisplay for Obligation { + fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + match self { + Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), + Obligation::Projection(proj) => write!( + f, + "Normalize({} => {})", + proj.projection_ty.display(f.db), + proj.ty.display(f.db) + ), + } + } +} diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 0769e6e17..fde5d8a47 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -7,7 +7,7 @@ use parking_lot::Mutex; use ra_prof::profile; use rustc_hash::FxHashSet; -use super::{Canonical, GenericPredicate, ProjectionTy, TraitRef, Ty}; +use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty}; use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; use self::chalk::{from_chalk, ToChalk}; @@ -61,7 +61,6 @@ fn solve( ) -> Option { let context = ChalkContext { db, krate }; let solver = db.trait_solver(krate); - debug!("solve goal: {:?}", goal); let solution = solver.lock().solve(&context, goal); debug!("solve({:?}) => {:?}", goal, solution); solution @@ -120,10 +119,11 @@ pub struct ProjectionPredicate { pub(crate) fn trait_solve_query( db: &impl HirDatabase, krate: Crate, - trait_ref: Canonical>, + goal: Canonical>, ) -> Option { let _p = profile("trait_solve_query"); - let canonical = trait_ref.to_chalk(db).cast(); + debug!("trait_solve_query({})", goal.value.value.display(db)); + let canonical = goal.to_chalk(db).cast(); // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; -- cgit v1.2.3 From 9d72b14cfe3606e9c55e488d74568471f981bb3d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 11 Aug 2019 13:52:34 +0200 Subject: Normalize assoc types in more places --- crates/ra_hir/src/ty/infer.rs | 27 +++++++++++++++++++++------ crates/ra_hir/src/ty/tests.rs | 12 ++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 74fc77cfb..675df4a22 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -412,11 +412,18 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + /// Recurses through the given type, normalizing associated types mentioned + /// in it by replacing them by type variables and registering obligations to + /// resolve later. This should be done once for every type we get from some + /// 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. fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + let ty = self.resolve_ty_as_possible(&mut vec![], ty); ty.fold(&mut |ty| match ty { Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty), Ty::UnselectedProjection(proj_ty) => { - // FIXME + // FIXME use Chalk's unselected projection support Ty::UnselectedProjection(proj_ty) } _ => ty, @@ -569,6 +576,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); let ty = ty.subst(&substs); let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); Some(ty) } Resolution::LocalBinding(pat) => { @@ -690,6 +698,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); + let expected_ty = self.normalize_associated_types_in(expected_ty); self.infer_pat(subpat, &expected_ty, default_bm); } @@ -717,6 +726,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); let expected_ty = matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); + let expected_ty = self.normalize_associated_types_in(expected_ty); self.infer_pat(subpat.pat, &expected_ty, default_bm); } @@ -947,9 +957,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.unify(&expected_receiver_ty, &actual_receiver_ty); let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); - for (arg, param) in args.iter().zip(param_iter) { - self.infer_expr(*arg, &Expectation::has_type(param)); + for (arg, param_ty) in args.iter().zip(param_iter) { + let param_ty = self.normalize_associated_types_in(param_ty); + self.infer_expr(*arg, &Expectation::has_type(param_ty)); } + let ret_ty = self.normalize_associated_types_in(ret_ty); ret_ty } @@ -1040,9 +1052,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }; self.register_obligations_for_call(&callee_ty); let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); - for (arg, param) in args.iter().zip(param_iter) { - self.infer_expr(*arg, &Expectation::has_type(param)); + for (arg, param_ty) in args.iter().zip(param_iter) { + let param_ty = self.normalize_associated_types_in(param_ty); + self.infer_expr(*arg, &Expectation::has_type(param_ty)); } + let ret_ty = self.normalize_associated_types_in(ret_ty); ret_ty } Expr::MethodCall { receiver, args, method_name, generic_args } => self @@ -1140,7 +1154,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { _ => None, }) .unwrap_or(Ty::Unknown); - self.insert_type_vars(ty) + let ty = self.insert_type_vars(ty); + self.normalize_associated_types_in(ty) } Expr::Await { expr } => { let inner_ty = self.infer_expr(*expr, &Expectation::none()); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 01b358335..e6a09cc67 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2515,10 +2515,10 @@ fn test() { @r###" ⋮ ⋮[108; 227) '{ ...ter; }': () - ⋮[118; 119) 'x': ::Item - ⋮[145; 146) '1': ::Item - ⋮[156; 157) 'y': ::Item - ⋮[183; 192) 'no_matter': ::Item + ⋮[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} "### @@ -2552,9 +2552,9 @@ fn test() { ⋮[205; 209) 'foo1': fn foo1(T) -> {unknown} ⋮[205; 212) 'foo1(S)': {unknown} ⋮[210; 211) 'S': S - ⋮[222; 223) 'y': ::Item + ⋮[222; 223) 'y': u32 ⋮[226; 230) 'foo2': fn foo2(T) -> ::Item - ⋮[226; 233) 'foo2(S)': ::Item + ⋮[226; 233) 'foo2(S)': u32 ⋮[231; 232) 'S': S "### ); -- cgit v1.2.3 From 5af9691dc9132db61b50c4e90cdeda6fea0c5dd9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 11 Aug 2019 15:54:31 +0200 Subject: Handle placeholder assoc types when Chalk produces them --- crates/ra_hir/src/ty/tests.rs | 49 ++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/ty/traits/chalk.rs | 21 +++++++++++++--- 2 files changed, 67 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e6a09cc67..28727bb18 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3181,6 +3181,55 @@ fn test(t: T) { (*t)<|>; } assert_eq!(t, "i128"); } +#[test] +fn associated_type_placeholder() { + let t = type_at( + r#" +//- /main.rs +pub trait ApplyL { + type Out; +} + +pub struct RefMutL; + +impl ApplyL for RefMutL { + type Out = ::Out; +} + +fn test() { + let y: as ApplyL>::Out = no_matter; + y<|>; +} +"#, + ); + // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types]. + // FIXME: fix type parameter names going missing when going through Chalk + assert_eq!(t, "ApplyL::Out<[missing name]>"); +} + +#[test] +fn associated_type_placeholder_2() { + let t = type_at( + r#" +//- /main.rs +pub trait ApplyL { + type Out; +} +fn foo(t: T) -> ::Out; + +fn test(t: T) { + let y = foo(t); + y<|>; +} +"#, + ); + // FIXME here Chalk doesn't normalize the type to a placeholder. I think we + // need to add a rule like Normalize(::Out -> ApplyL::Out) + // to the trait env ourselves here; probably Chalk can't do this by itself. + // assert_eq!(t, "ApplyL::Out<[missing name]>"); + 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(); diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index e669f835b..6df7094c5 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -45,8 +45,17 @@ impl ToChalk for Ty { fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty { match self { Ty::Apply(apply_ty) => { - let struct_id = apply_ty.ctor.to_chalk(db); - let name = TypeName::TypeKindId(struct_id.into()); + let name = match apply_ty.ctor { + TypeCtor::AssociatedType(type_alias) => { + let type_id = type_alias.to_chalk(db); + TypeName::AssociatedType(type_id) + } + _ => { + // other TypeCtors get interned and turned into a chalk StructId + let struct_id = apply_ty.ctor.to_chalk(db); + TypeName::TypeKindId(struct_id.into()) + } + }; let parameters = apply_ty.parameters.to_chalk(db); chalk_ir::ApplicationTy { name, parameters }.cast() } @@ -79,15 +88,21 @@ impl ToChalk for Ty { fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self { match chalk { chalk_ir::Ty::Apply(apply_ty) => { + // FIXME this is kind of hacky due to the fact that + // TypeName::Placeholder is a Ty::Param on our side match apply_ty.name { TypeName::TypeKindId(TypeKindId::StructId(struct_id)) => { let ctor = from_chalk(db, struct_id); let parameters = from_chalk(db, apply_ty.parameters); Ty::Apply(ApplicationTy { ctor, parameters }) } + TypeName::AssociatedType(type_id) => { + let ctor = TypeCtor::AssociatedType(from_chalk(db, type_id)); + let parameters = from_chalk(db, apply_ty.parameters); + Ty::Apply(ApplicationTy { ctor, parameters }) + } // FIXME handle TypeKindId::Trait/Type here TypeName::TypeKindId(_) => unimplemented!(), - TypeName::AssociatedType(_) => unimplemented!(), TypeName::Placeholder(idx) => { assert_eq!(idx.ui, UniverseIndex::ROOT); Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() } -- cgit v1.2.3