Skip to content

Commit 336a3c6

Browse files
committed
Fix #3373
Basically, we need to allow variables in the caller self type to unify with the impl's declared self type. That requires some more contortions in the variable handling. I'm looking forward to (hopefully) handling this in a cleaner way when we switch to Chalk's types and unification code.
1 parent 6db2da4 commit 336a3c6

File tree

4 files changed

+45
-6
lines changed

4 files changed

+45
-6
lines changed

crates/ra_hir_ty/src/infer/unify.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,21 @@ impl<T> Canonicalized<T> {
142142

143143
pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
144144
let mut table = InferenceTable::new();
145+
let num_vars = ty1.num_vars.max(ty2.num_vars);
145146
let vars =
146-
Substs::builder(ty1.num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
147-
let ty_with_vars = ty1.value.clone().subst_bound_vars(&vars);
148-
if !table.unify(&ty_with_vars, &ty2.value) {
147+
Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
148+
let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars);
149+
let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars);
150+
if !table.unify(&ty1_with_vars, &ty2_with_vars) {
149151
return None;
150152
}
153+
// default any type vars that weren't unified back to their original bound vars
154+
// (kind of hacky)
155+
for (i, var) in vars.iter().enumerate() {
156+
if &*table.resolve_ty_shallow(var) == var {
157+
table.unify(var, &Ty::Bound(i as u32));
158+
}
159+
}
151160
Some(
152161
Substs::builder(ty1.num_vars)
153162
.fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))

crates/ra_hir_ty/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ impl Substs {
355355
Substs(self.0[..std::cmp::min(self.0.len(), n)].into())
356356
}
357357

358+
pub fn suffix(&self, n: usize) -> Substs {
359+
Substs(self.0[self.0.len() - std::cmp::min(self.0.len(), n)..].into())
360+
}
361+
358362
pub fn as_single(&self) -> &Ty {
359363
if self.0.len() != 1 {
360364
panic!("expected substs of len 1, got {:?}", self);

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -508,10 +508,17 @@ pub(crate) fn inherent_impl_substs(
508508
impl_id: ImplId,
509509
self_ty: &Canonical<Ty>,
510510
) -> Option<Substs> {
511-
let vars = Substs::build_for_def(db, impl_id).fill_with_bound_vars(0).build();
511+
// we create a var for each type parameter of the impl; we need to keep in
512+
// mind here that `self_ty` might have vars of its own
513+
let vars =
514+
Substs::build_for_def(db, impl_id).fill_with_bound_vars(self_ty.num_vars as u32).build();
512515
let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
513-
let self_ty_with_vars = Canonical { num_vars: vars.len(), value: self_ty_with_vars };
514-
super::infer::unify(&self_ty_with_vars, self_ty)
516+
let self_ty_with_vars =
517+
Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
518+
let substs = super::infer::unify(&self_ty_with_vars, self_ty);
519+
// we only want the substs for the vars we added, not the ones from self_ty
520+
let result = substs.map(|s| s.suffix(vars.len()));
521+
result
515522
}
516523

517524
fn transform_receiver_ty(

crates/ra_hir_ty/src/tests/method_resolution.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,25 @@ where
10481048
assert_eq!(t, "{unknown}");
10491049
}
10501050

1051+
#[test]
1052+
fn method_resolution_3373() {
1053+
let t = type_at(
1054+
r#"
1055+
//- /main.rs
1056+
struct A<T>(T);
1057+
1058+
impl A<i32> {
1059+
fn from(v: i32) -> A<i32> { A(v) }
1060+
}
1061+
1062+
fn main() {
1063+
A::from(3)<|>;
1064+
}
1065+
"#,
1066+
);
1067+
assert_eq!(t, "A<i32>");
1068+
}
1069+
10511070
#[test]
10521071
fn method_resolution_slow() {
10531072
// this can get quite slow if we set the solver size limit too high

0 commit comments

Comments
 (0)