Do autoderef for indexing

This commit is contained in:
Florian Diebold 2020-02-29 22:48:23 +01:00
parent e313efb992
commit 31171eed5e
4 changed files with 74 additions and 7 deletions

View File

@ -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<TraitId> {
self.resolve_lang_item("index")?.as_trait()
}
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])
}
}

View File

@ -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 {

View File

@ -447,6 +447,25 @@ fn iterate_inherent_methods<T>(
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(
db: &impl HirDatabase,
name: Option<&Name>,

View File

@ -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<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]
fn deref_trait() {
let t = type_at(