]> git.lizzy.rs Git - rust.git/commitdiff
Unify with the autorefed/autoderefed receiver type during method resolution
authorFlorian Diebold <flodiebold@gmail.com>
Sun, 17 Feb 2019 13:43:59 +0000 (14:43 +0100)
committerFlorian Diebold <flodiebold@gmail.com>
Sun, 17 Feb 2019 13:44:39 +0000 (14:44 +0100)
crates/ra_hir/src/ty.rs
crates/ra_hir/src/ty/method_resolution.rs
crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap [new file with mode: 0644]
crates/ra_hir/src/ty/tests.rs
crates/ra_ide_api/src/completion/complete_dot.rs

index f32c77fafd3f4a8c015bc15a904bd70eb9056cca..562ad1f4961ba8322515d60000bbc41270fa8986 100644 (file)
@@ -1381,12 +1381,12 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
             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 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     }
                     _ => (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));
index 8d1076774dab86cdaa858517dfec9e48eea45569..94b757af2102ddbb830f102f7101a62aec7735e7 100644 (file)
@@ -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<Function> {
-        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 @@ pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<Functio
     pub fn iterate_methods<T>(
         self,
         db: &impl HirDatabase,
-        mut callback: impl FnMut(Function) -> Option<T>,
+        mut callback: impl FnMut(&Ty, Function) -> Option<T>,
     ) -> Option<T> {
         // 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 @@ pub fn iterate_methods<T>(
                 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 (file)
index 0000000..f609eaf
--- /dev/null
@@ -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<T>
+[98; 100) '{}': ()
+[111; 112) 'o': Option<u32>
+[127; 165) '{     ...f(); }': ()
+[133; 146) '(&o).as_ref()': Option<&u32>
+[134; 136) '&o': &Option<u32>
+[135; 136) 'o': Option<u32>
+[152; 153) 'o': Option<u32>
+[152; 162) 'o.as_ref()': Option<&u32>
+
index 1eca68dc5461776d315c0ad8067acd8662ed174d..5eb9c4f5bcc8c7de160167f902ddeab9017624ba 100644 (file)
@@ -538,6 +538,26 @@ fn test() -> i128 {
     );
 }
 
+#[test]
+fn infer_impl_generics_with_autoderef() {
+    check_inference(
+        "infer_impl_generics_with_autoderef",
+        r#"
+enum Option<T> {
+    Some(T),
+    None,
+}
+impl<T> Option<T> {
+    fn as_ref(&self) -> Option<&T> {}
+}
+fn test(o: Option<u32>) {
+    (&o).as_ref();
+    o.as_ref();
+}
+"#,
+    );
+}
+
 #[test]
 fn infer_generic_chain() {
     check_inference(
index be839345f80e8f3c2c29ca7529795b4406fb1a22..20fa323ce9903f821a1c888f6bda5b009d78c48c 100644 (file)
@@ -63,7 +63,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
 }
 
 fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
-    receiver.iterate_methods(ctx.db, |func| {
+    receiver.iterate_methods(ctx.db, |_ty, func| {
         let sig = func.signature(ctx.db);
         if sig.has_self_param() {
             CompletionItem::new(