3380: Unsizing in method resolution & autoderef for indexing r=matklad a=flodiebold

 - do autoderef for indexing
 - do array unsizing (`[T; N]` -> `[T]`) in both method resolution and indexing. It turns out array unsizing is actually the only unsizing coercion that rustc does for method receivers, so this is simpler than I expected.

Sadly, this doesn't fix indexing slices/arrays yet, there are still some trait solving problems...

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2020-02-29 22:01:36 +00:00 committed by GitHub
commit ad4e108c39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 9 deletions

View file

@ -28,7 +28,7 @@ use hir_def::{
path::{path, Path}, path::{path, Path},
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, Resolver, TypeNs},
type_ref::{Mutability, TypeRef}, 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 hir_expand::{diagnostics::DiagnosticSink, name::name};
use ra_arena::map::ArenaMap; use ra_arena::map::ArenaMap;
@ -540,8 +540,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
Some(struct_.into()) Some(struct_.into())
} }
fn resolve_ops_index(&self) -> Option<TraitId> {
self.resolve_lang_item("index")?.as_trait()
}
fn resolve_ops_index_output(&self) -> Option<TypeAliasId> { fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
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]) self.db.trait_data(trait_).associated_type_by_name(&name![Output])
} }
} }

View file

@ -429,11 +429,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let base_ty = self.infer_expr_inner(*base, &Expectation::none()); let base_ty = self.infer_expr_inner(*base, &Expectation::none());
let index_ty = self.infer_expr(*index, &Expectation::none()); let index_ty = self.infer_expr(*index, &Expectation::none());
self.resolve_associated_type_with_params( if let (Some(index_trait), Some(krate)) =
base_ty, (self.resolve_ops_index(), self.resolver.krate())
self.resolve_ops_index_output(), {
&[index_ty], 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 } => { Expr::Tuple { exprs } => {
let mut tys = match &expected.ty { let mut tys = match &expected.ty {

View file

@ -20,7 +20,7 @@ use crate::{
db::HirDatabase, db::HirDatabase,
primitive::{FloatBitness, Uncertain}, primitive::{FloatBitness, Uncertain},
utils::all_super_traits, 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. /// This is used as a key for indexing impls.
@ -214,7 +214,7 @@ pub fn iterate_method_candidates<T>(
// the methods by autoderef order of *receiver types*, not *self // the methods by autoderef order of *receiver types*, not *self
// types*. // 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() { for i in 0..deref_chain.len() {
if let Some(result) = iterate_method_candidates_with_autoref( if let Some(result) = iterate_method_candidates_with_autoref(
&deref_chain[i..], &deref_chain[i..],
@ -447,6 +447,25 @@ fn iterate_inherent_methods<T>(
None None
} }
/// Returns the self type for the index trait call.
pub fn resolve_indexing_op(
db: &impl HirDatabase,
ty: &Canonical<Ty>,
env: Arc<TraitEnvironment>,
krate: CrateId,
index_trait: TraitId,
) -> Option<Canonical<Ty>> {
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( fn is_valid_candidate(
db: &impl HirDatabase, db: &impl HirDatabase,
name: Option<&Name>, name: Option<&Name>,
@ -548,3 +567,20 @@ fn generic_implements_goal(
let obligation = super::Obligation::Trait(trait_ref); let obligation = super::Obligation::Trait(trait_ref);
Canonical { num_vars, value: InEnvironment::new(env, obligation) } Canonical { num_vars, value: InEnvironment::new(env, obligation) }
} }
fn autoderef_method_receiver(
db: &impl HirDatabase,
krate: CrateId,
ty: InEnvironment<Canonical<Ty>>,
) -> Vec<Canonical<Ty>> {
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
}

View file

@ -838,6 +838,24 @@ fn test() { (&S).foo()<|>; }
assert_eq!(t, "u128"); assert_eq!(t, "u128");
} }
#[test]
fn method_resolution_unsize_array() {
let t = type_at(
r#"
//- /main.rs
#[lang = "slice"]
impl<T> [T] {
fn len(&self) -> usize { loop {} }
}
fn test() {
let a = [1, 2, 3];
a.len()<|>;
}
"#,
);
assert_eq!(t, "usize");
}
#[test] #[test]
fn method_resolution_trait_from_prelude() { fn method_resolution_trait_from_prelude() {
let (db, pos) = TestDB::with_position( let (db, pos) = TestDB::with_position(

View file

@ -567,6 +567,34 @@ mod ops {
assert_eq!("Foo", type_at_pos(&db, pos)); 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<T> ops::Index<u32> for [T] {
type Output = T;
}
#[prelude_import] use ops::*;
mod ops {
#[lang = "index"]
pub trait Index<Idx> {
type Output;
}
}
"#,
);
assert_eq!("u32", type_at_pos(&db, pos));
}
#[test] #[test]
fn deref_trait() { fn deref_trait() {
let t = type_at( let t = type_at(