From 795d718ba17545aedb0475051332aed6db2104ed Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 17 Feb 2019 14:43:59 +0100 Subject: Unify with the autorefed/autoderefed receiver type during method resolution --- crates/ra_hir/src/ty.rs | 18 +++++++++++------- crates/ra_hir/src/ty/method_resolution.rs | 12 +++++++----- .../tests__infer_impl_generics_with_autoderef.snap | 16 ++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 20 ++++++++++++++++++++ 4 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f32c77faf..562ad1f49 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -1381,12 +1381,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::MethodCall { receiver, args, method_name, generic_args } => { let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); let resolved = receiver_ty.clone().lookup_method(self.db, method_name); - let (method_ty, def_generics) = match resolved { - Some(func) => { + let (derefed_receiver_ty, method_ty, def_generics) = match resolved { + Some((ty, func)) => { self.write_method_resolution(tgt_expr, func); - (self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) + (ty, self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) } - None => (Ty::Unknown, None), + None => (Ty::Unknown, receiver_ty, None), }; // handle provided type arguments let method_ty = if let Some(generic_args) = generic_args { @@ -1429,9 +1429,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } _ => (Ty::Unknown, Vec::new(), Ty::Unknown), }; - // TODO we would have to apply the autoderef/autoref steps here - // to get the correct receiver type to unify... - self.unify(&expected_receiver_ty, &receiver_ty); + // Apply autoref so the below unification works correctly + let actual_receiver_ty = match expected_receiver_ty { + Ty::Ref(_, mutability) => Ty::Ref(Arc::new(derefed_receiver_ty), mutability), + _ => derefed_receiver_ty, + }; + self.unify(&expected_receiver_ty, &actual_receiver_ty); + let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); for (arg, param) in args.iter().zip(param_iter) { self.infer_expr(*arg, &Expectation::has_type(param)); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 8d1076774..94b757af2 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -118,11 +118,13 @@ impl Ty { // TODO: cache this as a query? // - if so, what signature? (TyFingerprint, Name)? // - or maybe cache all names and def_ids of methods per fingerprint? - pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option { - self.iterate_methods(db, |f| { + /// Look up the method with the given name, returning the actual autoderefed + /// receiver type (but without autoref applied yet). + pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { + self.iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { - Some(f) + Some((ty.clone(), f)) } else { None } @@ -134,7 +136,7 @@ impl Ty { pub fn iterate_methods( self, db: &impl HirDatabase, - mut callback: impl FnMut(Function) -> Option, + mut callback: impl FnMut(&Ty, Function) -> Option, ) -> Option { // For method calls, rust first does any number of autoderef, and then one // autoref (i.e. when the method takes &self or &mut self). We just ignore @@ -156,7 +158,7 @@ impl Ty { for item in impl_block.items(db) { match item { ImplItem::Method(f) => { - if let Some(result) = callback(f) { + if let Some(result) = callback(&derefed_ty, f) { return Some(result); } } diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap new file mode 100644 index 000000000..f609eaf7c --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap @@ -0,0 +1,16 @@ +--- +created: "2019-02-17T13:35:06.385679926Z" +creator: insta@0.6.2 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[78; 82) 'self': &Option +[98; 100) '{}': () +[111; 112) 'o': Option +[127; 165) '{ ...f(); }': () +[133; 146) '(&o).as_ref()': Option<&u32> +[134; 136) '&o': &Option +[135; 136) 'o': Option +[152; 153) 'o': Option +[152; 162) 'o.as_ref()': Option<&u32> + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 1eca68dc5..5eb9c4f5b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -538,6 +538,26 @@ fn test() -> i128 { ); } +#[test] +fn infer_impl_generics_with_autoderef() { + check_inference( + "infer_impl_generics_with_autoderef", + r#" +enum Option { + Some(T), + None, +} +impl Option { + fn as_ref(&self) -> Option<&T> {} +} +fn test(o: Option) { + (&o).as_ref(); + o.as_ref(); +} +"#, + ); +} + #[test] fn infer_generic_chain() { check_inference( -- cgit v1.2.3