Skip to content

Commit ad4e108

Browse files
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 <[email protected]>
2 parents 5e78036 + 31171ee commit ad4e108

File tree

5 files changed

+111
-9
lines changed

5 files changed

+111
-9
lines changed

crates/ra_hir_ty/src/infer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use hir_def::{
2828
path::{path, Path},
2929
resolver::{HasResolver, Resolver, TypeNs},
3030
type_ref::{Mutability, TypeRef},
31-
AdtId, AssocItemId, DefWithBodyId, FunctionId, StructFieldId, TypeAliasId, VariantId,
31+
AdtId, AssocItemId, DefWithBodyId, FunctionId, StructFieldId, TraitId, TypeAliasId, VariantId,
3232
};
3333
use hir_expand::{diagnostics::DiagnosticSink, name::name};
3434
use ra_arena::map::ArenaMap;
@@ -540,8 +540,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
540540
Some(struct_.into())
541541
}
542542

543+
fn resolve_ops_index(&self) -> Option<TraitId> {
544+
self.resolve_lang_item("index")?.as_trait()
545+
}
546+
543547
fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
544-
let trait_ = self.resolve_lang_item("index")?.as_trait()?;
548+
let trait_ = self.resolve_ops_index()?;
545549
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
546550
}
547551
}

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
429429
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
430430
let index_ty = self.infer_expr(*index, &Expectation::none());
431431

432-
self.resolve_associated_type_with_params(
433-
base_ty,
434-
self.resolve_ops_index_output(),
435-
&[index_ty],
436-
)
432+
if let (Some(index_trait), Some(krate)) =
433+
(self.resolve_ops_index(), self.resolver.krate())
434+
{
435+
let canonicalized = self.canonicalizer().canonicalize_ty(base_ty);
436+
let self_ty = method_resolution::resolve_indexing_op(
437+
self.db,
438+
&canonicalized.value,
439+
self.trait_env.clone(),
440+
krate,
441+
index_trait,
442+
);
443+
let self_ty =
444+
self_ty.map_or(Ty::Unknown, |t| canonicalized.decanonicalize_ty(t.value));
445+
self.resolve_associated_type_with_params(
446+
self_ty,
447+
self.resolve_ops_index_output(),
448+
&[index_ty],
449+
)
450+
} else {
451+
Ty::Unknown
452+
}
437453
}
438454
Expr::Tuple { exprs } => {
439455
let mut tys = match &expected.ty {

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
db::HirDatabase,
2121
primitive::{FloatBitness, Uncertain},
2222
utils::all_super_traits,
23-
Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
23+
ApplicationTy, Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
2424
};
2525

2626
/// This is used as a key for indexing impls.
@@ -214,7 +214,7 @@ pub fn iterate_method_candidates<T>(
214214
// the methods by autoderef order of *receiver types*, not *self
215215
// types*.
216216

217-
let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
217+
let deref_chain = autoderef_method_receiver(db, krate, ty);
218218
for i in 0..deref_chain.len() {
219219
if let Some(result) = iterate_method_candidates_with_autoref(
220220
&deref_chain[i..],
@@ -447,6 +447,25 @@ fn iterate_inherent_methods<T>(
447447
None
448448
}
449449

450+
/// Returns the self type for the index trait call.
451+
pub fn resolve_indexing_op(
452+
db: &impl HirDatabase,
453+
ty: &Canonical<Ty>,
454+
env: Arc<TraitEnvironment>,
455+
krate: CrateId,
456+
index_trait: TraitId,
457+
) -> Option<Canonical<Ty>> {
458+
let ty = InEnvironment { value: ty.clone(), environment: env.clone() };
459+
let deref_chain = autoderef_method_receiver(db, krate, ty);
460+
for ty in deref_chain {
461+
let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone());
462+
if db.trait_solve(krate, goal).is_some() {
463+
return Some(ty);
464+
}
465+
}
466+
None
467+
}
468+
450469
fn is_valid_candidate(
451470
db: &impl HirDatabase,
452471
name: Option<&Name>,
@@ -548,3 +567,20 @@ fn generic_implements_goal(
548567
let obligation = super::Obligation::Trait(trait_ref);
549568
Canonical { num_vars, value: InEnvironment::new(env, obligation) }
550569
}
570+
571+
fn autoderef_method_receiver(
572+
db: &impl HirDatabase,
573+
krate: CrateId,
574+
ty: InEnvironment<Canonical<Ty>>,
575+
) -> Vec<Canonical<Ty>> {
576+
let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
577+
// As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
578+
if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
579+
deref_chain.last().map(|ty| &ty.value)
580+
{
581+
let num_vars = deref_chain.last().unwrap().num_vars;
582+
let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
583+
deref_chain.push(Canonical { value: unsized_ty, num_vars })
584+
}
585+
deref_chain
586+
}

crates/ra_hir_ty/src/tests/method_resolution.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,24 @@ fn test() { (&S).foo()<|>; }
838838
assert_eq!(t, "u128");
839839
}
840840

841+
#[test]
842+
fn method_resolution_unsize_array() {
843+
let t = type_at(
844+
r#"
845+
//- /main.rs
846+
#[lang = "slice"]
847+
impl<T> [T] {
848+
fn len(&self) -> usize { loop {} }
849+
}
850+
fn test() {
851+
let a = [1, 2, 3];
852+
a.len()<|>;
853+
}
854+
"#,
855+
);
856+
assert_eq!(t, "usize");
857+
}
858+
841859
#[test]
842860
fn method_resolution_trait_from_prelude() {
843861
let (db, pos) = TestDB::with_position(

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,34 @@ mod ops {
567567
assert_eq!("Foo", type_at_pos(&db, pos));
568568
}
569569

570+
#[test]
571+
fn infer_ops_index_autoderef() {
572+
let (db, pos) = TestDB::with_position(
573+
r#"
574+
//- /main.rs crate:main deps:std
575+
fn test() {
576+
let a = &[1u32, 2, 3];
577+
let b = a[1];
578+
b<|>;
579+
}
580+
581+
//- /std.rs crate:std
582+
impl<T> ops::Index<u32> for [T] {
583+
type Output = T;
584+
}
585+
586+
#[prelude_import] use ops::*;
587+
mod ops {
588+
#[lang = "index"]
589+
pub trait Index<Idx> {
590+
type Output;
591+
}
592+
}
593+
"#,
594+
);
595+
assert_eq!("u32", type_at_pos(&db, pos));
596+
}
597+
570598
#[test]
571599
fn deref_trait() {
572600
let t = type_at(

0 commit comments

Comments
 (0)