From 9c5e7dd849eff7bd6f20aa353feef083d089ff58 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 12 May 2019 18:33:47 +0200 Subject: Implement autoderef using the Deref trait - add support for other lang item targets, since we need the Deref lang item --- crates/ra_hir/src/ty/autoderef.rs | 68 +++++++++++++++++++++++++++---- crates/ra_hir/src/ty/infer.rs | 40 +++++++++--------- crates/ra_hir/src/ty/method_resolution.rs | 5 +-- crates/ra_hir/src/ty/traits.rs | 4 +- 4 files changed, 84 insertions(+), 33 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index a442a856c..bee756d80 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -5,17 +5,67 @@ use std::iter::successors; -use crate::HirDatabase; -use super::Ty; +use log::info; -impl Ty { - /// Iterates over the possible derefs of `ty`. - pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator + 'a { - successors(Some(self), move |ty| ty.autoderef_step(db)) +use crate::{HirDatabase, Name, Resolver}; +use super::{traits::Solution, Ty, Canonical}; + +pub(crate) fn autoderef<'a>( + db: &'a impl HirDatabase, + resolver: &'a Resolver, + ty: Canonical, +) -> impl Iterator> + 'a { + successors(Some(ty), move |ty| deref(db, resolver, ty)) +} + +pub(crate) fn deref( + db: &impl HirDatabase, + resolver: &Resolver, + ty: &Canonical, +) -> Option> { + if let Some(derefed) = ty.value.builtin_deref() { + Some(Canonical { value: derefed, num_vars: ty.num_vars }) + } else { + deref_by_trait(db, resolver, ty) } +} + +fn deref_by_trait( + db: &impl HirDatabase, + resolver: &Resolver, + ty: &Canonical, +) -> Option> { + let krate = resolver.krate()?; + let deref_trait = match db.lang_item(krate, "deref".into())? { + crate::lang_item::LangItemTarget::Trait(t) => t, + _ => return None, + }; + let target = deref_trait.associated_type_by_name(db, Name::target())?; + + // FIXME we should check that Deref has no type parameters, because we assume it below + + // FIXME make the Canonical handling nicer + // TODO shift inference variables in ty + + let projection = super::traits::ProjectionPredicate { + ty: Ty::Bound(0), + projection_ty: super::ProjectionTy { + associated_ty: target, + parameters: vec![ty.value.clone()].into(), + }, + }; + + let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection }; + + let solution = db.normalize(krate, canonical)?; - fn autoderef_step(&self, _db: &impl HirDatabase) -> Option { - // FIXME Deref::deref - self.builtin_deref() + match &solution { + Solution::Unique(vars) => { + Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars }) + } + Solution::Ambig(_) => { + info!("Ambiguous solution for deref: {:?}", solution); + None + } } } diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index e150d7fd8..fdb444de2 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -46,7 +46,7 @@ use crate::{ use super::{ Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef, traits::{Solution, Obligation, Guidance}, - method_resolution, + method_resolution, autoderef, }; mod unify; @@ -1074,25 +1074,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } Expr::Field { expr, name } => { let receiver_ty = self.infer_expr(*expr, &Expectation::none()); - let ty = receiver_ty - .autoderef(self.db) - .find_map(|derefed_ty| match derefed_ty { - Ty::Apply(a_ty) => match a_ty.ctor { - TypeCtor::Tuple { .. } => { - let i = name.to_string().parse::().ok(); - i.and_then(|i| a_ty.parameters.0.get(i).cloned()) - } - TypeCtor::Adt(AdtDef::Struct(s)) => { - s.field(self.db, name).map(|field| { - self.write_field_resolution(tgt_expr, field); - field.ty(self.db).subst(&a_ty.parameters) - }) - } - _ => None, - }, + let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); + let ty = autoderef::autoderef( + self.db, + &self.resolver.clone(), + canonicalized.value.clone(), + ) + .find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) { + Ty::Apply(a_ty) => match a_ty.ctor { + TypeCtor::Tuple { .. } => { + let i = name.to_string().parse::().ok(); + i.and_then(|i| a_ty.parameters.0.get(i).cloned()) + } + TypeCtor::Adt(AdtDef::Struct(s)) => s.field(self.db, name).map(|field| { + self.write_field_resolution(tgt_expr, field); + field.ty(self.db).subst(&a_ty.parameters) + }), _ => None, - }) - .unwrap_or(Ty::Unknown); + }, + _ => None, + }) + .unwrap_or(Ty::Unknown); self.insert_type_vars(ty) } Expr::Try { expr } => { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 646e58aa9..ad26d591c 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -16,7 +16,7 @@ use crate::{ generics::HasGenericParams, ty::primitive::{UncertainIntTy, UncertainFloatTy} }; -use super::{TraitRef, Canonical}; +use super::{TraitRef, Canonical, autoderef}; /// This is used as a key for indexing impls. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -162,8 +162,7 @@ pub(crate) fn iterate_method_candidates( // rustc does an autoderef and then autoref again). let krate = resolver.krate()?; - for derefed_ty in ty.value.clone().autoderef(db) { - let derefed_ty = Canonical { value: derefed_ty, num_vars: ty.num_vars }; + for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) { return Some(result); diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index f3e488403..6cf3dd70a 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -80,8 +80,8 @@ pub enum Obligation { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ProjectionPredicate { - projection_ty: ProjectionTy, - ty: Ty, + pub projection_ty: ProjectionTy, + pub ty: Ty, } /// Check using Chalk whether trait is implemented for given parameters including `Self` type. -- cgit v1.2.3