about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs71
-rw-r--r--tests/ui/unboxed-closures/unboxed-closures-type-mismatch.rs32
-rw-r--r--tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr123
3 files changed, 221 insertions, 5 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index c0332a48be3..9b7f8f80310 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -16,6 +16,7 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
@@ -28,7 +29,7 @@ use rustc_infer::infer::TypeTrace;
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::{self, IsSuggestable, Ty};
+use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{self, sym, BytePos, Span};
@@ -722,6 +723,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         &mut err,
                         fn_def_id,
                         callee_ty,
+                        call_expr,
+                        None,
                         Some(mismatch_idx),
                         is_method,
                     );
@@ -826,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &mut err,
                 fn_def_id,
                 callee_ty,
+                call_expr,
+                Some(expected_ty),
                 Some(expected_idx.as_usize()),
                 is_method,
             );
@@ -1208,7 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // Call out where the function is defined
-        self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
+        self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method);
 
         // And add a suggestion block for all of the parameters
         let suggestion_text = match suggestion_text {
@@ -1899,6 +1904,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err: &mut Diagnostic,
         callable_def_id: Option<DefId>,
         callee_ty: Option<Ty<'tcx>>,
+        call_expr: &'tcx hir::Expr<'tcx>,
+        expected_ty: Option<Ty<'tcx>>,
         // A specific argument should be labeled, instead of all of them
         expected_idx: Option<usize>,
         is_method: bool,
@@ -2015,6 +2022,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let param = expected_idx
                 .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
             let (kind, span) = if let Some(param) = param {
+                // Try to find earlier invocations of this closure to find if the type mismatch
+                // is because of inference. If we find one, point at them.
+                let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] };
+                let node = self.tcx
+                    .opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id))
+                    .and_then(|hir_id| self.tcx.hir().find(hir_id));
+                match node {
+                    Some(hir::Node::Item(item)) => call_finder.visit_item(item),
+                    Some(hir::Node::TraitItem(item)) => call_finder.visit_trait_item(item),
+                    Some(hir::Node::ImplItem(item)) => call_finder.visit_impl_item(item),
+                    _ => {}
+                }
+                let typeck = self.typeck_results.borrow();
+                for (rcvr, args) in call_finder.calls {
+                    if let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
+                        && let ty::Closure(call_def_id, _) = rcvr_ty.kind()
+                        && def_id == *call_def_id
+                        && let Some(idx) = expected_idx
+                        && let Some(arg) = args.get(idx)
+                        && let Some(arg_ty) = typeck.node_type_opt(arg.hir_id)
+                        && let Some(expected_ty) = expected_ty
+                        && self.can_eq(self.param_env, arg_ty, expected_ty)
+                    {
+                        let mut sp: MultiSpan = vec![arg.span].into();
+                        sp.push_span_label(
+                            arg.span,
+                            format!("expected because this argument is of type `{arg_ty}`"),
+                        );
+                        sp.push_span_label(rcvr.span, "in this closure call");
+                        err.span_note(
+                            sp,
+                            format!(
+                                "expected because the closure was earlier called with an \
+                                argument of type `{arg_ty}`",
+                            ),
+                        );
+                        break;
+                    }
+                }
+
                 ("closure parameter", param.span)
             } else {
                 ("closure", self.tcx.def_span(def_id))
@@ -2028,3 +2075,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 }
+
+struct FindClosureArg<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+}
+
+impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
+    type NestedFilter = rustc_middle::hir::nested_filter::All;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Call(rcvr, args) = ex.kind {
+            self.calls.push((rcvr, args));
+        }
+        hir::intravisit::walk_expr(self, ex);
+    }
+}
diff --git a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.rs b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
index 9f76849e5fb..0999f61b01a 100644
--- a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
+++ b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
@@ -1,7 +1,35 @@
 use std::ops::FnMut;
 
-pub fn main() {
+fn main() {
     let mut f = |x: isize, y: isize| -> isize { x + y };
-    let z = f(1_usize, 2);    //~ ERROR mismatched types
+    let z = f(1_usize, 2); //~ ERROR mismatched types
     println!("{}", z);
+    let mut g = |x, y| { x + y };
+    let y = g(1_i32, 2);
+    let z = g(1_usize, 2); //~ ERROR mismatched types
+    println!("{}", z);
+}
+
+trait T {
+    fn bar(&self) {
+        let identity = |x| x;
+        identity(1u8);
+        identity(1u16); //~ ERROR mismatched types
+        let identity = |x| x;
+        identity(&1u8);
+        identity(&1u16); //~ ERROR mismatched types
+    }
+}
+
+struct S;
+
+impl T  for S {
+    fn bar(&self) {
+        let identity = |x| x;
+        identity(1u8);
+        identity(1u16); //~ ERROR mismatched types
+        let identity = |x| x;
+        identity(&1u8);
+        identity(&1u16); //~ ERROR mismatched types
+    }
 }
diff --git a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
index 455f83f5721..327df50e645 100644
--- a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
+++ b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
@@ -16,6 +16,127 @@ help: change the type of the numeric literal from `usize` to `isize`
 LL |     let z = f(1_isize, 2);
    |                 ~~~~~
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:9:15
+   |
+LL |     let z = g(1_usize, 2);
+   |             - ^^^^^^^ expected `i32`, found `usize`
+   |             |
+   |             arguments to this function are incorrect
+   |
+note: expected because the closure was earlier called with an argument of type `i32`
+  --> $DIR/unboxed-closures-type-mismatch.rs:8:15
+   |
+LL |     let y = g(1_i32, 2);
+   |             - ^^^^^ expected because this argument is of type `i32`
+   |             |
+   |             in this closure call
+note: closure parameter defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:7:18
+   |
+LL |     let mut g = |x, y| { x + y };
+   |                  ^
+help: change the type of the numeric literal from `usize` to `i32`
+   |
+LL |     let z = g(1_i32, 2);
+   |                 ~~~
+
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:17:18
+   |
+LL |         identity(1u16);
+   |         -------- ^^^^ expected `u8`, found `u16`
+   |         |
+   |         arguments to this function are incorrect
+   |
+note: expected because the closure was earlier called with an argument of type `u8`
+  --> $DIR/unboxed-closures-type-mismatch.rs:16:18
+   |
+LL |         identity(1u8);
+   |         -------- ^^^ expected because this argument is of type `u8`
+   |         |
+   |         in this closure call
+note: closure parameter defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:15:25
+   |
+LL |         let identity = |x| x;
+   |                         ^
+help: change the type of the numeric literal from `u16` to `u8`
+   |
+LL |         identity(1u8);
+   |                   ~~
+
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:20:18
+   |
+LL |         identity(&1u16);
+   |         -------- ^^^^^ expected `&u8`, found `&u16`
+   |         |
+   |         arguments to this function are incorrect
+   |
+   = note: expected reference `&u8`
+              found reference `&u16`
+note: expected because the closure was earlier called with an argument of type `&u8`
+  --> $DIR/unboxed-closures-type-mismatch.rs:19:18
+   |
+LL |         identity(&1u8);
+   |         -------- ^^^^ expected because this argument is of type `&u8`
+   |         |
+   |         in this closure call
+note: closure parameter defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:18:25
+   |
+LL |         let identity = |x| x;
+   |                         ^
+
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:30:18
+   |
+LL |         identity(1u16);
+   |         -------- ^^^^ expected `u8`, found `u16`
+   |         |
+   |         arguments to this function are incorrect
+   |
+note: expected because the closure was earlier called with an argument of type `u8`
+  --> $DIR/unboxed-closures-type-mismatch.rs:29:18
+   |
+LL |         identity(1u8);
+   |         -------- ^^^ expected because this argument is of type `u8`
+   |         |
+   |         in this closure call
+note: closure parameter defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:28:25
+   |
+LL |         let identity = |x| x;
+   |                         ^
+help: change the type of the numeric literal from `u16` to `u8`
+   |
+LL |         identity(1u8);
+   |                   ~~
+
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:33:18
+   |
+LL |         identity(&1u16);
+   |         -------- ^^^^^ expected `&u8`, found `&u16`
+   |         |
+   |         arguments to this function are incorrect
+   |
+   = note: expected reference `&u8`
+              found reference `&u16`
+note: expected because the closure was earlier called with an argument of type `&u8`
+  --> $DIR/unboxed-closures-type-mismatch.rs:32:18
+   |
+LL |         identity(&1u8);
+   |         -------- ^^^^ expected because this argument is of type `&u8`
+   |         |
+   |         in this closure call
+note: closure parameter defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:31:25
+   |
+LL |         let identity = |x| x;
+   |                         ^
+
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0308`.