about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-08-27 20:53:04 +0000
committerMichael Goulet <michael@errs.io>2022-08-27 21:23:06 +0000
commit0734200e801d52662f02aff7fdd5026337798620 (patch)
tree660139b220ef44e6815a7f7cab354c67e798de6b
parentdca5f5bf8f55faaab84fb23db1b4c5ebdc404c0c (diff)
downloadrust-0734200e801d52662f02aff7fdd5026337798620.tar.gz
rust-0734200e801d52662f02aff7fdd5026337798620.zip
Use autoderef
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs94
-rw-r--r--src/test/ui/suggestions/call-boxed.rs7
-rw-r--r--src/test/ui/suggestions/call-boxed.stderr20
3 files changed, 78 insertions, 43 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 57771e0969b..45872a0b07a 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -61,55 +61,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pointing_at_return_type
     }
 
-    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
-    /// the ctor would successfully solve the type mismatch and if so, suggest it:
+    /// When encountering an fn-like type, try accessing the output of the type
+    /// // and suggesting calling it if it satisfies a predicate (i.e. if the
+    /// output has a method or a field):
     /// ```compile_fail,E0308
     /// fn foo(x: usize) -> usize { x }
     /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
     /// ```
-    fn suggest_fn_call(
+    pub(crate) fn suggest_fn_call(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
         found: Ty<'tcx>,
+        can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
-        let (def_id, output, inputs) = match *found.kind() {
-            ty::FnDef(def_id, _) => {
-                let fn_sig = found.fn_sig(self.tcx);
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
-            }
-            ty::Closure(def_id, substs) => {
-                let fn_sig = substs.as_closure().sig();
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
-            }
-            ty::Opaque(def_id, substs) => {
-                let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
-                    if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
-                    && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
-                    // args tuple will always be substs[1]
-                    && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
-                    {
-                        Some((
-                            pred.kind().rebind(proj.term.ty().unwrap()),
-                            args.len(),
-                        ))
+        // Autoderef is useful here because sometimes we box callables, etc.
+        let Some((def_id, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
+            match *found.kind() {
+                ty::FnPtr(fn_sig) => Some((None, fn_sig.output(), fn_sig.inputs().skip_binder().len())),
+                ty::FnDef(def_id, _) => {
+                    let fn_sig = found.fn_sig(self.tcx);
+                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len()))
+                }
+                ty::Closure(def_id, substs) => {
+                    let fn_sig = substs.as_closure().sig();
+                    Some((Some(def_id), fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1))
+                }
+                ty::Opaque(def_id, substs) => {
+                    let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+                        if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        // args tuple will always be substs[1]
+                        && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+                        {
+                            Some((
+                                pred.kind().rebind(proj.term.ty().unwrap()),
+                                args.len(),
+                            ))
+                        } else {
+                            None
+                        }
+                    });
+                    if let Some((output, inputs)) = sig {
+                        Some((Some(def_id), output, inputs))
                     } else {
                         None
                     }
-                });
-                if let Some((output, inputs)) = sig {
-                    (def_id, output, inputs)
-                } else {
-                    return false;
                 }
+                _ => None,
             }
-            _ => return false,
-        };
+        }) else { return false; };
 
         let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
-        let output = self.normalize_associated_types_in(expr.span, output);
-        if !output.is_ty_var() && self.can_coerce(output, expected) {
+        // We don't want to register any extra obligations, which should be
+        // implied by wf, but also because that would possibly result in
+        // erroneous errors later on.
+        let infer::InferOk { value: output, obligations: _ } =
+            self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+        if !output.is_ty_var() && can_satisfy(output) {
             let (sugg_call, mut applicability) = match inputs {
                 0 => ("".to_string(), Applicability::MachineApplicable),
                 1..=4 => (
@@ -119,11 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => ("...".to_string(), Applicability::HasPlaceholders),
             };
 
-            let msg = match self.tcx.def_kind(def_id) {
-                DefKind::Fn => "call this function",
-                DefKind::Closure | DefKind::OpaqueTy => "call this closure",
-                DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
-                DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
+            let msg = match def_id.map(|def_id| self.tcx.def_kind(def_id)) {
+                Some(DefKind::Fn) => "call this function",
+                Some(DefKind::Closure | DefKind::OpaqueTy) => "call this closure",
+                Some(DefKind::Ctor(CtorOf::Struct, _)) => "instantiate this tuple struct",
+                Some(DefKind::Ctor(CtorOf::Variant, _)) => "instantiate this tuple variant",
                 _ => "call this function",
             };
 
@@ -178,12 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 err.span_suggestion(sp, &msg, suggestion, applicability);
             }
-        } else if let (ty::FnDef(def_id, ..), true) =
-            (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+        } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+            && let ty::FnDef(def_id, ..) = &found.kind()
+            && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
         {
-            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
-                err.span_label(sp, format!("{found} defined here"));
-            }
+            err.span_label(sp, format!("{found} defined here"));
         } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
diff --git a/src/test/ui/suggestions/call-boxed.rs b/src/test/ui/suggestions/call-boxed.rs
new file mode 100644
index 00000000000..d19e4596a0c
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut x = 1i32;
+    let y = Box::new(|| 1);
+    x = y;
+    //~^ ERROR mismatched types
+    //~| HELP use parentheses to call this closure
+}
diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr
new file mode 100644
index 00000000000..9b619ac9a3f
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/call-boxed.rs:4:9
+   |
+LL |     let mut x = 1i32;
+   |                 ---- expected due to this value
+LL |     let y = Box::new(|| 1);
+   |                      -- the found closure
+LL |     x = y;
+   |         ^ expected `i32`, found struct `Box`
+   |
+   = note: expected type `i32`
+            found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
+help: use parentheses to call this closure
+   |
+LL |     x = y();
+   |          ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.