Support auto-deref in argument position

This commit is contained in:
uHOOCCOOHu 2019-09-18 04:15:31 +08:00
parent 870ce4b1a5
commit 5205c84ec7
No known key found for this signature in database
GPG key ID: CED392DE0C483D00
2 changed files with 161 additions and 1 deletions

View file

@ -806,6 +806,47 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
}
fn unify_with_autoderef(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
macro_rules! ty_app {
($ctor:pat, $param:pat) => {
Ty::Apply(ApplicationTy { ctor: $ctor, parameters: $param })
};
}
// If given type and expected type are compatible reference,
// trigger auto-deref.
let (_to_mut, from_ty, to_ty) =
match (&*self.resolve_ty_shallow(&from_ty), &*self.resolve_ty_shallow(&to_ty)) {
(
ty_app!(TypeCtor::Ref(from_mut), from_param),
ty_app!(TypeCtor::Ref(to_mut), to_param),
) if *from_mut == Mutability::Mut || from_mut == to_mut => {
(to_mut, from_param[0].clone(), to_param[0].clone())
}
_ => {
// Otherwise, just unify
return self.unify(&from_ty, &to_ty);
}
};
let canonicalized = self.canonicalizer().canonicalize_ty(from_ty);
// FIXME: Auto DerefMut
for derefed_ty in
autoderef::autoderef(self.db, &self.resolver.clone(), canonicalized.value.clone())
{
let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
match (&*self.resolve_ty_shallow(&derefed_ty), &*self.resolve_ty_shallow(&to_ty)) {
// Unify when constructor matches.
(ty_app!(from_ctor, _), ty_app!(to_ctor, _)) if from_ctor == to_ctor => {
return self.unify(&derefed_ty, &to_ty);
}
_ => {}
}
}
false
}
fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(tgt_expr, expected);
let could_unify = self.unify(&ty, &expected.ty);
@ -1285,7 +1326,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
let param_ty = self.normalize_associated_types_in(param_ty);
self.infer_expr(arg, &Expectation::has_type(param_ty));
let arg_ty = self.infer_expr_inner(arg, &Expectation::has_type(param_ty.clone()));
self.unify_with_autoderef(&arg_ty, &param_ty);
}
}
}

View file

@ -800,6 +800,124 @@ fn test2(a1: *const A, a2: *mut A) {
);
}
#[test]
fn infer_argument_autoderef() {
assert_snapshot!(
infer(r#"
#[lang = "deref"]
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
struct A<T>(T);
impl<T: Copy> A<T> {
fn foo(&self) -> T {
self.0
}
}
struct B<T>(T);
impl<T> Deref for B<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn test() {
A::foo(&&B(B(A(42))));
}
"#),
@r###"
[76; 80) 'self': &Self
[153; 157) 'self': &A<T>
[164; 186) '{ ... }': T
[174; 178) 'self': &A<T>
[174; 180) 'self.0': T
[267; 271) 'self': &B<T>
[290; 313) '{ ... }': &T
[300; 307) '&self.0': &T
[301; 305) 'self': &B<T>
[301; 307) 'self.0': T
[327; 357) '{ ...))); }': ()
[333; 339) 'A::foo': fn foo<i32>(&A<T>) -> T
[333; 354) 'A::foo...42))))': i32
[340; 353) '&&B(B(A(42)))': &&B<B<A<i32>>>
[341; 353) '&B(B(A(42)))': &B<B<A<i32>>>
[342; 343) 'B': B<B<A<i32>>>(T) -> B<T>
[342; 353) 'B(B(A(42)))': B<B<A<i32>>>
[344; 345) 'B': B<A<i32>>(T) -> B<T>
[344; 352) 'B(A(42))': B<A<i32>>
[346; 347) 'A': A<i32>(T) -> A<T>
[346; 351) 'A(42)': A<i32>
[348; 350) '42': i32
"###
);
}
#[test]
fn infer_method_argument_autoderef() {
assert_snapshot!(
infer(r#"
#[lang = "deref"]
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
struct A<T>(*mut T);
impl<T: Copy> A<T> {
fn foo(&self, x: &A<T>) -> T {
x
}
}
struct B<T>(T);
impl<T> Deref for B<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn test(a: A<i32>) {
A(0 as *mut _).foo(&&B(B(a)));
}
"#),
@r###"
[76; 80) 'self': &Self
[158; 162) 'self': &A<T>
[164; 165) 'x': &A<T>
[179; 196) '{ ... }': &A<T>
[189; 190) 'x': &A<T>
[277; 281) 'self': &B<T>
[300; 323) '{ ... }': &T
[310; 317) '&self.0': &T
[311; 315) 'self': &B<T>
[311; 317) 'self.0': T
[335; 336) 'a': A<i32>
[346; 384) '{ ...))); }': ()
[352; 353) 'A': A<i32>(*mut T) -> A<T>
[352; 366) 'A(0 as *mut _)': A<i32>
[352; 381) 'A(0 as...B(a)))': i32
[354; 355) '0': i32
[354; 365) '0 as *mut _': *mut i32
[371; 380) '&&B(B(a))': &&B<B<A<i32>>>
[372; 380) '&B(B(a))': &B<B<A<i32>>>
[373; 374) 'B': B<B<A<i32>>>(T) -> B<T>
[373; 380) 'B(B(a))': B<B<A<i32>>>
[375; 376) 'B': B<A<i32>>(T) -> B<T>
[375; 379) 'B(a)': B<A<i32>>
[377; 378) 'a': A<i32>
"###
);
}
#[test]
fn bug_484() {
assert_snapshot!(