From e313efb9926be80a5d7614d800e425b6891cf7d9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Feb 2020 22:01:36 +0100 Subject: [PATCH 1/2] Do array unsizing for method receivers It turns out rustc actually only unsizes array method receivers, so we don't need to do any trait solving for this (at least for now). Fixes #2670. --- crates/ra_hir_ty/src/method_resolution.rs | 21 +++++++++++++++++-- .../ra_hir_ty/src/tests/method_resolution.rs | 18 ++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index df5901835ad9..7b0ff8161163 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -20,7 +20,7 @@ use crate::{ db::HirDatabase, primitive::{FloatBitness, Uncertain}, utils::all_super_traits, - Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, + ApplicationTy, Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, }; /// This is used as a key for indexing impls. @@ -214,7 +214,7 @@ pub fn iterate_method_candidates( // the methods by autoderef order of *receiver types*, not *self // types*. - let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); + let deref_chain = autoderef_method_receiver(db, krate, ty); for i in 0..deref_chain.len() { if let Some(result) = iterate_method_candidates_with_autoref( &deref_chain[i..], @@ -548,3 +548,20 @@ fn generic_implements_goal( let obligation = super::Obligation::Trait(trait_ref); Canonical { num_vars, value: InEnvironment::new(env, obligation) } } + +fn autoderef_method_receiver( + db: &impl HirDatabase, + krate: CrateId, + ty: InEnvironment>, +) -> Vec> { + let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); + // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) + if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) = + deref_chain.last().map(|ty| &ty.value) + { + let num_vars = deref_chain.last().unwrap().num_vars; + let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone()); + deref_chain.push(Canonical { value: unsized_ty, num_vars }) + } + deref_chain +} diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 644d59e17c01..f9b394f059a5 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -838,6 +838,24 @@ fn test() { (&S).foo()<|>; } assert_eq!(t, "u128"); } +#[test] +fn method_resolution_unsize_array() { + let t = type_at( + r#" +//- /main.rs +#[lang = "slice"] +impl [T] { + fn len(&self) -> usize { loop {} } +} +fn test() { + let a = [1, 2, 3]; + a.len()<|>; +} +"#, + ); + assert_eq!(t, "usize"); +} + #[test] fn method_resolution_trait_from_prelude() { let (db, pos) = TestDB::with_position( From 31171eed5eeab217280237e63ffe6adda62baf96 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 29 Feb 2020 22:48:23 +0100 Subject: [PATCH 2/2] Do autoderef for indexing --- crates/ra_hir_ty/src/infer.rs | 8 +++++-- crates/ra_hir_ty/src/infer/expr.rs | 26 +++++++++++++++++---- crates/ra_hir_ty/src/method_resolution.rs | 19 +++++++++++++++ crates/ra_hir_ty/src/tests/traits.rs | 28 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 569d46cc380e..377f44fa75a6 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -28,7 +28,7 @@ use hir_def::{ path::{path, Path}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{Mutability, TypeRef}, - AdtId, AssocItemId, DefWithBodyId, FunctionId, StructFieldId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, FunctionId, StructFieldId, TraitId, TypeAliasId, VariantId, }; use hir_expand::{diagnostics::DiagnosticSink, name::name}; use ra_arena::map::ArenaMap; @@ -540,8 +540,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Some(struct_.into()) } + fn resolve_ops_index(&self) -> Option { + self.resolve_lang_item("index")?.as_trait() + } + fn resolve_ops_index_output(&self) -> Option { - let trait_ = self.resolve_lang_item("index")?.as_trait()?; + let trait_ = self.resolve_ops_index()?; self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 3db5b2b51527..e89cc72987c9 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -429,11 +429,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let base_ty = self.infer_expr_inner(*base, &Expectation::none()); let index_ty = self.infer_expr(*index, &Expectation::none()); - self.resolve_associated_type_with_params( - base_ty, - self.resolve_ops_index_output(), - &[index_ty], - ) + if let (Some(index_trait), Some(krate)) = + (self.resolve_ops_index(), self.resolver.krate()) + { + let canonicalized = self.canonicalizer().canonicalize_ty(base_ty); + let self_ty = method_resolution::resolve_indexing_op( + self.db, + &canonicalized.value, + self.trait_env.clone(), + krate, + index_trait, + ); + let self_ty = + self_ty.map_or(Ty::Unknown, |t| canonicalized.decanonicalize_ty(t.value)); + self.resolve_associated_type_with_params( + self_ty, + self.resolve_ops_index_output(), + &[index_ty], + ) + } else { + Ty::Unknown + } } Expr::Tuple { exprs } => { let mut tys = match &expected.ty { diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 7b0ff8161163..74b908c2e032 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -447,6 +447,25 @@ fn iterate_inherent_methods( None } +/// Returns the self type for the index trait call. +pub fn resolve_indexing_op( + db: &impl HirDatabase, + ty: &Canonical, + env: Arc, + krate: CrateId, + index_trait: TraitId, +) -> Option> { + let ty = InEnvironment { value: ty.clone(), environment: env.clone() }; + let deref_chain = autoderef_method_receiver(db, krate, ty); + for ty in deref_chain { + let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); + if db.trait_solve(krate, goal).is_some() { + return Some(ty); + } + } + None +} + fn is_valid_candidate( db: &impl HirDatabase, name: Option<&Name>, diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 7d796d0b940f..547010b35199 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -567,6 +567,34 @@ mod ops { assert_eq!("Foo", type_at_pos(&db, pos)); } +#[test] +fn infer_ops_index_autoderef() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:std +fn test() { + let a = &[1u32, 2, 3]; + let b = a[1]; + b<|>; +} + +//- /std.rs crate:std +impl ops::Index for [T] { + type Output = T; +} + +#[prelude_import] use ops::*; +mod ops { + #[lang = "index"] + pub trait Index { + type Output; + } +} +"#, + ); + assert_eq!("u32", type_at_pos(&db, pos)); +} + #[test] fn deref_trait() { let t = type_at(