Skip to content

Commit 0e5e04b

Browse files
Rollup merge of #116250 - estebank:closure-arg-inference-span, r=petrochenkov
On type error of closure call argument, point at earlier calls that affected inference Mitigate part of #71209. When we encounter a type error on a specific argument of a closure call argument, where the closure's definition doesn't have a type specified, look for other calls of the closure to try and find the specific call that cased that argument to be inferred of the expected type. ``` 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); | ~~ ```
2 parents 4be9cfa + 7bb594f commit 0e5e04b

File tree

3 files changed

+221
-5
lines changed

3 files changed

+221
-5
lines changed

Diff for: compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+69-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_errors::{
1616
use rustc_hir as hir;
1717
use rustc_hir::def::{CtorOf, DefKind, Res};
1818
use rustc_hir::def_id::DefId;
19+
use rustc_hir::intravisit::Visitor;
1920
use rustc_hir::{ExprKind, Node, QPath};
2021
use rustc_hir_analysis::astconv::AstConv;
2122
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
@@ -28,7 +29,7 @@ use rustc_infer::infer::TypeTrace;
2829
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
2930
use rustc_middle::ty::adjustment::AllowTwoPhase;
3031
use rustc_middle::ty::visit::TypeVisitableExt;
31-
use rustc_middle::ty::{self, IsSuggestable, Ty};
32+
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
3233
use rustc_session::Session;
3334
use rustc_span::symbol::{kw, Ident};
3435
use rustc_span::{self, sym, BytePos, Span};
@@ -722,6 +723,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
722723
&mut err,
723724
fn_def_id,
724725
callee_ty,
726+
call_expr,
727+
None,
725728
Some(mismatch_idx),
726729
is_method,
727730
);
@@ -826,6 +829,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
826829
&mut err,
827830
fn_def_id,
828831
callee_ty,
832+
call_expr,
833+
Some(expected_ty),
829834
Some(expected_idx.as_usize()),
830835
is_method,
831836
);
@@ -1208,7 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12081213
}
12091214

12101215
// Call out where the function is defined
1211-
self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
1216+
self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method);
12121217

12131218
// And add a suggestion block for all of the parameters
12141219
let suggestion_text = match suggestion_text {
@@ -1899,6 +1904,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18991904
err: &mut Diagnostic,
19001905
callable_def_id: Option<DefId>,
19011906
callee_ty: Option<Ty<'tcx>>,
1907+
call_expr: &'tcx hir::Expr<'tcx>,
1908+
expected_ty: Option<Ty<'tcx>>,
19021909
// A specific argument should be labeled, instead of all of them
19031910
expected_idx: Option<usize>,
19041911
is_method: bool,
@@ -2015,6 +2022,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20152022
let param = expected_idx
20162023
.and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
20172024
let (kind, span) = if let Some(param) = param {
2025+
// Try to find earlier invocations of this closure to find if the type mismatch
2026+
// is because of inference. If we find one, point at them.
2027+
let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] };
2028+
let node = self.tcx
2029+
.opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id))
2030+
.and_then(|hir_id| self.tcx.hir().find(hir_id));
2031+
match node {
2032+
Some(hir::Node::Item(item)) => call_finder.visit_item(item),
2033+
Some(hir::Node::TraitItem(item)) => call_finder.visit_trait_item(item),
2034+
Some(hir::Node::ImplItem(item)) => call_finder.visit_impl_item(item),
2035+
_ => {}
2036+
}
2037+
let typeck = self.typeck_results.borrow();
2038+
for (rcvr, args) in call_finder.calls {
2039+
if let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
2040+
&& let ty::Closure(call_def_id, _) = rcvr_ty.kind()
2041+
&& def_id == *call_def_id
2042+
&& let Some(idx) = expected_idx
2043+
&& let Some(arg) = args.get(idx)
2044+
&& let Some(arg_ty) = typeck.node_type_opt(arg.hir_id)
2045+
&& let Some(expected_ty) = expected_ty
2046+
&& self.can_eq(self.param_env, arg_ty, expected_ty)
2047+
{
2048+
let mut sp: MultiSpan = vec![arg.span].into();
2049+
sp.push_span_label(
2050+
arg.span,
2051+
format!("expected because this argument is of type `{arg_ty}`"),
2052+
);
2053+
sp.push_span_label(rcvr.span, "in this closure call");
2054+
err.span_note(
2055+
sp,
2056+
format!(
2057+
"expected because the closure was earlier called with an \
2058+
argument of type `{arg_ty}`",
2059+
),
2060+
);
2061+
break;
2062+
}
2063+
}
2064+
20182065
("closure parameter", param.span)
20192066
} else {
20202067
("closure", self.tcx.def_span(def_id))
@@ -2028,3 +2075,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20282075
}
20292076
}
20302077
}
2078+
2079+
struct FindClosureArg<'tcx> {
2080+
tcx: TyCtxt<'tcx>,
2081+
calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
2082+
}
2083+
2084+
impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
2085+
type NestedFilter = rustc_middle::hir::nested_filter::All;
2086+
2087+
fn nested_visit_map(&mut self) -> Self::Map {
2088+
self.tcx.hir()
2089+
}
2090+
2091+
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
2092+
if let hir::ExprKind::Call(rcvr, args) = ex.kind {
2093+
self.calls.push((rcvr, args));
2094+
}
2095+
hir::intravisit::walk_expr(self, ex);
2096+
}
2097+
}
+30-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11
use std::ops::FnMut;
22

3-
pub fn main() {
3+
fn main() {
44
let mut f = |x: isize, y: isize| -> isize { x + y };
5-
let z = f(1_usize, 2); //~ ERROR mismatched types
5+
let z = f(1_usize, 2); //~ ERROR mismatched types
66
println!("{}", z);
7+
let mut g = |x, y| { x + y };
8+
let y = g(1_i32, 2);
9+
let z = g(1_usize, 2); //~ ERROR mismatched types
10+
println!("{}", z);
11+
}
12+
13+
trait T {
14+
fn bar(&self) {
15+
let identity = |x| x;
16+
identity(1u8);
17+
identity(1u16); //~ ERROR mismatched types
18+
let identity = |x| x;
19+
identity(&1u8);
20+
identity(&1u16); //~ ERROR mismatched types
21+
}
22+
}
23+
24+
struct S;
25+
26+
impl T for S {
27+
fn bar(&self) {
28+
let identity = |x| x;
29+
identity(1u8);
30+
identity(1u16); //~ ERROR mismatched types
31+
let identity = |x| x;
32+
identity(&1u8);
33+
identity(&1u16); //~ ERROR mismatched types
34+
}
735
}

Diff for: tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr

+122-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,127 @@ help: change the type of the numeric literal from `usize` to `isize`
1616
LL | let z = f(1_isize, 2);
1717
| ~~~~~
1818

19-
error: aborting due to previous error
19+
error[E0308]: mismatched types
20+
--> $DIR/unboxed-closures-type-mismatch.rs:9:15
21+
|
22+
LL | let z = g(1_usize, 2);
23+
| - ^^^^^^^ expected `i32`, found `usize`
24+
| |
25+
| arguments to this function are incorrect
26+
|
27+
note: expected because the closure was earlier called with an argument of type `i32`
28+
--> $DIR/unboxed-closures-type-mismatch.rs:8:15
29+
|
30+
LL | let y = g(1_i32, 2);
31+
| - ^^^^^ expected because this argument is of type `i32`
32+
| |
33+
| in this closure call
34+
note: closure parameter defined here
35+
--> $DIR/unboxed-closures-type-mismatch.rs:7:18
36+
|
37+
LL | let mut g = |x, y| { x + y };
38+
| ^
39+
help: change the type of the numeric literal from `usize` to `i32`
40+
|
41+
LL | let z = g(1_i32, 2);
42+
| ~~~
43+
44+
error[E0308]: mismatched types
45+
--> $DIR/unboxed-closures-type-mismatch.rs:17:18
46+
|
47+
LL | identity(1u16);
48+
| -------- ^^^^ expected `u8`, found `u16`
49+
| |
50+
| arguments to this function are incorrect
51+
|
52+
note: expected because the closure was earlier called with an argument of type `u8`
53+
--> $DIR/unboxed-closures-type-mismatch.rs:16:18
54+
|
55+
LL | identity(1u8);
56+
| -------- ^^^ expected because this argument is of type `u8`
57+
| |
58+
| in this closure call
59+
note: closure parameter defined here
60+
--> $DIR/unboxed-closures-type-mismatch.rs:15:25
61+
|
62+
LL | let identity = |x| x;
63+
| ^
64+
help: change the type of the numeric literal from `u16` to `u8`
65+
|
66+
LL | identity(1u8);
67+
| ~~
68+
69+
error[E0308]: mismatched types
70+
--> $DIR/unboxed-closures-type-mismatch.rs:20:18
71+
|
72+
LL | identity(&1u16);
73+
| -------- ^^^^^ expected `&u8`, found `&u16`
74+
| |
75+
| arguments to this function are incorrect
76+
|
77+
= note: expected reference `&u8`
78+
found reference `&u16`
79+
note: expected because the closure was earlier called with an argument of type `&u8`
80+
--> $DIR/unboxed-closures-type-mismatch.rs:19:18
81+
|
82+
LL | identity(&1u8);
83+
| -------- ^^^^ expected because this argument is of type `&u8`
84+
| |
85+
| in this closure call
86+
note: closure parameter defined here
87+
--> $DIR/unboxed-closures-type-mismatch.rs:18:25
88+
|
89+
LL | let identity = |x| x;
90+
| ^
91+
92+
error[E0308]: mismatched types
93+
--> $DIR/unboxed-closures-type-mismatch.rs:30:18
94+
|
95+
LL | identity(1u16);
96+
| -------- ^^^^ expected `u8`, found `u16`
97+
| |
98+
| arguments to this function are incorrect
99+
|
100+
note: expected because the closure was earlier called with an argument of type `u8`
101+
--> $DIR/unboxed-closures-type-mismatch.rs:29:18
102+
|
103+
LL | identity(1u8);
104+
| -------- ^^^ expected because this argument is of type `u8`
105+
| |
106+
| in this closure call
107+
note: closure parameter defined here
108+
--> $DIR/unboxed-closures-type-mismatch.rs:28:25
109+
|
110+
LL | let identity = |x| x;
111+
| ^
112+
help: change the type of the numeric literal from `u16` to `u8`
113+
|
114+
LL | identity(1u8);
115+
| ~~
116+
117+
error[E0308]: mismatched types
118+
--> $DIR/unboxed-closures-type-mismatch.rs:33:18
119+
|
120+
LL | identity(&1u16);
121+
| -------- ^^^^^ expected `&u8`, found `&u16`
122+
| |
123+
| arguments to this function are incorrect
124+
|
125+
= note: expected reference `&u8`
126+
found reference `&u16`
127+
note: expected because the closure was earlier called with an argument of type `&u8`
128+
--> $DIR/unboxed-closures-type-mismatch.rs:32:18
129+
|
130+
LL | identity(&1u8);
131+
| -------- ^^^^ expected because this argument is of type `&u8`
132+
| |
133+
| in this closure call
134+
note: closure parameter defined here
135+
--> $DIR/unboxed-closures-type-mismatch.rs:31:25
136+
|
137+
LL | let identity = |x| x;
138+
| ^
139+
140+
error: aborting due to 6 previous errors
20141

21142
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)