mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge #3380
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:
commit
ad4e108c39
5 changed files with 111 additions and 9 deletions
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<T>(
|
|||
// 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..],
|
||||
|
@ -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>,
|
||||
|
@ -548,3 +567,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<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
|
||||
}
|
||||
|
|
|
@ -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> [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(
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue