Skip to content

Commit a86d33b

Browse files
committed
Point at expressions where inference refines an unexpected type
Address rust-lang#106355.
1 parent b7cdb63 commit a86d33b

24 files changed

+299
-17
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+159-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@ use rustc_errors::MultiSpan;
44
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
55
use rustc_hir as hir;
66
use rustc_hir::def::CtorKind;
7+
use rustc_hir::intravisit::Visitor;
78
use rustc_hir::lang_items::LangItem;
89
use rustc_hir::{is_range_literal, Node};
910
use rustc_infer::infer::InferOk;
1011
use rustc_middle::lint::in_external_macro;
1112
use rustc_middle::middle::stability::EvalResult;
1213
use rustc_middle::ty::adjustment::AllowTwoPhase;
1314
use rustc_middle::ty::error::{ExpectedFound, TypeError};
14-
use rustc_middle::ty::print::with_no_trimmed_paths;
15-
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut};
15+
use rustc_middle::ty::fold::TypeFolder;
16+
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
17+
use rustc_middle::ty::{
18+
self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable,
19+
};
1620
use rustc_span::symbol::{sym, Symbol};
1721
use rustc_span::{BytePos, Span};
1822
use rustc_trait_selection::infer::InferCtxtExt as _;
@@ -53,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5357
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
5458
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
5559
|| self.suggest_into(err, expr, expr_ty, expected)
56-
|| self.suggest_floating_point_literal(err, expr, expected);
60+
|| self.suggest_floating_point_literal(err, expr, expected)
61+
|| self.point_inference_types(err, expr);
5762
}
5863

5964
pub fn emit_coerce_suggestions(
@@ -205,6 +210,157 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
205210
(expected, Some(err))
206211
}
207212

213+
fn point_inference_types(&self, err: &mut Diagnostic, expr: &hir::Expr<'_>) -> bool {
214+
let tcx = self.tcx;
215+
let map = self.tcx.hir();
216+
217+
// Hack to make equality checks on types with inference variables and regions useful.
218+
struct TypeEraser<'tcx> {
219+
tcx: TyCtxt<'tcx>,
220+
}
221+
impl<'tcx> TypeFolder<'tcx> for TypeEraser<'tcx> {
222+
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
223+
self.tcx
224+
}
225+
fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> {
226+
self.tcx().lifetimes.re_erased
227+
}
228+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
229+
if !t.needs_infer() && !t.has_erasable_regions() {
230+
return t;
231+
}
232+
match *t.kind() {
233+
ty::Infer(ty::TyVar(_) | ty::FreshTy(_)) => {
234+
self.tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0)))
235+
}
236+
ty::Infer(ty::IntVar(_) | ty::FreshIntTy(_)) => {
237+
self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 }))
238+
}
239+
ty::Infer(ty::FloatVar(_) | ty::FreshFloatTy(_)) => {
240+
self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 }))
241+
}
242+
_ => t.super_fold_with(self),
243+
}
244+
}
245+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
246+
ct.super_fold_with(self)
247+
}
248+
}
249+
250+
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; };
251+
let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
252+
let hir::def::Res::Local(hir_id) = p.res else { return false; };
253+
let Some(node) = map.find(hir_id) else { return false; };
254+
let hir::Node::Pat(pat) = node else { return false; };
255+
let parent = map.get_parent_node(pat.hir_id);
256+
let Some(hir::Node::Local(hir::Local {
257+
ty: None,
258+
init: Some(init),
259+
..
260+
})) = map.find(parent) else { return false; };
261+
262+
let ty = self.node_ty(init.hir_id);
263+
if ty.is_closure() || init.span.overlaps(expr.span) {
264+
return false;
265+
}
266+
let mut span_labels = vec![(
267+
init.span,
268+
with_forced_trimmed_paths!(format!(
269+
"here the type of `{ident}` is inferred to be `{ty}`",
270+
)),
271+
)];
272+
273+
// Locate all the usages of the relevant binding.
274+
struct FindExprs<'hir> {
275+
hir_id: hir::HirId,
276+
uses: Vec<&'hir hir::Expr<'hir>>,
277+
}
278+
impl<'v> Visitor<'v> for FindExprs<'v> {
279+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
280+
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind
281+
&& let hir::def::Res::Local(hir_id) = path.res
282+
&& hir_id == self.hir_id
283+
{
284+
self.uses.push(ex);
285+
}
286+
hir::intravisit::walk_expr(self, ex);
287+
}
288+
}
289+
290+
let mut expr_finder = FindExprs { hir_id, uses: vec![] };
291+
let id = map.get_parent_item(hir_id);
292+
let hir_id: hir::HirId = id.into();
293+
294+
if let Some(node) = map.find(hir_id) && let Some(body_id) = node.body_id() {
295+
let body = map.body(body_id);
296+
expr_finder.visit_expr(body.value);
297+
let mut eraser = TypeEraser { tcx };
298+
let mut prev = eraser.fold_ty(ty);
299+
300+
for ex in expr_finder.uses {
301+
if ex.span.overlaps(expr.span) { break; }
302+
let parent = map.get_parent_node(ex.hir_id);
303+
if let Some(hir::Node::Expr(expr))
304+
| Some(hir::Node::Stmt(hir::Stmt {
305+
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
306+
..
307+
})) = &map.find(parent)
308+
&& let hir::ExprKind::MethodCall(s, rcvr, args, span) = expr.kind
309+
&& rcvr.hir_id == ex.hir_id
310+
{
311+
let ty = if let Ok(m) = self.lookup_method(ty, s, span, expr, rcvr, args) {
312+
// We get the self type from `lookup_method` because the `rcvr` node
313+
// type will not have had any adjustments from the fn arguments.
314+
let ty = m.sig.inputs_and_output[0];
315+
match ty.kind() {
316+
// Remove one layer of references to account for `&mut self` and
317+
// `&self`, so that we can compare it against the binding.
318+
ty::Ref(_, ty, _) => *ty,
319+
_ => ty,
320+
}
321+
} else {
322+
self.node_ty(rcvr.hir_id)
323+
};
324+
let ty = eraser.fold_ty(ty);
325+
if ty.references_error() {
326+
break;
327+
}
328+
if ty != prev {
329+
span_labels.push((
330+
s.ident.span,
331+
with_forced_trimmed_paths!(format!(
332+
"here the type of `{ident}` is inferred to be `{ty}`",
333+
)),
334+
));
335+
prev = ty;
336+
}
337+
} else {
338+
let ty = eraser.fold_ty(self.node_ty(ex.hir_id));
339+
if ty.references_error() {
340+
break;
341+
}
342+
if ty != prev {
343+
span_labels.push((
344+
ex.span,
345+
with_forced_trimmed_paths!(format!(
346+
"here the type of `{ident}` is inferred to be `{ty}`",
347+
)),
348+
));
349+
}
350+
prev = ty;
351+
}
352+
if ex.hir_id == expr.hir_id {
353+
// Stop showing spans after the error type was emitted.
354+
break;
355+
}
356+
}
357+
}
358+
for (sp, label) in span_labels {
359+
err.span_label(sp, &label);
360+
}
361+
true
362+
}
363+
208364
fn annotate_expected_due_to_let_ty(
209365
&self,
210366
err: &mut Diagnostic,

src/test/ui/argument-suggestions/two-mismatch-notes.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: arguments to this function are incorrect
22
--> $DIR/two-mismatch-notes.rs:10:5
33
|
4+
LL | let w = Wrapper::<isize>(1isize);
5+
| ------------------------ here the type of `w` is inferred to be `Wrapper<isize>`
46
LL | foo(f, w);
57
| ^^^
68
|

src/test/ui/async-await/dont-suggest-missing-await.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/dont-suggest-missing-await.rs:14:18
33
|
4+
LL | let x = make_u32();
5+
| ---------- here the type of `x` is inferred to be `impl Future<Output = u32>`
46
LL | take_u32(x)
57
| -------- ^ expected `u32`, found opaque type
68
| |

src/test/ui/async-await/suggest-missing-await-closure.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/suggest-missing-await-closure.rs:16:18
33
|
4+
LL | let x = make_u32();
5+
| ---------- here the type of `x` is inferred to be `impl Future<Output = u32>`
46
LL | take_u32(x)
57
| -------- ^ expected `u32`, found opaque type
68
| |

src/test/ui/async-await/suggest-missing-await.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/suggest-missing-await.rs:12:14
33
|
4+
LL | let x = make_u32();
5+
| ---------- here the type of `x` is inferred to be `impl Future<Output = u32>`
46
LL | take_u32(x)
57
| -------- ^ expected `u32`, found opaque type
68
| |

src/test/ui/closures/closure-return-type-mismatch.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/closure-return-type-mismatch.rs:7:9
33
|
4+
LL | let a = true;
5+
| ---- here the type of `a` is inferred to be `bool`
46
LL | a
57
| ^ expected `&str`, found `bool`
68
|

src/test/ui/coercion/coerce-to-bang.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ LL | fn foo(x: usize, y: !, z: usize) { }
3333
error[E0308]: mismatched types
3434
--> $DIR/coerce-to-bang.rs:26:12
3535
|
36+
LL | let b = 22;
37+
| -- here the type of `b` is inferred to be `{integer}`
38+
LL | let c = 44;
3639
LL | foo(a, b, c); // ... and hence a reference to `a` is expected to diverge.
3740
| --- ^ expected `!`, found integer
3841
| |
@@ -49,6 +52,9 @@ LL | fn foo(x: usize, y: !, z: usize) { }
4952
error[E0308]: mismatched types
5053
--> $DIR/coerce-to-bang.rs:36:12
5154
|
55+
LL | let b = 22;
56+
| -- here the type of `b` is inferred to be `{integer}`
57+
LL | let c = 44;
5258
LL | foo(a, b, c);
5359
| --- ^ expected `!`, found integer
5460
| |
@@ -65,6 +71,9 @@ LL | fn foo(x: usize, y: !, z: usize) { }
6571
error[E0308]: mismatched types
6672
--> $DIR/coerce-to-bang.rs:45:12
6773
|
74+
LL | let b = 22;
75+
| -- here the type of `b` is inferred to be `{integer}`
76+
LL | let c = 44;
6877
LL | foo(a, b, c);
6978
| --- ^ expected `!`, found integer
7079
| |

src/test/ui/generic-associated-types/collections-project-default.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ error[E0308]: mismatched types
44
LL | fn floatify_sibling<C>(ints: &C) -> <C as Collection<i32>>::Sibling<f32>
55
| ------------------------------------ expected `<C as Collection<i32>>::Sibling<f32>` because of return type
66
...
7+
LL | let mut res = <C::Family as CollectionFamily>::Member::<f32>::empty();
8+
| ------------------------------------------------------- here the type of `res` is inferred to be `<<C as Collection<i32>>::Family as CollectionFamily>::Member<f32>`
9+
...
710
LL | res
811
| ^^^ expected Collection::Sibling, found CollectionFamily::Member
912
|

src/test/ui/issues/issue-15783.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
error[E0308]: mismatched types
22
--> $DIR/issue-15783.rs:8:19
33
|
4+
LL | let x = Some(&[name]);
5+
| ------------- here the type of `x` is inferred to be `Option<_>`
46
LL | let msg = foo(x);
57
| --- ^ expected slice `[&str]`, found array `[&str; 1]`
68
| |

src/test/ui/let-else/let-else-ref-bindings.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return };
1919
error[E0308]: mismatched types
2020
--> $DIR/let-else-ref-bindings.rs:24:34
2121
|
22+
LL | let some = Some(bytes);
23+
| ----------- here the type of `some` is inferred to be `Option<_>`
24+
...
25+
LL | let Some(ref a): Option<&[u8]> = some else { return };
26+
| ---- here the type of `some` is inferred to be `Option<Vec<u8>>`
27+
...
2228
LL | let Some(a): Option<&[u8]> = some else { return };
2329
| ------------- ^^^^ expected `&[u8]`, found struct `Vec`
2430
| |
@@ -59,6 +65,12 @@ LL | let Some(ref mut a): Option<&mut [u8]> = &mut some else { return };
5965
error[E0308]: mismatched types
6066
--> $DIR/let-else-ref-bindings.rs:52:38
6167
|
68+
LL | let mut some = Some(bytes);
69+
| ----------- here the type of `some` is inferred to be `Option<_>`
70+
...
71+
LL | let Some(ref mut a): Option<&mut [u8]> = some else { return };
72+
| ---- here the type of `some` is inferred to be `Option<Vec<u8>>`
73+
...
6274
LL | let Some(a): Option<&mut [u8]> = some else { return };
6375
| ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec`
6476
| |

src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ LL | #![feature(unsized_locals, unsized_fn_params)]
1010
error[E0308]: mismatched types
1111
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:87:24
1212
|
13+
LL | let z = x.foo();
14+
| ------- here the type of `z` is inferred to be `u32`
15+
...
1316
LL | let _seetype: () = z;
1417
| -- ^ expected `()`, found `u32`
1518
| |
@@ -18,6 +21,9 @@ LL | let _seetype: () = z;
1821
error[E0308]: mismatched types
1922
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:104:24
2023
|
24+
LL | let z = x.foo();
25+
| ------- here the type of `z` is inferred to be `u64`
26+
...
2127
LL | let _seetype: () = z;
2228
| -- ^ expected `()`, found `u64`
2329
| |
@@ -60,6 +66,9 @@ LL | let z = FinalFoo::foo(x);
6066
error[E0308]: mismatched types
6167
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24
6268
|
69+
LL | let z = x.foo();
70+
| ------- here the type of `z` is inferred to be `u8`
71+
...
6372
LL | let _seetype: () = z;
6473
| -- ^ expected `()`, found `u8`
6574
| |
@@ -68,6 +77,9 @@ LL | let _seetype: () = z;
6877
error[E0308]: mismatched types
6978
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:157:24
7079
|
80+
LL | let z = x.foo();
81+
| ------- here the type of `z` is inferred to be `u32`
82+
...
7183
LL | let _seetype: () = z;
7284
| -- ^ expected `()`, found `u32`
7385
| |
@@ -76,6 +88,9 @@ LL | let _seetype: () = z;
7688
error[E0308]: mismatched types
7789
--> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:174:24
7890
|
91+
LL | let z = x.foo();
92+
| ------- here the type of `z` is inferred to be `u32`
93+
...
7994
LL | let _seetype: () = z;
8095
| -- ^ expected `()`, found `u32`
8196
| |

0 commit comments

Comments
 (0)