From f7cd40d8306dfd3f2c55ad97de5167319350a592 Mon Sep 17 00:00:00 2001 From: Unreal Hoang Date: Sun, 7 Jul 2019 16:31:09 +0900 Subject: add projection to infer for loop variable --- crates/ra_hir/src/name.rs | 14 ++++ crates/ra_hir/src/ty/infer.rs | 124 ++++++++++++++++++++++++++---------- crates/ra_hir/src/ty/infer/unify.rs | 29 ++++++++- crates/ra_hir/src/ty/tests.rs | 36 +++++++++++ crates/ra_hir/src/ty/traits.rs | 2 +- 5 files changed, 170 insertions(+), 35 deletions(-) diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 06a2dbc76..b71590560 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -86,6 +86,11 @@ impl Name { "Self" => KnownName::SelfType, "self" => KnownName::SelfParam, "macro_rules" => KnownName::MacroRules, + + "std" => KnownName::Std, + "iter" => KnownName::Iter, + "IntoIterator" => KnownName::IntoIterator, + "Item" => KnownName::Item, _ => return None, }; Some(name) @@ -157,6 +162,11 @@ pub(crate) enum KnownName { SelfParam, MacroRules, + + Std, + Iter, + IntoIterator, + Item, } impl AsName for KnownName { @@ -182,6 +192,10 @@ impl AsName for KnownName { KnownName::SelfType => "Self", KnownName::SelfParam => "self", KnownName::MacroRules => "macro_rules", + KnownName::Std => "std", + KnownName::Iter => "iter", + KnownName::IntoIterator => "IntoIterator", + KnownName::Item => "Item", }; Name::new(s.into()) } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index e79e5e223..973de70df 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -28,24 +28,28 @@ use test_utils::tested_by; use super::{ autoderef, method_resolution, op, primitive, - traits::{Guidance, Obligation, Solution}, - ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypableDef, TypeCtor, + traits::{Guidance, Obligation, ProjectionPredicate, Solution}, + ApplicationTy, CallableDef, ProjectionTy, Substs, TraitRef, Ty, TypableDef, TypeCtor, }; use crate::{ adt::VariantDef, + code_model::{ModuleDef::Trait, TypeAlias}, diagnostics::DiagnosticSink, expr::{ self, Array, BinaryOp, BindingAnnotation, Body, Expr, ExprId, FieldPat, Literal, Pat, PatId, Statement, UnaryOp, }, generics::{GenericParams, HasGenericParams}, - nameres::Namespace, - path::{GenericArg, GenericArgs}, - resolve::{Resolution, Resolver}, + nameres::{Namespace, PerNs}, + path::{GenericArg, GenericArgs, PathKind, PathSegment}, + resolve::{ + Resolution::{self, Def}, + Resolver, + }, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - AdtDef, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, ModuleDef, Name, Path, - StructField, + AdtDef, AsName, ConstData, DefWithBody, FnData, Function, HirDatabase, ImplItem, KnownName, + ModuleDef, Name, Path, StructField, }; mod unify; @@ -323,34 +327,53 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn resolve_obligations_as_possible(&mut self) { let obligations = mem::replace(&mut self.obligations, Vec::new()); for obligation in obligations { - let (solution, canonicalized) = match &obligation { + match &obligation { Obligation::Trait(tr) => { let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone()); - ( - self.db.implements( - self.resolver.krate().unwrap(), - canonicalized.value.clone(), - ), - canonicalized, - ) - } - }; - match solution { - Some(Solution::Unique(substs)) => { - canonicalized.apply_solution(self, substs.0); - } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.0); - self.obligations.push(obligation); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - self.obligations.push(obligation); + let solution = self + .db + .implements(self.resolver.krate().unwrap(), canonicalized.value.clone()); + match solution { + Some(Solution::Unique(substs)) => { + canonicalized.apply_solution(self, substs.0); + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(self, substs.0); + self.obligations.push(obligation); + } + Some(_) => { + // FIXME use this when trying to resolve everything at the end + self.obligations.push(obligation); + } + None => { + // FIXME obligation cannot be fulfilled => diagnostic + } + }; } - None => { - // FIXME obligation cannot be fulfilled => diagnostic + Obligation::Projection(pr) => { + let canonicalized = self.canonicalizer().canonicalize_projection(pr.clone()); + let solution = self + .db + .normalize(self.resolver.krate().unwrap(), canonicalized.value.clone()); + + match solution { + Some(Solution::Unique(substs)) => { + canonicalized.apply_solution(self, substs.0); + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(self, substs.0); + self.obligations.push(obligation); + } + Some(_) => { + // FIXME use this when trying to resolve everything at the end + self.obligations.push(obligation); + } + None => { + // FIXME obligation cannot be fulfilled => diagnostic + } + }; } - } + }; } } @@ -967,8 +990,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::unit() } Expr::For { iterable, body, pat } => { - let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); - self.infer_pat(*pat, &Ty::Unknown, BindingMode::default()); + let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); + + let pat_ty = match self.resolve_into_iter_item() { + Some(into_iter_item_alias) => { + let pat_ty = self.new_type_var(); + let projection = ProjectionPredicate { + ty: pat_ty.clone(), + projection_ty: ProjectionTy { + associated_ty: into_iter_item_alias, + parameters: vec![iterable_ty].into(), + }, + }; + self.obligations.push(Obligation::Projection(projection)); + self.resolve_ty_as_possible(&mut vec![], pat_ty) + } + None => Ty::Unknown, + }; + + self.infer_pat(*pat, &pat_ty, BindingMode::default()); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); Ty::unit() } @@ -1301,6 +1341,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { fn infer_body(&mut self) { self.infer_expr(self.body.body_expr(), &Expectation::has_type(self.return_ty.clone())); } + + fn resolve_into_iter_item(&self) -> Option { + let into_iter_path = Path { + kind: PathKind::Abs, + segments: vec![ + PathSegment { name: KnownName::Std.as_name(), args_and_bindings: None }, + PathSegment { name: KnownName::Iter.as_name(), args_and_bindings: None }, + PathSegment { name: KnownName::IntoIterator.as_name(), args_and_bindings: None }, + ], + }; + + match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() { + PerNs { types: Some(Def(Trait(trait_))), .. } => { + Some(trait_.associated_type_by_name(self.db, KnownName::Item.as_name())?) + } + _ => None, + } + } } /// The ID of a type variable. diff --git a/crates/ra_hir/src/ty/infer/unify.rs b/crates/ra_hir/src/ty/infer/unify.rs index 49bf5b946..a24e5eb5c 100644 --- a/crates/ra_hir/src/ty/infer/unify.rs +++ b/crates/ra_hir/src/ty/infer/unify.rs @@ -2,7 +2,7 @@ use super::InferenceContext; use crate::db::HirDatabase; -use crate::ty::{Canonical, InferTy, TraitRef, Ty}; +use crate::ty::{Canonical, InferTy, ProjectionPredicate, ProjectionTy, TraitRef, Ty}; impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub(super) fn canonicalizer<'b>(&'b mut self) -> Canonicalizer<'a, 'b, D> @@ -86,6 +86,25 @@ where } } + fn do_canonicalize_projection_ty(&mut self, projection_ty: ProjectionTy) -> ProjectionTy { + let params = projection_ty + .parameters + .iter() + .map(|ty| self.do_canonicalize_ty(ty.clone())) + .collect::>(); + ProjectionTy { associated_ty: projection_ty.associated_ty, parameters: params.into() } + } + + fn do_canonicalize_projection_predicate( + &mut self, + projection: ProjectionPredicate, + ) -> ProjectionPredicate { + let ty = self.do_canonicalize_ty(projection.ty); + let projection_ty = self.do_canonicalize_projection_ty(projection.projection_ty); + + ProjectionPredicate { ty, projection_ty } + } + pub fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized { let result = self.do_canonicalize_ty(ty); self.into_canonicalized(result) @@ -95,6 +114,14 @@ where let result = self.do_canonicalize_trait_ref(trait_ref); self.into_canonicalized(result) } + + pub fn canonicalize_projection( + mut self, + projection: ProjectionPredicate, + ) -> Canonicalized { + let result = self.do_canonicalize_projection_predicate(projection); + self.into_canonicalized(result) + } } impl Canonicalized { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index aacd94a26..fe5e89f2d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -20,6 +20,42 @@ use crate::{ // against snapshots of the expected results using insta. Use cargo-insta to // update the snapshots. +#[test] +fn infer_for_loop() { + let (mut db, pos) = MockDatabase::with_position( + r#" +//- /main.rs +struct Vec {} +impl Vec { + fn new() -> Self { Vec {} } + fn push(&mut self, t: T) { } +} + +impl ::std::iter::IntoIterator for Vec { + type Item=T; +} +fn test() { + let v = Vec::new(); + v.push("foo"); + for x in v { + x<|>; + } +} + +//- /lib.rs +mod iter { + trait IntoIterator { + type Item; + } +} +"#, + ); + db.set_crate_graph_from_fixture(crate_graph! { + "main": ("/main.rs", ["std"]), + "std": ("/lib.rs", []), + }); + assert_eq!("&str", type_at_pos(&db, pos)); +} #[test] fn infer_basics() { assert_snapshot_matches!( diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 23a26a971..3e28852b6 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -75,7 +75,7 @@ pub enum Obligation { /// Prove that a certain type implements a trait (the type is the `Self` type /// parameter to the `TraitRef`). Trait(TraitRef), - // Projection(ProjectionPredicate), + Projection(ProjectionPredicate), } impl Obligation { -- cgit v1.2.3