From ea43eb3721e9a6567ff402db442fc5c1ad942009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 2 Jan 2023 16:23:08 -0800 Subject: [PATCH 01/12] Point at expressions where inference refines an unexpected type Address #106355. --- compiler/rustc_hir_typeck/src/demand.rs | 162 +++++++++++++++++- .../two-mismatch-notes.stderr | 2 + .../dont-suggest-missing-await.stderr | 2 + .../suggest-missing-await-closure.stderr | 2 + .../async-await/suggest-missing-await.stderr | 2 + .../closure-return-type-mismatch.stderr | 2 + src/test/ui/coercion/coerce-to-bang.stderr | 9 + .../collections-project-default.stderr | 3 + src/test/ui/issues/issue-15783.stderr | 2 + .../ui/let-else/let-else-ref-bindings.stderr | 12 ++ ...e-trait-object-with-separate-params.stderr | 15 ++ src/test/ui/mismatched_types/abridged.stderr | 36 ++-- .../struct-literal-variant-in-if.stderr | 2 + ...e-does-not-interact-with-let-chains.stderr | 12 ++ src/test/ui/span/coerce-suggestions.stderr | 5 +- src/test/ui/span/issue-33884.stderr | 5 +- .../ui/structs/struct-base-wrong-type.stderr | 2 + src/test/ui/suggestions/call-boxed.stderr | 5 +- ...gest-deref-inside-macro-issue-58298.stderr | 5 +- .../issue-86100-tuple-paren-comma.stderr | 2 + src/test/ui/traits/issue-52893.stderr | 2 + src/test/ui/tuple/wrong_argument_ice-3.stderr | 3 + .../type/type-check/assignment-in-if.stderr | 18 ++ .../type/type-mismatch-same-crate-name.stderr | 6 + 24 files changed, 299 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 9c6c53abf0748..c9ee5c6cac8bd 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -4,6 +4,7 @@ use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::CtorKind; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_infer::infer::InferOk; @@ -11,8 +12,11 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; +use rustc_middle::ty::{ + self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable, +}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -53,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) - || self.suggest_floating_point_literal(err, expr, expected); + || self.suggest_floating_point_literal(err, expr, expected) + || self.point_inference_types(err, expr); } pub fn emit_coerce_suggestions( @@ -205,6 +210,157 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } + fn point_inference_types(&self, err: &mut Diagnostic, expr: &hir::Expr<'_>) -> bool { + let tcx = self.tcx; + let map = self.tcx.hir(); + + // Hack to make equality checks on types with inference variables and regions useful. + struct TypeEraser<'tcx> { + tcx: TyCtxt<'tcx>, + } + impl<'tcx> TypeFolder<'tcx> for TypeEraser<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> { + self.tcx().lifetimes.re_erased + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if !t.needs_infer() && !t.has_erasable_regions() { + return t; + } + match *t.kind() { + ty::Infer(ty::TyVar(_) | ty::FreshTy(_)) => { + self.tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))) + } + ty::Infer(ty::IntVar(_) | ty::FreshIntTy(_)) => { + self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) + } + ty::Infer(ty::FloatVar(_) | ty::FreshFloatTy(_)) => { + self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 })) + } + _ => t.super_fold_with(self), + } + } + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + ct.super_fold_with(self) + } + } + + let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; + let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; + let hir::def::Res::Local(hir_id) = p.res else { return false; }; + let Some(node) = map.find(hir_id) else { return false; }; + let hir::Node::Pat(pat) = node else { return false; }; + let parent = map.get_parent_node(pat.hir_id); + let Some(hir::Node::Local(hir::Local { + ty: None, + init: Some(init), + .. + })) = map.find(parent) else { return false; }; + + let ty = self.node_ty(init.hir_id); + if ty.is_closure() || init.span.overlaps(expr.span) { + return false; + } + let mut span_labels = vec![( + init.span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + )]; + + // Locate all the usages of the relevant binding. + struct FindExprs<'hir> { + hir_id: hir::HirId, + uses: Vec<&'hir hir::Expr<'hir>>, + } + impl<'v> Visitor<'v> for FindExprs<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind + && let hir::def::Res::Local(hir_id) = path.res + && hir_id == self.hir_id + { + self.uses.push(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + + let mut expr_finder = FindExprs { hir_id, uses: vec![] }; + let id = map.get_parent_item(hir_id); + let hir_id: hir::HirId = id.into(); + + if let Some(node) = map.find(hir_id) && let Some(body_id) = node.body_id() { + let body = map.body(body_id); + expr_finder.visit_expr(body.value); + let mut eraser = TypeEraser { tcx }; + let mut prev = eraser.fold_ty(ty); + + for ex in expr_finder.uses { + if ex.span.overlaps(expr.span) { break; } + let parent = map.get_parent_node(ex.hir_id); + if let Some(hir::Node::Expr(expr)) + | Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), + .. + })) = &map.find(parent) + && let hir::ExprKind::MethodCall(s, rcvr, args, span) = expr.kind + && rcvr.hir_id == ex.hir_id + { + let ty = if let Ok(m) = self.lookup_method(ty, s, span, expr, rcvr, args) { + // We get the self type from `lookup_method` because the `rcvr` node + // type will not have had any adjustments from the fn arguments. + let ty = m.sig.inputs_and_output[0]; + match ty.kind() { + // Remove one layer of references to account for `&mut self` and + // `&self`, so that we can compare it against the binding. + ty::Ref(_, ty, _) => *ty, + _ => ty, + } + } else { + self.node_ty(rcvr.hir_id) + }; + let ty = eraser.fold_ty(ty); + if ty.references_error() { + break; + } + if ty != prev { + span_labels.push(( + s.ident.span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + )); + prev = ty; + } + } else { + let ty = eraser.fold_ty(self.node_ty(ex.hir_id)); + if ty.references_error() { + break; + } + if ty != prev { + span_labels.push(( + ex.span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + )); + } + prev = ty; + } + if ex.hir_id == expr.hir_id { + // Stop showing spans after the error type was emitted. + break; + } + } + } + for (sp, label) in span_labels { + err.span_label(sp, &label); + } + true + } + fn annotate_expected_due_to_let_ty( &self, err: &mut Diagnostic, diff --git a/src/test/ui/argument-suggestions/two-mismatch-notes.stderr b/src/test/ui/argument-suggestions/two-mismatch-notes.stderr index 7873cf964cbbc..3ccd399863d55 100644 --- a/src/test/ui/argument-suggestions/two-mismatch-notes.stderr +++ b/src/test/ui/argument-suggestions/two-mismatch-notes.stderr @@ -1,6 +1,8 @@ error[E0308]: arguments to this function are incorrect --> $DIR/two-mismatch-notes.rs:10:5 | +LL | let w = Wrapper::(1isize); + | ------------------------ here the type of `w` is inferred to be `Wrapper` LL | foo(f, w); | ^^^ | diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr index 627bf05bba2d9..6e232dd006426 100644 --- a/src/test/ui/async-await/dont-suggest-missing-await.stderr +++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/dont-suggest-missing-await.rs:14:18 | +LL | let x = make_u32(); + | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/async-await/suggest-missing-await-closure.stderr b/src/test/ui/async-await/suggest-missing-await-closure.stderr index a5958baffbaf7..9f51832365b61 100644 --- a/src/test/ui/async-await/suggest-missing-await-closure.stderr +++ b/src/test/ui/async-await/suggest-missing-await-closure.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/suggest-missing-await-closure.rs:16:18 | +LL | let x = make_u32(); + | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr index 1196601ace090..ce4c8edaf74d6 100644 --- a/src/test/ui/async-await/suggest-missing-await.stderr +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/suggest-missing-await.rs:12:14 | +LL | let x = make_u32(); + | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/closures/closure-return-type-mismatch.stderr b/src/test/ui/closures/closure-return-type-mismatch.stderr index 3a89d30a05d20..d33cf383cbcb4 100644 --- a/src/test/ui/closures/closure-return-type-mismatch.stderr +++ b/src/test/ui/closures/closure-return-type-mismatch.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:7:9 | +LL | let a = true; + | ---- here the type of `a` is inferred to be `bool` LL | a | ^ expected `&str`, found `bool` | diff --git a/src/test/ui/coercion/coerce-to-bang.stderr b/src/test/ui/coercion/coerce-to-bang.stderr index 1207dc7e7a2ff..d2fd0f788384e 100644 --- a/src/test/ui/coercion/coerce-to-bang.stderr +++ b/src/test/ui/coercion/coerce-to-bang.stderr @@ -33,6 +33,9 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:26:12 | +LL | let b = 22; + | -- here the type of `b` is inferred to be `{integer}` +LL | let c = 44; LL | foo(a, b, c); // ... and hence a reference to `a` is expected to diverge. | --- ^ expected `!`, found integer | | @@ -49,6 +52,9 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:36:12 | +LL | let b = 22; + | -- here the type of `b` is inferred to be `{integer}` +LL | let c = 44; LL | foo(a, b, c); | --- ^ expected `!`, found integer | | @@ -65,6 +71,9 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:45:12 | +LL | let b = 22; + | -- here the type of `b` is inferred to be `{integer}` +LL | let c = 44; LL | foo(a, b, c); | --- ^ expected `!`, found integer | | diff --git a/src/test/ui/generic-associated-types/collections-project-default.stderr b/src/test/ui/generic-associated-types/collections-project-default.stderr index 5701017dc3471..c11a5acc35244 100644 --- a/src/test/ui/generic-associated-types/collections-project-default.stderr +++ b/src/test/ui/generic-associated-types/collections-project-default.stderr @@ -4,6 +4,9 @@ error[E0308]: mismatched types LL | fn floatify_sibling(ints: &C) -> >::Sibling | ------------------------------------ expected `>::Sibling` because of return type ... +LL | let mut res = ::Member::::empty(); + | ------------------------------------------------------- here the type of `res` is inferred to be `<>::Family as CollectionFamily>::Member` +... LL | res | ^^^ expected Collection::Sibling, found CollectionFamily::Member | diff --git a/src/test/ui/issues/issue-15783.stderr b/src/test/ui/issues/issue-15783.stderr index 660dfe9ed3d51..74a7c5de7abe2 100644 --- a/src/test/ui/issues/issue-15783.stderr +++ b/src/test/ui/issues/issue-15783.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/issue-15783.rs:8:19 | +LL | let x = Some(&[name]); + | ------------- here the type of `x` is inferred to be `Option<_>` LL | let msg = foo(x); | --- ^ expected slice `[&str]`, found array `[&str; 1]` | | diff --git a/src/test/ui/let-else/let-else-ref-bindings.stderr b/src/test/ui/let-else/let-else-ref-bindings.stderr index 56b9e073330a6..39b57ceb43d37 100644 --- a/src/test/ui/let-else/let-else-ref-bindings.stderr +++ b/src/test/ui/let-else/let-else-ref-bindings.stderr @@ -19,6 +19,12 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 | +LL | let some = Some(bytes); + | ----------- here the type of `some` is inferred to be `Option<_>` +... +LL | let Some(ref a): Option<&[u8]> = some else { return }; + | ---- here the type of `some` is inferred to be `Option>` +... LL | let Some(a): Option<&[u8]> = some else { return }; | ------------- ^^^^ expected `&[u8]`, found struct `Vec` | | @@ -59,6 +65,12 @@ LL | let Some(ref mut a): Option<&mut [u8]> = &mut some else { return }; error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:52:38 | +LL | let mut some = Some(bytes); + | ----------- here the type of `some` is inferred to be `Option<_>` +... +LL | let Some(ref mut a): Option<&mut [u8]> = some else { return }; + | ---- here the type of `some` is inferred to be `Option>` +... LL | let Some(a): Option<&mut [u8]> = some else { return }; | ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec` | | diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index 82addab94792a..1d836f2001272 100644 --- a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -10,6 +10,9 @@ LL | #![feature(unsized_locals, unsized_fn_params)] error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:87:24 | +LL | let z = x.foo(); + | ------- here the type of `z` is inferred to be `u32` +... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | @@ -18,6 +21,9 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:104:24 | +LL | let z = x.foo(); + | ------- here the type of `z` is inferred to be `u64` +... LL | let _seetype: () = z; | -- ^ expected `()`, found `u64` | | @@ -60,6 +66,9 @@ LL | let z = FinalFoo::foo(x); error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24 | +LL | let z = x.foo(); + | ------- here the type of `z` is inferred to be `u8` +... LL | let _seetype: () = z; | -- ^ expected `()`, found `u8` | | @@ -68,6 +77,9 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:157:24 | +LL | let z = x.foo(); + | ------- here the type of `z` is inferred to be `u32` +... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | @@ -76,6 +88,9 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:174:24 | +LL | let z = x.foo(); + | ------- here the type of `z` is inferred to be `u32` +... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index ff1a836c9aec0..8fd3239e8ee2c 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -53,11 +53,19 @@ LL | Ok(Foo { bar: 1 }) error[E0308]: mismatched types --> $DIR/abridged.rs:39:5 | -LL | fn d() -> X, String> { - | ---------------------------- expected `X, String>` because of return type -... -LL | x - | ^ expected struct `String`, found integer +LL | fn d() -> X, String> { + | ---------------------------- expected `X, String>` because of return type +LL | let x = X { + | _____________- +LL | | x: X { +LL | | x: "".to_string(), +LL | | y: 2, +LL | | }, +LL | | y: 3, +LL | | }; + | |_____- here the type of `x` is inferred to be `X<_, _>` +LL | x + | ^ expected struct `String`, found integer | = note: expected struct `X, String>` found struct `X, {integer}>` @@ -65,11 +73,19 @@ LL | x error[E0308]: mismatched types --> $DIR/abridged.rs:50:5 | -LL | fn e() -> X, String> { - | ---------------------------- expected `X, String>` because of return type -... -LL | x - | ^ expected struct `String`, found integer +LL | fn e() -> X, String> { + | ---------------------------- expected `X, String>` because of return type +LL | let x = X { + | _____________- +LL | | x: X { +LL | | x: "".to_string(), +LL | | y: 2, +LL | | }, +LL | | y: "".to_string(), +LL | | }; + | |_____- here the type of `x` is inferred to be `X<_, _>` +LL | x + | ^ expected struct `String`, found integer | = note: expected struct `X, _>` found struct `X, _>` diff --git a/src/test/ui/parser/struct-literal-variant-in-if.stderr b/src/test/ui/parser/struct-literal-variant-in-if.stderr index 9f0c0074d674c..97cdd130d0bec 100644 --- a/src/test/ui/parser/struct-literal-variant-in-if.stderr +++ b/src/test/ui/parser/struct-literal-variant-in-if.stderr @@ -51,6 +51,8 @@ LL | if x == E::V { field } {} error[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 | +LL | let field = true; + | ---- here the type of `field` is inferred to be `bool` LL | if x == E::V { field } {} | ---------------^^^^^-- | | | diff --git a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr index 498a112fa9bb3..1785c31cfb948 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr @@ -100,6 +100,12 @@ LL | let Some(n) = opt && let another = n else { error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:9:19 | +LL | let opt = Some(1i32); + | ---------- here the type of `opt` is inferred to be `Option<_>` +LL | +LL | let Some(n) = opt else { + | --- here the type of `opt` is inferred to be `Option` +... LL | let Some(n) = opt && n == 1 else { | ^^^ expected `bool`, found enum `Option` | @@ -120,6 +126,12 @@ LL | let Some(n) = opt && n == 1 else { error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:19 | +LL | let opt = Some(1i32); + | ---------- here the type of `opt` is inferred to be `Option<_>` +LL | +LL | let Some(n) = opt else { + | --- here the type of `opt` is inferred to be `Option` +... LL | let Some(n) = opt && let another = n else { | ^^^ expected `bool`, found enum `Option` | diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index db784d5fe6cfc..75d460d7d8ca9 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -63,7 +63,10 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 | LL | s = format!("foo"); - | ^^^^^^^^^^^^^^ expected `&mut String`, found struct `String` + | ^^^^^^^^^^^^^^ + | | + | expected `&mut String`, found struct `String` + | here the type of `res` is inferred to be `String` | = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr index aee1530851773..30e248f381c67 100644 --- a/src/test/ui/span/issue-33884.stderr +++ b/src/test/ui/span/issue-33884.stderr @@ -2,7 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-33884.rs:6:22 | LL | stream.write_fmt(format!("message received")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Arguments`, found struct `String` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected struct `Arguments`, found struct `String` + | here the type of `res` is inferred to be `String` | = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/structs/struct-base-wrong-type.stderr b/src/test/ui/structs/struct-base-wrong-type.stderr index b039ce2cc9209..30feb9cdd70c1 100644 --- a/src/test/ui/structs/struct-base-wrong-type.stderr +++ b/src/test/ui/structs/struct-base-wrong-type.stderr @@ -13,6 +13,8 @@ LL | static foo_i: Foo = Foo { a: 2, ..4 }; error[E0308]: mismatched types --> $DIR/struct-base-wrong-type.rs:12:27 | +LL | let b = Bar { x: 5 }; + | ------------ here the type of `b` is inferred to be `Bar` LL | let f = Foo { a: 2, ..b }; | ^ expected struct `Foo`, found struct `Bar` diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr index 9b619ac9a3f50..8295e010f400e 100644 --- a/src/test/ui/suggestions/call-boxed.stderr +++ b/src/test/ui/suggestions/call-boxed.stderr @@ -4,7 +4,10 @@ error[E0308]: mismatched types LL | let mut x = 1i32; | ---- expected due to this value LL | let y = Box::new(|| 1); - | -- the found closure + | -------------- + | | | + | | the found closure + | here the type of `y` is inferred to be `Box<_>` LL | x = y; | ^ expected `i32`, found struct `Box` | diff --git a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr index 5dc4e64446fb2..2546f2515d749 100644 --- a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr +++ b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr @@ -4,7 +4,10 @@ error[E0308]: mismatched types LL | / intrinsic_match! { LL | | "abc" LL | | }; - | |_____^ expected `&str`, found struct `String` + | | ^ + | | | + | |_____expected `&str`, found struct `String` + | here the type of `res` is inferred to be `String` | = note: this error originates in the macro `format` which comes from the expansion of the macro `intrinsic_match` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr index 8c9a41a202767..388d8d8d895e3 100644 --- a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr +++ b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr @@ -49,6 +49,8 @@ LL | let _s = S { _s: ("abc".to_string(),) }; error[E0308]: mismatched types --> $DIR/issue-86100-tuple-paren-comma.rs:23:22 | +LL | let t = (1, 2); + | ------ here the type of `t` is inferred to be `({integer}, {integer})` LL | let _x: (i32,) = (t); | ------ ^^^ expected a tuple with 1 element, found one with 2 elements | | diff --git a/src/test/ui/traits/issue-52893.stderr b/src/test/ui/traits/issue-52893.stderr index 7924d3db06f36..f7b5b7fca06e8 100644 --- a/src/test/ui/traits/issue-52893.stderr +++ b/src/test/ui/traits/issue-52893.stderr @@ -4,6 +4,8 @@ error[E0308]: mismatched types LL | impl AddClass for Class

| - this type parameter ... +LL | let output = builder.to_ref(); + | ---------------- here the type of `output` is inferred to be `Class

` LL | builder.push(output); | ---- ^^^^^^ expected type parameter `F`, found struct `Class` | | diff --git a/src/test/ui/tuple/wrong_argument_ice-3.stderr b/src/test/ui/tuple/wrong_argument_ice-3.stderr index 0a503e1fe58c1..c83bc28d8553a 100644 --- a/src/test/ui/tuple/wrong_argument_ice-3.stderr +++ b/src/test/ui/tuple/wrong_argument_ice-3.stderr @@ -1,6 +1,9 @@ error[E0061]: this method takes 1 argument but 2 arguments were supplied --> $DIR/wrong_argument_ice-3.rs:9:16 | +LL | let new_group = vec![String::new()]; + | ------------------- here the type of `new_group` is inferred to be `Vec<_, _>` +... LL | groups.push(new_group, vec![process]); | ^^^^ ------------- argument of type `Vec<&Process>` unexpected | diff --git a/src/test/ui/type/type-check/assignment-in-if.stderr b/src/test/ui/type/type-check/assignment-in-if.stderr index 9f4558adab150..fb11d6160bbaa 100644 --- a/src/test/ui/type/type-check/assignment-in-if.stderr +++ b/src/test/ui/type/type-check/assignment-in-if.stderr @@ -67,6 +67,12 @@ LL | x == 5 error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:44:18 | +LL | let x = 1; + | - here the type of `x` is inferred to be `{integer}` +... +LL | println!("{}", x); + | - here the type of `x` is inferred to be `usize` +... LL | if x == x && x = x && x == x { | ------ ^ expected `bool`, found `usize` | | @@ -75,6 +81,12 @@ LL | if x == x && x = x && x == x { error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:44:22 | +LL | let x = 1; + | - here the type of `x` is inferred to be `{integer}` +... +LL | println!("{}", x); + | - here the type of `x` is inferred to be `usize` +... LL | if x == x && x = x && x == x { | ^ expected `bool`, found `usize` @@ -92,6 +104,12 @@ LL | if x == x && x == x && x == x { error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:51:28 | +LL | let x = 1; + | - here the type of `x` is inferred to be `{integer}` +... +LL | println!("{}", x); + | - here the type of `x` is inferred to be `usize` +... LL | if x == x && x == x && x = x { | ---------------- ^ expected `bool`, found `usize` | | diff --git a/src/test/ui/type/type-mismatch-same-crate-name.stderr b/src/test/ui/type/type-mismatch-same-crate-name.stderr index fcafd315ebf54..e99d30d339631 100644 --- a/src/test/ui/type/type-mismatch-same-crate-name.stderr +++ b/src/test/ui/type/type-mismatch-same-crate-name.stderr @@ -1,6 +1,9 @@ error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:16:20 | +LL | let foo2 = {extern crate crate_a2 as a; a::Foo}; + | ------------------------------------ here the type of `foo2` is inferred to be `_` +... LL | a::try_foo(foo2); | ---------- ^^^^ expected struct `main::a::Foo`, found a different struct `main::a::Foo` | | @@ -27,6 +30,9 @@ LL | pub fn try_foo(x: Foo){} error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:20:20 | +LL | let bar2 = {extern crate crate_a2 as a; a::bar()}; + | -------------------------------------- here the type of `bar2` is inferred to be `_` +... LL | a::try_bar(bar2); | ---------- ^^^^ expected trait `main::a::Bar`, found a different trait `main::a::Bar` | | From 05c30b0ca5450b2beec0a6e4e034953e216f6bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 2 Jan 2023 21:56:19 -0800 Subject: [PATCH 02/12] Skip macros to avoid talking about bindings the user can't see --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- src/test/ui/span/coerce-suggestions.stderr | 5 +---- src/test/ui/span/issue-33884.stderr | 5 +---- .../dont-suggest-deref-inside-macro-issue-58298.stderr | 5 +---- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index c9ee5c6cac8bd..5ba0d41ab9d11 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -260,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { })) = map.find(parent) else { return false; }; let ty = self.node_ty(init.hir_id); - if ty.is_closure() || init.span.overlaps(expr.span) { + if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() { return false; } let mut span_labels = vec![( diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 75d460d7d8ca9..db784d5fe6cfc 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -63,10 +63,7 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:21:9 | LL | s = format!("foo"); - | ^^^^^^^^^^^^^^ - | | - | expected `&mut String`, found struct `String` - | here the type of `res` is inferred to be `String` + | ^^^^^^^^^^^^^^ expected `&mut String`, found struct `String` | = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr index 30e248f381c67..aee1530851773 100644 --- a/src/test/ui/span/issue-33884.stderr +++ b/src/test/ui/span/issue-33884.stderr @@ -2,10 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-33884.rs:6:22 | LL | stream.write_fmt(format!("message received")) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected struct `Arguments`, found struct `String` - | here the type of `res` is inferred to be `String` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Arguments`, found struct `String` | = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr index 2546f2515d749..5dc4e64446fb2 100644 --- a/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr +++ b/src/test/ui/suggestions/dont-suggest-deref-inside-macro-issue-58298.stderr @@ -4,10 +4,7 @@ error[E0308]: mismatched types LL | / intrinsic_match! { LL | | "abc" LL | | }; - | | ^ - | | | - | |_____expected `&str`, found struct `String` - | here the type of `res` is inferred to be `String` + | |_____^ expected `&str`, found struct `String` | = note: this error originates in the macro `format` which comes from the expansion of the macro `intrinsic_match` (in Nightly builds, run with -Z macro-backtrace for more info) From 48094a4a6f53ee3be636b4166a7841505bb7cabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 2 Jan 2023 22:25:50 -0800 Subject: [PATCH 03/12] More eagerly resolve expr `ty`s before writing them This allows the expressions to have more accurate types when showing inference steps. --- compiler/rustc_hir_typeck/src/expr.rs | 1 + src/test/ui/issues/issue-15783.stderr | 2 +- src/test/ui/let-else/let-else-ref-bindings.stderr | 10 ++-------- src/test/ui/mismatched_types/abridged.stderr | 4 ++-- ...t-let-else-does-not-interact-with-let-chains.stderr | 10 ++-------- src/test/ui/suggestions/call-boxed.stderr | 2 +- src/test/ui/tuple/wrong_argument_ice-3.stderr | 2 +- src/test/ui/type/type-mismatch-same-crate-name.stderr | 4 ++-- 8 files changed, 12 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 631749fcc0fa4..6ed1bc051a5fa 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -234,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) => self.check_expr_path(qpath, expr, args), _ => self.check_expr_kind(expr, expected), }); + let ty = self.resolve_vars_if_possible(ty); // Warn for non-block expressions with diverging children. match expr.kind { diff --git a/src/test/ui/issues/issue-15783.stderr b/src/test/ui/issues/issue-15783.stderr index 74a7c5de7abe2..e7fecfebdc8f3 100644 --- a/src/test/ui/issues/issue-15783.stderr +++ b/src/test/ui/issues/issue-15783.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-15783.rs:8:19 | LL | let x = Some(&[name]); - | ------------- here the type of `x` is inferred to be `Option<_>` + | ------------- here the type of `x` is inferred to be `Option<&[&str; 1]>` LL | let msg = foo(x); | --- ^ expected slice `[&str]`, found array `[&str; 1]` | | diff --git a/src/test/ui/let-else/let-else-ref-bindings.stderr b/src/test/ui/let-else/let-else-ref-bindings.stderr index 39b57ceb43d37..4c58b2db575d0 100644 --- a/src/test/ui/let-else/let-else-ref-bindings.stderr +++ b/src/test/ui/let-else/let-else-ref-bindings.stderr @@ -20,10 +20,7 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 | LL | let some = Some(bytes); - | ----------- here the type of `some` is inferred to be `Option<_>` -... -LL | let Some(ref a): Option<&[u8]> = some else { return }; - | ---- here the type of `some` is inferred to be `Option>` + | ----------- here the type of `some` is inferred to be `Option>` ... LL | let Some(a): Option<&[u8]> = some else { return }; | ------------- ^^^^ expected `&[u8]`, found struct `Vec` @@ -66,10 +63,7 @@ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:52:38 | LL | let mut some = Some(bytes); - | ----------- here the type of `some` is inferred to be `Option<_>` -... -LL | let Some(ref mut a): Option<&mut [u8]> = some else { return }; - | ---- here the type of `some` is inferred to be `Option>` + | ----------- here the type of `some` is inferred to be `Option>` ... LL | let Some(a): Option<&mut [u8]> = some else { return }; | ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec` diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index 8fd3239e8ee2c..9b5f0134f1274 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -63,7 +63,7 @@ LL | | y: 2, LL | | }, LL | | y: 3, LL | | }; - | |_____- here the type of `x` is inferred to be `X<_, _>` + | |_____- here the type of `x` is inferred to be `X, {integer}>` LL | x | ^ expected struct `String`, found integer | @@ -83,7 +83,7 @@ LL | | y: 2, LL | | }, LL | | y: "".to_string(), LL | | }; - | |_____- here the type of `x` is inferred to be `X<_, _>` + | |_____- here the type of `x` is inferred to be `X, String>` LL | x | ^ expected struct `String`, found integer | diff --git a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr index 1785c31cfb948..8c30f015c5d84 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr @@ -101,10 +101,7 @@ error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:9:19 | LL | let opt = Some(1i32); - | ---------- here the type of `opt` is inferred to be `Option<_>` -LL | -LL | let Some(n) = opt else { - | --- here the type of `opt` is inferred to be `Option` + | ---------- here the type of `opt` is inferred to be `Option` ... LL | let Some(n) = opt && n == 1 else { | ^^^ expected `bool`, found enum `Option` @@ -127,10 +124,7 @@ error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:19 | LL | let opt = Some(1i32); - | ---------- here the type of `opt` is inferred to be `Option<_>` -LL | -LL | let Some(n) = opt else { - | --- here the type of `opt` is inferred to be `Option` + | ---------- here the type of `opt` is inferred to be `Option` ... LL | let Some(n) = opt && let another = n else { | ^^^ expected `bool`, found enum `Option` diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr index 8295e010f400e..1609c2a3094e1 100644 --- a/src/test/ui/suggestions/call-boxed.stderr +++ b/src/test/ui/suggestions/call-boxed.stderr @@ -7,7 +7,7 @@ LL | let y = Box::new(|| 1); | -------------- | | | | | the found closure - | here the type of `y` is inferred to be `Box<_>` + | here the type of `y` is inferred to be `Box<[closure@call-boxed.rs:3:22]>` LL | x = y; | ^ expected `i32`, found struct `Box` | diff --git a/src/test/ui/tuple/wrong_argument_ice-3.stderr b/src/test/ui/tuple/wrong_argument_ice-3.stderr index c83bc28d8553a..1ad1d69212102 100644 --- a/src/test/ui/tuple/wrong_argument_ice-3.stderr +++ b/src/test/ui/tuple/wrong_argument_ice-3.stderr @@ -2,7 +2,7 @@ error[E0061]: this method takes 1 argument but 2 arguments were supplied --> $DIR/wrong_argument_ice-3.rs:9:16 | LL | let new_group = vec![String::new()]; - | ------------------- here the type of `new_group` is inferred to be `Vec<_, _>` + | ------------------- here the type of `new_group` is inferred to be `Vec` ... LL | groups.push(new_group, vec![process]); | ^^^^ ------------- argument of type `Vec<&Process>` unexpected diff --git a/src/test/ui/type/type-mismatch-same-crate-name.stderr b/src/test/ui/type/type-mismatch-same-crate-name.stderr index e99d30d339631..0d754459f4a50 100644 --- a/src/test/ui/type/type-mismatch-same-crate-name.stderr +++ b/src/test/ui/type/type-mismatch-same-crate-name.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:16:20 | LL | let foo2 = {extern crate crate_a2 as a; a::Foo}; - | ------------------------------------ here the type of `foo2` is inferred to be `_` + | ------------------------------------ here the type of `foo2` is inferred to be `Foo` ... LL | a::try_foo(foo2); | ---------- ^^^^ expected struct `main::a::Foo`, found a different struct `main::a::Foo` @@ -31,7 +31,7 @@ error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:20:20 | LL | let bar2 = {extern crate crate_a2 as a; a::bar()}; - | -------------------------------------- here the type of `bar2` is inferred to be `_` + | -------------------------------------- here the type of `bar2` is inferred to be `Box` ... LL | a::try_bar(bar2); | ---------- ^^^^ expected trait `main::a::Bar`, found a different trait `main::a::Bar` From 6b0cce4b5018bcd1c7aed1f84cb0b86e3cc03f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 2 Jan 2023 23:34:26 -0800 Subject: [PATCH 04/12] review comments: do not always point at init expr --- compiler/rustc_hir_typeck/src/demand.rs | 3 ++ .../two-mismatch-notes.stderr | 2 -- .../dont-suggest-missing-await.stderr | 2 -- .../suggest-missing-await-closure.stderr | 2 -- .../async-await/suggest-missing-await.stderr | 2 -- .../closure-return-type-mismatch.stderr | 2 -- src/test/ui/coercion/coerce-to-bang.stderr | 9 ----- .../collections-project-default.stderr | 3 -- src/test/ui/issues/issue-15783.stderr | 2 -- .../ui/let-else/let-else-ref-bindings.stderr | 6 ---- ...e-trait-object-with-separate-params.stderr | 15 -------- src/test/ui/mismatched_types/abridged.stderr | 36 ++++++------------- .../struct-literal-variant-in-if.stderr | 2 -- ...e-does-not-interact-with-let-chains.stderr | 6 ---- .../ui/structs/struct-base-wrong-type.stderr | 2 -- src/test/ui/suggestions/call-boxed.stderr | 5 +-- .../issue-86100-tuple-paren-comma.stderr | 2 -- src/test/ui/traits/issue-52893.stderr | 2 -- src/test/ui/tuple/wrong_argument_ice-3.stderr | 3 -- .../type/type-mismatch-same-crate-name.stderr | 6 ---- 20 files changed, 14 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 5ba0d41ab9d11..c50d03b944ffd 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -355,6 +355,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + if span_labels.len() < 2 { + return false; + } for (sp, label) in span_labels { err.span_label(sp, &label); } diff --git a/src/test/ui/argument-suggestions/two-mismatch-notes.stderr b/src/test/ui/argument-suggestions/two-mismatch-notes.stderr index 3ccd399863d55..7873cf964cbbc 100644 --- a/src/test/ui/argument-suggestions/two-mismatch-notes.stderr +++ b/src/test/ui/argument-suggestions/two-mismatch-notes.stderr @@ -1,8 +1,6 @@ error[E0308]: arguments to this function are incorrect --> $DIR/two-mismatch-notes.rs:10:5 | -LL | let w = Wrapper::(1isize); - | ------------------------ here the type of `w` is inferred to be `Wrapper` LL | foo(f, w); | ^^^ | diff --git a/src/test/ui/async-await/dont-suggest-missing-await.stderr b/src/test/ui/async-await/dont-suggest-missing-await.stderr index 6e232dd006426..627bf05bba2d9 100644 --- a/src/test/ui/async-await/dont-suggest-missing-await.stderr +++ b/src/test/ui/async-await/dont-suggest-missing-await.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/dont-suggest-missing-await.rs:14:18 | -LL | let x = make_u32(); - | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/async-await/suggest-missing-await-closure.stderr b/src/test/ui/async-await/suggest-missing-await-closure.stderr index 9f51832365b61..a5958baffbaf7 100644 --- a/src/test/ui/async-await/suggest-missing-await-closure.stderr +++ b/src/test/ui/async-await/suggest-missing-await-closure.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/suggest-missing-await-closure.rs:16:18 | -LL | let x = make_u32(); - | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr index ce4c8edaf74d6..1196601ace090 100644 --- a/src/test/ui/async-await/suggest-missing-await.stderr +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/suggest-missing-await.rs:12:14 | -LL | let x = make_u32(); - | ---------- here the type of `x` is inferred to be `impl Future` LL | take_u32(x) | -------- ^ expected `u32`, found opaque type | | diff --git a/src/test/ui/closures/closure-return-type-mismatch.stderr b/src/test/ui/closures/closure-return-type-mismatch.stderr index d33cf383cbcb4..3a89d30a05d20 100644 --- a/src/test/ui/closures/closure-return-type-mismatch.stderr +++ b/src/test/ui/closures/closure-return-type-mismatch.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/closure-return-type-mismatch.rs:7:9 | -LL | let a = true; - | ---- here the type of `a` is inferred to be `bool` LL | a | ^ expected `&str`, found `bool` | diff --git a/src/test/ui/coercion/coerce-to-bang.stderr b/src/test/ui/coercion/coerce-to-bang.stderr index d2fd0f788384e..1207dc7e7a2ff 100644 --- a/src/test/ui/coercion/coerce-to-bang.stderr +++ b/src/test/ui/coercion/coerce-to-bang.stderr @@ -33,9 +33,6 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:26:12 | -LL | let b = 22; - | -- here the type of `b` is inferred to be `{integer}` -LL | let c = 44; LL | foo(a, b, c); // ... and hence a reference to `a` is expected to diverge. | --- ^ expected `!`, found integer | | @@ -52,9 +49,6 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:36:12 | -LL | let b = 22; - | -- here the type of `b` is inferred to be `{integer}` -LL | let c = 44; LL | foo(a, b, c); | --- ^ expected `!`, found integer | | @@ -71,9 +65,6 @@ LL | fn foo(x: usize, y: !, z: usize) { } error[E0308]: mismatched types --> $DIR/coerce-to-bang.rs:45:12 | -LL | let b = 22; - | -- here the type of `b` is inferred to be `{integer}` -LL | let c = 44; LL | foo(a, b, c); | --- ^ expected `!`, found integer | | diff --git a/src/test/ui/generic-associated-types/collections-project-default.stderr b/src/test/ui/generic-associated-types/collections-project-default.stderr index c11a5acc35244..5701017dc3471 100644 --- a/src/test/ui/generic-associated-types/collections-project-default.stderr +++ b/src/test/ui/generic-associated-types/collections-project-default.stderr @@ -4,9 +4,6 @@ error[E0308]: mismatched types LL | fn floatify_sibling(ints: &C) -> >::Sibling | ------------------------------------ expected `>::Sibling` because of return type ... -LL | let mut res = ::Member::::empty(); - | ------------------------------------------------------- here the type of `res` is inferred to be `<>::Family as CollectionFamily>::Member` -... LL | res | ^^^ expected Collection::Sibling, found CollectionFamily::Member | diff --git a/src/test/ui/issues/issue-15783.stderr b/src/test/ui/issues/issue-15783.stderr index e7fecfebdc8f3..660dfe9ed3d51 100644 --- a/src/test/ui/issues/issue-15783.stderr +++ b/src/test/ui/issues/issue-15783.stderr @@ -1,8 +1,6 @@ error[E0308]: mismatched types --> $DIR/issue-15783.rs:8:19 | -LL | let x = Some(&[name]); - | ------------- here the type of `x` is inferred to be `Option<&[&str; 1]>` LL | let msg = foo(x); | --- ^ expected slice `[&str]`, found array `[&str; 1]` | | diff --git a/src/test/ui/let-else/let-else-ref-bindings.stderr b/src/test/ui/let-else/let-else-ref-bindings.stderr index 4c58b2db575d0..56b9e073330a6 100644 --- a/src/test/ui/let-else/let-else-ref-bindings.stderr +++ b/src/test/ui/let-else/let-else-ref-bindings.stderr @@ -19,9 +19,6 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 | -LL | let some = Some(bytes); - | ----------- here the type of `some` is inferred to be `Option>` -... LL | let Some(a): Option<&[u8]> = some else { return }; | ------------- ^^^^ expected `&[u8]`, found struct `Vec` | | @@ -62,9 +59,6 @@ LL | let Some(ref mut a): Option<&mut [u8]> = &mut some else { return }; error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:52:38 | -LL | let mut some = Some(bytes); - | ----------- here the type of `some` is inferred to be `Option>` -... LL | let Some(a): Option<&mut [u8]> = some else { return }; | ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec` | | diff --git a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index 1d836f2001272..82addab94792a 100644 --- a/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/src/test/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -10,9 +10,6 @@ LL | #![feature(unsized_locals, unsized_fn_params)] error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:87:24 | -LL | let z = x.foo(); - | ------- here the type of `z` is inferred to be `u32` -... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | @@ -21,9 +18,6 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:104:24 | -LL | let z = x.foo(); - | ------- here the type of `z` is inferred to be `u64` -... LL | let _seetype: () = z; | -- ^ expected `()`, found `u64` | | @@ -66,9 +60,6 @@ LL | let z = FinalFoo::foo(x); error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24 | -LL | let z = x.foo(); - | ------- here the type of `z` is inferred to be `u8` -... LL | let _seetype: () = z; | -- ^ expected `()`, found `u8` | | @@ -77,9 +68,6 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:157:24 | -LL | let z = x.foo(); - | ------- here the type of `z` is inferred to be `u32` -... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | @@ -88,9 +76,6 @@ LL | let _seetype: () = z; error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:174:24 | -LL | let z = x.foo(); - | ------- here the type of `z` is inferred to be `u32` -... LL | let _seetype: () = z; | -- ^ expected `()`, found `u32` | | diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr index 9b5f0134f1274..ff1a836c9aec0 100644 --- a/src/test/ui/mismatched_types/abridged.stderr +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -53,19 +53,11 @@ LL | Ok(Foo { bar: 1 }) error[E0308]: mismatched types --> $DIR/abridged.rs:39:5 | -LL | fn d() -> X, String> { - | ---------------------------- expected `X, String>` because of return type -LL | let x = X { - | _____________- -LL | | x: X { -LL | | x: "".to_string(), -LL | | y: 2, -LL | | }, -LL | | y: 3, -LL | | }; - | |_____- here the type of `x` is inferred to be `X, {integer}>` -LL | x - | ^ expected struct `String`, found integer +LL | fn d() -> X, String> { + | ---------------------------- expected `X, String>` because of return type +... +LL | x + | ^ expected struct `String`, found integer | = note: expected struct `X, String>` found struct `X, {integer}>` @@ -73,19 +65,11 @@ LL | x error[E0308]: mismatched types --> $DIR/abridged.rs:50:5 | -LL | fn e() -> X, String> { - | ---------------------------- expected `X, String>` because of return type -LL | let x = X { - | _____________- -LL | | x: X { -LL | | x: "".to_string(), -LL | | y: 2, -LL | | }, -LL | | y: "".to_string(), -LL | | }; - | |_____- here the type of `x` is inferred to be `X, String>` -LL | x - | ^ expected struct `String`, found integer +LL | fn e() -> X, String> { + | ---------------------------- expected `X, String>` because of return type +... +LL | x + | ^ expected struct `String`, found integer | = note: expected struct `X, _>` found struct `X, _>` diff --git a/src/test/ui/parser/struct-literal-variant-in-if.stderr b/src/test/ui/parser/struct-literal-variant-in-if.stderr index 97cdd130d0bec..9f0c0074d674c 100644 --- a/src/test/ui/parser/struct-literal-variant-in-if.stderr +++ b/src/test/ui/parser/struct-literal-variant-in-if.stderr @@ -51,8 +51,6 @@ LL | if x == E::V { field } {} error[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 | -LL | let field = true; - | ---- here the type of `field` is inferred to be `bool` LL | if x == E::V { field } {} | ---------------^^^^^-- | | | diff --git a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr index 8c30f015c5d84..498a112fa9bb3 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr @@ -100,9 +100,6 @@ LL | let Some(n) = opt && let another = n else { error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:9:19 | -LL | let opt = Some(1i32); - | ---------- here the type of `opt` is inferred to be `Option` -... LL | let Some(n) = opt && n == 1 else { | ^^^ expected `bool`, found enum `Option` | @@ -123,9 +120,6 @@ LL | let Some(n) = opt && n == 1 else { error[E0308]: mismatched types --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:19 | -LL | let opt = Some(1i32); - | ---------- here the type of `opt` is inferred to be `Option` -... LL | let Some(n) = opt && let another = n else { | ^^^ expected `bool`, found enum `Option` | diff --git a/src/test/ui/structs/struct-base-wrong-type.stderr b/src/test/ui/structs/struct-base-wrong-type.stderr index 30feb9cdd70c1..b039ce2cc9209 100644 --- a/src/test/ui/structs/struct-base-wrong-type.stderr +++ b/src/test/ui/structs/struct-base-wrong-type.stderr @@ -13,8 +13,6 @@ LL | static foo_i: Foo = Foo { a: 2, ..4 }; error[E0308]: mismatched types --> $DIR/struct-base-wrong-type.rs:12:27 | -LL | let b = Bar { x: 5 }; - | ------------ here the type of `b` is inferred to be `Bar` LL | let f = Foo { a: 2, ..b }; | ^ expected struct `Foo`, found struct `Bar` diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr index 1609c2a3094e1..9b619ac9a3f50 100644 --- a/src/test/ui/suggestions/call-boxed.stderr +++ b/src/test/ui/suggestions/call-boxed.stderr @@ -4,10 +4,7 @@ error[E0308]: mismatched types LL | let mut x = 1i32; | ---- expected due to this value LL | let y = Box::new(|| 1); - | -------------- - | | | - | | the found closure - | here the type of `y` is inferred to be `Box<[closure@call-boxed.rs:3:22]>` + | -- the found closure LL | x = y; | ^ expected `i32`, found struct `Box` | diff --git a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr index 388d8d8d895e3..8c9a41a202767 100644 --- a/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr +++ b/src/test/ui/suggestions/issue-86100-tuple-paren-comma.stderr @@ -49,8 +49,6 @@ LL | let _s = S { _s: ("abc".to_string(),) }; error[E0308]: mismatched types --> $DIR/issue-86100-tuple-paren-comma.rs:23:22 | -LL | let t = (1, 2); - | ------ here the type of `t` is inferred to be `({integer}, {integer})` LL | let _x: (i32,) = (t); | ------ ^^^ expected a tuple with 1 element, found one with 2 elements | | diff --git a/src/test/ui/traits/issue-52893.stderr b/src/test/ui/traits/issue-52893.stderr index f7b5b7fca06e8..7924d3db06f36 100644 --- a/src/test/ui/traits/issue-52893.stderr +++ b/src/test/ui/traits/issue-52893.stderr @@ -4,8 +4,6 @@ error[E0308]: mismatched types LL | impl AddClass for Class

| - this type parameter ... -LL | let output = builder.to_ref(); - | ---------------- here the type of `output` is inferred to be `Class

` LL | builder.push(output); | ---- ^^^^^^ expected type parameter `F`, found struct `Class` | | diff --git a/src/test/ui/tuple/wrong_argument_ice-3.stderr b/src/test/ui/tuple/wrong_argument_ice-3.stderr index 1ad1d69212102..0a503e1fe58c1 100644 --- a/src/test/ui/tuple/wrong_argument_ice-3.stderr +++ b/src/test/ui/tuple/wrong_argument_ice-3.stderr @@ -1,9 +1,6 @@ error[E0061]: this method takes 1 argument but 2 arguments were supplied --> $DIR/wrong_argument_ice-3.rs:9:16 | -LL | let new_group = vec![String::new()]; - | ------------------- here the type of `new_group` is inferred to be `Vec` -... LL | groups.push(new_group, vec![process]); | ^^^^ ------------- argument of type `Vec<&Process>` unexpected | diff --git a/src/test/ui/type/type-mismatch-same-crate-name.stderr b/src/test/ui/type/type-mismatch-same-crate-name.stderr index 0d754459f4a50..fcafd315ebf54 100644 --- a/src/test/ui/type/type-mismatch-same-crate-name.stderr +++ b/src/test/ui/type/type-mismatch-same-crate-name.stderr @@ -1,9 +1,6 @@ error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:16:20 | -LL | let foo2 = {extern crate crate_a2 as a; a::Foo}; - | ------------------------------------ here the type of `foo2` is inferred to be `Foo` -... LL | a::try_foo(foo2); | ---------- ^^^^ expected struct `main::a::Foo`, found a different struct `main::a::Foo` | | @@ -30,9 +27,6 @@ LL | pub fn try_foo(x: Foo){} error[E0308]: mismatched types --> $DIR/type-mismatch-same-crate-name.rs:20:20 | -LL | let bar2 = {extern crate crate_a2 as a; a::bar()}; - | -------------------------------------- here the type of `bar2` is inferred to be `Box` -... LL | a::try_bar(bar2); | ---------- ^^^^ expected trait `main::a::Bar`, found a different trait `main::a::Bar` | | From 9cc8d8619062278046e678bbc08401b733a17236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 3 Jan 2023 20:20:31 -0800 Subject: [PATCH 05/12] Tweak output - Only point at a the single expression where the found type was first inferred. - Find method call argument that might have caused the found type to be inferred. - Provide structured suggestion. - Apply some review comments. - Tweak wording. --- compiler/rustc_hir_typeck/src/demand.rs | 165 +++++++++++++----- .../type/type-check/assignment-in-if.stderr | 15 +- .../type/type-check/point-at-inference-2.rs | 13 ++ .../type-check/point-at-inference-2.stderr | 56 ++++++ .../type/type-check/point-at-inference.fixed | 13 ++ .../ui/type/type-check/point-at-inference.rs | 13 ++ .../type/type-check/point-at-inference.stderr | 26 +++ 7 files changed, 242 insertions(+), 59 deletions(-) create mode 100644 src/test/ui/type/type-check/point-at-inference-2.rs create mode 100644 src/test/ui/type/type-check/point-at-inference-2.stderr create mode 100644 src/test/ui/type/type-check/point-at-inference.fixed create mode 100644 src/test/ui/type/type-check/point-at-inference.rs create mode 100644 src/test/ui/type/type-check/point-at-inference.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index c50d03b944ffd..317e9b5a7a1ec 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,5 +1,6 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; @@ -14,12 +15,14 @@ use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; +use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::{ self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable, }; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches; use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -44,7 +47,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_alternative_method_deref(err, expr, error); // Use `||` to give these suggestions a precedence - let _ = self.suggest_missing_parentheses(err, expr) + let suggested = self.suggest_missing_parentheses(err, expr) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) @@ -57,8 +60,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) - || self.suggest_floating_point_literal(err, expr, expected) - || self.point_inference_types(err, expr); + || self.suggest_floating_point_literal(err, expr, expected); + if !suggested { + self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected); + } } pub fn emit_coerce_suggestions( @@ -210,7 +215,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } - fn point_inference_types(&self, err: &mut Diagnostic, expr: &hir::Expr<'_>) -> bool { + fn point_at_expr_source_of_inferred_type( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { let tcx = self.tcx; let map = self.tcx.hir(); @@ -250,25 +261,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; let hir::def::Res::Local(hir_id) = p.res else { return false; }; - let Some(node) = map.find(hir_id) else { return false; }; - let hir::Node::Pat(pat) = node else { return false; }; + let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; }; let parent = map.get_parent_node(pat.hir_id); let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = map.find(parent) else { return false; }; - - let ty = self.node_ty(init.hir_id); + let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; }; if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() { return false; } - let mut span_labels = vec![( - init.span, - with_forced_trimmed_paths!(format!( - "here the type of `{ident}` is inferred to be `{ty}`", - )), - )]; // Locate all the usages of the relevant binding. struct FindExprs<'hir> { @@ -296,71 +299,139 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_finder.visit_expr(body.value); let mut eraser = TypeEraser { tcx }; let mut prev = eraser.fold_ty(ty); + let mut prev_span = None; - for ex in expr_finder.uses { - if ex.span.overlaps(expr.span) { break; } - let parent = map.get_parent_node(ex.hir_id); + for binding in expr_finder.uses { + // In every expression where the binding is referenced, we will look at that + // expression's type and see if it is where the incorrect found type was fully + // "materialized" and point at it. We will also try to provide a suggestion there. + let parent = map.get_parent_node(binding.hir_id); if let Some(hir::Node::Expr(expr)) | Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), .. })) = &map.find(parent) - && let hir::ExprKind::MethodCall(s, rcvr, args, span) = expr.kind - && rcvr.hir_id == ex.hir_id + && let hir::ExprKind::MethodCall(s, rcvr, args, _span) = expr.kind + && rcvr.hir_id == binding.hir_id + && let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) { - let ty = if let Ok(m) = self.lookup_method(ty, s, span, expr, rcvr, args) { - // We get the self type from `lookup_method` because the `rcvr` node - // type will not have had any adjustments from the fn arguments. - let ty = m.sig.inputs_and_output[0]; - match ty.kind() { - // Remove one layer of references to account for `&mut self` and - // `&self`, so that we can compare it against the binding. - ty::Ref(_, ty, _) => *ty, - _ => ty, + // We special case methods, because they can influence inference through the + // call's arguments and we can provide a more explicit span. + let sig = self.tcx.fn_sig(def_id); + let def_self_ty = sig.input(0).skip_binder(); + let rcvr_ty = self.node_ty(rcvr.hir_id); + // Get the evaluated type *after* calling the method call, so that the influence + // of the arguments can be reflected in the receiver type. The receiver + // expression has the type *before* theis analysis is done. + let ty = match self.lookup_probe(s.ident, rcvr_ty, expr, probe::ProbeScope::TraitsInScope) { + Ok(pick) => pick.self_ty, + Err(_) => rcvr_ty, + }; + // Remove one layer of references to account for `&mut self` and + // `&self`, so that we can compare it against the binding. + let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { + (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), + _ => (ty, def_self_ty), + }; + let mut param_args = FxHashMap::default(); + let mut param_expected = FxHashMap::default(); + let mut param_found = FxHashMap::default(); + if self.can_eq(self.param_env, ty, found).is_ok() { + // We only point at the first place where the found type was inferred. + for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() { + if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() { + // We found an argument that references a type parameter in `Self`, + // so we assume that this is the argument that caused the found + // type, which we know already because of `can_eq` above was first + // inferred in this method call. + let arg = &args[i]; + let arg_ty = self.node_ty(arg.hir_id); + err.span_label( + arg.span, + &format!( + "this is of type `{arg_ty}`, which makes `{ident}` to be \ + inferred as `{ty}`", + ), + ); + param_args.insert(param_ty, (arg, arg_ty)); + } } - } else { - self.node_ty(rcvr.hir_id) + } + + // Here we find, for a type param `T`, the type that `T` is in the current + // method call *and* in the original expected type. That way, we can see if we + // can give any structured suggestion for the function argument. + let mut c = CollectAllMismatches { + infcx: &self.infcx, + param_env: self.param_env, + errors: vec![], }; + let _ = c.relate(def_self_ty, ty); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_found.insert(error.expected, error.found); + } + } + c.errors = vec![]; + let _ = c.relate(def_self_ty, expected); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_expected.insert(error.expected, error.found); + } + } + for (param, (arg,arg_ty)) in param_args.iter() { + let Some(expected) = param_expected.get(param) else { continue; }; + let Some(found) = param_found.get(param) else { continue; }; + if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; } + self.suggest_deref_ref_or_into(err, arg, *expected, *found, None); + } + let ty = eraser.fold_ty(ty); if ty.references_error() { break; } - if ty != prev { - span_labels.push(( + if ty != prev + && param_args.is_empty() + && self.can_eq(self.param_env, ty, found).is_ok() + { + // We only point at the first place where the found type was inferred. + err.span_label( s.ident.span, with_forced_trimmed_paths!(format!( "here the type of `{ident}` is inferred to be `{ty}`", )), - )); - prev = ty; + ); + break; } + prev = ty; } else { - let ty = eraser.fold_ty(self.node_ty(ex.hir_id)); + let ty = eraser.fold_ty(self.node_ty(binding.hir_id)); if ty.references_error() { break; } - if ty != prev { - span_labels.push(( - ex.span, + if ty != prev && let Some(span) = prev_span && self.can_eq(self.param_env, ty, found).is_ok() { + // We only point at the first place where the found type was inferred. + // We use the *previous* span because if the type is known *here* it means + // it was *evaluated earlier*. We don't do this for method calls because we + // evaluate the method's self type eagerly, but not in any other case. + err.span_label( + span, with_forced_trimmed_paths!(format!( "here the type of `{ident}` is inferred to be `{ty}`", )), - )); + ); + break; } prev = ty; } - if ex.hir_id == expr.hir_id { - // Stop showing spans after the error type was emitted. + if binding.hir_id == expr.hir_id { + // Do not look at expressions that come after the expression we were originally + // evaluating and had a type error. break; } + prev_span = Some(binding.span); } } - if span_labels.len() < 2 { - return false; - } - for (sp, label) in span_labels { - err.span_label(sp, &label); - } true } diff --git a/src/test/ui/type/type-check/assignment-in-if.stderr b/src/test/ui/type/type-check/assignment-in-if.stderr index fb11d6160bbaa..de133e5599cf9 100644 --- a/src/test/ui/type/type-check/assignment-in-if.stderr +++ b/src/test/ui/type/type-check/assignment-in-if.stderr @@ -67,10 +67,7 @@ LL | x == 5 error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:44:18 | -LL | let x = 1; - | - here the type of `x` is inferred to be `{integer}` -... -LL | println!("{}", x); +LL | if y = (Foo { foo: x }) { | - here the type of `x` is inferred to be `usize` ... LL | if x == x && x = x && x == x { @@ -81,10 +78,7 @@ LL | if x == x && x = x && x == x { error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:44:22 | -LL | let x = 1; - | - here the type of `x` is inferred to be `{integer}` -... -LL | println!("{}", x); +LL | if y = (Foo { foo: x }) { | - here the type of `x` is inferred to be `usize` ... LL | if x == x && x = x && x == x { @@ -104,10 +98,7 @@ LL | if x == x && x == x && x == x { error[E0308]: mismatched types --> $DIR/assignment-in-if.rs:51:28 | -LL | let x = 1; - | - here the type of `x` is inferred to be `{integer}` -... -LL | println!("{}", x); +LL | if y = (Foo { foo: x }) { | - here the type of `x` is inferred to be `usize` ... LL | if x == x && x == x && x = x { diff --git a/src/test/ui/type/type-check/point-at-inference-2.rs b/src/test/ui/type/type-check/point-at-inference-2.rs new file mode 100644 index 0000000000000..6557d7fa1911a --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference-2.rs @@ -0,0 +1,13 @@ +fn bar(_: Vec) {} +fn baz(_: &Vec<&i32>) {} +fn main() { + let v = vec![&1]; + bar(v); //~ ERROR E0308 + let v = vec![]; + baz(&v); + baz(&v); + bar(v); //~ ERROR E0308 + let v = vec![]; + baz(&v); + bar(v); //~ ERROR E0308 +} diff --git a/src/test/ui/type/type-check/point-at-inference-2.stderr b/src/test/ui/type/type-check/point-at-inference-2.stderr new file mode 100644 index 0000000000000..13227c5e245bd --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference-2.stderr @@ -0,0 +1,56 @@ +error[E0308]: mismatched types + --> $DIR/point-at-inference-2.rs:5:9 + | +LL | bar(v); + | --- ^ expected `i32`, found `&{integer}` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec<&{integer}>` +note: function defined here + --> $DIR/point-at-inference-2.rs:1:4 + | +LL | fn bar(_: Vec) {} + | ^^^ ----------- + +error[E0308]: mismatched types + --> $DIR/point-at-inference-2.rs:9:9 + | +LL | baz(&v); + | - here the type of `v` is inferred to be `Vec<&i32>` +LL | baz(&v); +LL | bar(v); + | --- ^ expected `i32`, found `&i32` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec<&i32>` +note: function defined here + --> $DIR/point-at-inference-2.rs:1:4 + | +LL | fn bar(_: Vec) {} + | ^^^ ----------- + +error[E0308]: mismatched types + --> $DIR/point-at-inference-2.rs:12:9 + | +LL | baz(&v); + | - here the type of `v` is inferred to be `Vec<&i32>` +LL | bar(v); + | --- ^ expected `i32`, found `&i32` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec<&i32>` +note: function defined here + --> $DIR/point-at-inference-2.rs:1:4 + | +LL | fn bar(_: Vec) {} + | ^^^ ----------- + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type/type-check/point-at-inference.fixed b/src/test/ui/type/type-check/point-at-inference.fixed new file mode 100644 index 0000000000000..f41fbe59fba6c --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference.fixed @@ -0,0 +1,13 @@ +// run-rustfix +fn bar(_: Vec) {} +fn baz(_: &impl std::any::Any) {} +fn main() { + let v = vec![1, 2, 3, 4, 5]; + let mut foo = vec![]; + baz(&foo); + for i in &v { + foo.push(*i); + } + baz(&foo); + bar(foo); //~ ERROR E0308 +} diff --git a/src/test/ui/type/type-check/point-at-inference.rs b/src/test/ui/type/type-check/point-at-inference.rs new file mode 100644 index 0000000000000..6419e42e70d12 --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference.rs @@ -0,0 +1,13 @@ +// run-rustfix +fn bar(_: Vec) {} +fn baz(_: &impl std::any::Any) {} +fn main() { + let v = vec![1, 2, 3, 4, 5]; + let mut foo = vec![]; + baz(&foo); + for i in &v { + foo.push(i); + } + baz(&foo); + bar(foo); //~ ERROR E0308 +} diff --git a/src/test/ui/type/type-check/point-at-inference.stderr b/src/test/ui/type/type-check/point-at-inference.stderr new file mode 100644 index 0000000000000..197511bf64ef2 --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference.stderr @@ -0,0 +1,26 @@ +error[E0308]: mismatched types + --> $DIR/point-at-inference.rs:12:9 + | +LL | foo.push(i); + | - this is of type `&{integer}`, which makes `foo` to be inferred as `Vec<&{integer}>` +... +LL | bar(foo); + | --- ^^^ expected `i32`, found `&{integer}` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec<&{integer}>` +note: function defined here + --> $DIR/point-at-inference.rs:2:4 + | +LL | fn bar(_: Vec) {} + | ^^^ ----------- +help: consider dereferencing the borrow + | +LL | foo.push(*i); + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 4ac7d1c3ab519c72bf10521f0675060b12b0bb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 3 Jan 2023 20:32:15 -0800 Subject: [PATCH 06/12] Formatting --- compiler/rustc_hir_typeck/src/demand.rs | 262 ++++++++++++------------ 1 file changed, 135 insertions(+), 127 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 317e9b5a7a1ec..41bbe2d15c16f 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -294,143 +294,151 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let id = map.get_parent_item(hir_id); let hir_id: hir::HirId = id.into(); - if let Some(node) = map.find(hir_id) && let Some(body_id) = node.body_id() { - let body = map.body(body_id); - expr_finder.visit_expr(body.value); - let mut eraser = TypeEraser { tcx }; - let mut prev = eraser.fold_ty(ty); - let mut prev_span = None; - - for binding in expr_finder.uses { - // In every expression where the binding is referenced, we will look at that - // expression's type and see if it is where the incorrect found type was fully - // "materialized" and point at it. We will also try to provide a suggestion there. - let parent = map.get_parent_node(binding.hir_id); - if let Some(hir::Node::Expr(expr)) - | Some(hir::Node::Stmt(hir::Stmt { - kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), - .. - })) = &map.find(parent) - && let hir::ExprKind::MethodCall(s, rcvr, args, _span) = expr.kind - && rcvr.hir_id == binding.hir_id - && let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) - { - // We special case methods, because they can influence inference through the - // call's arguments and we can provide a more explicit span. - let sig = self.tcx.fn_sig(def_id); - let def_self_ty = sig.input(0).skip_binder(); - let rcvr_ty = self.node_ty(rcvr.hir_id); - // Get the evaluated type *after* calling the method call, so that the influence - // of the arguments can be reflected in the receiver type. The receiver - // expression has the type *before* theis analysis is done. - let ty = match self.lookup_probe(s.ident, rcvr_ty, expr, probe::ProbeScope::TraitsInScope) { - Ok(pick) => pick.self_ty, - Err(_) => rcvr_ty, - }; - // Remove one layer of references to account for `&mut self` and - // `&self`, so that we can compare it against the binding. - let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { - (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), - _ => (ty, def_self_ty), - }; - let mut param_args = FxHashMap::default(); - let mut param_expected = FxHashMap::default(); - let mut param_found = FxHashMap::default(); - if self.can_eq(self.param_env, ty, found).is_ok() { - // We only point at the first place where the found type was inferred. - for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() { - if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() { - // We found an argument that references a type parameter in `Self`, - // so we assume that this is the argument that caused the found - // type, which we know already because of `can_eq` above was first - // inferred in this method call. - let arg = &args[i]; - let arg_ty = self.node_ty(arg.hir_id); - err.span_label( - arg.span, - &format!( - "this is of type `{arg_ty}`, which makes `{ident}` to be \ - inferred as `{ty}`", - ), - ); - param_args.insert(param_ty, (arg, arg_ty)); - } + let Some(node) = map.find(hir_id) else { return false; }; + let Some(body_id) = node.body_id() else { return false; }; + let body = map.body(body_id); + expr_finder.visit_expr(body.value); + let mut eraser = TypeEraser { tcx }; + let mut prev = eraser.fold_ty(ty); + let mut prev_span = None; + + for binding in expr_finder.uses { + // In every expression where the binding is referenced, we will look at that + // expression's type and see if it is where the incorrect found type was fully + // "materialized" and point at it. We will also try to provide a suggestion there. + let parent = map.get_parent_node(binding.hir_id); + if let Some(hir::Node::Expr(expr)) + | Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), + .. + })) = &map.find(parent) + && let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind + && rcvr.hir_id == binding.hir_id + && let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) + { + // We special case methods, because they can influence inference through the + // call's arguments and we can provide a more explicit span. + let sig = self.tcx.fn_sig(def_id); + let def_self_ty = sig.input(0).skip_binder(); + let rcvr_ty = self.node_ty(rcvr.hir_id); + // Get the evaluated type *after* calling the method call, so that the influence + // of the arguments can be reflected in the receiver type. The receiver + // expression has the type *before* theis analysis is done. + let ty = match self.lookup_probe( + segment.ident, + rcvr_ty, + expr, + probe::ProbeScope::TraitsInScope, + ) { + Ok(pick) => pick.self_ty, + Err(_) => rcvr_ty, + }; + // Remove one layer of references to account for `&mut self` and + // `&self`, so that we can compare it against the binding. + let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { + (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), + _ => (ty, def_self_ty), + }; + let mut param_args = FxHashMap::default(); + let mut param_expected = FxHashMap::default(); + let mut param_found = FxHashMap::default(); + if self.can_eq(self.param_env, ty, found).is_ok() { + // We only point at the first place where the found type was inferred. + for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() { + if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() { + // We found an argument that references a type parameter in `Self`, + // so we assume that this is the argument that caused the found + // type, which we know already because of `can_eq` above was first + // inferred in this method call. + let arg = &args[i]; + let arg_ty = self.node_ty(arg.hir_id); + err.span_label( + arg.span, + &format!( + "this is of type `{arg_ty}`, which makes `{ident}` to be \ + inferred as `{ty}`", + ), + ); + param_args.insert(param_ty, (arg, arg_ty)); } } + } - // Here we find, for a type param `T`, the type that `T` is in the current - // method call *and* in the original expected type. That way, we can see if we - // can give any structured suggestion for the function argument. - let mut c = CollectAllMismatches { - infcx: &self.infcx, - param_env: self.param_env, - errors: vec![], - }; - let _ = c.relate(def_self_ty, ty); - for error in c.errors { - if let TypeError::Sorts(error) = error { - param_found.insert(error.expected, error.found); - } - } - c.errors = vec![]; - let _ = c.relate(def_self_ty, expected); - for error in c.errors { - if let TypeError::Sorts(error) = error { - param_expected.insert(error.expected, error.found); - } + // Here we find, for a type param `T`, the type that `T` is in the current + // method call *and* in the original expected type. That way, we can see if we + // can give any structured suggestion for the function argument. + let mut c = CollectAllMismatches { + infcx: &self.infcx, + param_env: self.param_env, + errors: vec![], + }; + let _ = c.relate(def_self_ty, ty); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_found.insert(error.expected, error.found); } - for (param, (arg,arg_ty)) in param_args.iter() { - let Some(expected) = param_expected.get(param) else { continue; }; - let Some(found) = param_found.get(param) else { continue; }; - if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; } - self.suggest_deref_ref_or_into(err, arg, *expected, *found, None); + } + c.errors = vec![]; + let _ = c.relate(def_self_ty, expected); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_expected.insert(error.expected, error.found); } + } + for (param, (arg,arg_ty)) in param_args.iter() { + let Some(expected) = param_expected.get(param) else { continue; }; + let Some(found) = param_found.get(param) else { continue; }; + if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; } + self.suggest_deref_ref_or_into(err, arg, *expected, *found, None); + } - let ty = eraser.fold_ty(ty); - if ty.references_error() { - break; - } - if ty != prev - && param_args.is_empty() - && self.can_eq(self.param_env, ty, found).is_ok() - { - // We only point at the first place where the found type was inferred. - err.span_label( - s.ident.span, - with_forced_trimmed_paths!(format!( - "here the type of `{ident}` is inferred to be `{ty}`", - )), - ); - break; - } - prev = ty; - } else { - let ty = eraser.fold_ty(self.node_ty(binding.hir_id)); - if ty.references_error() { - break; - } - if ty != prev && let Some(span) = prev_span && self.can_eq(self.param_env, ty, found).is_ok() { - // We only point at the first place where the found type was inferred. - // We use the *previous* span because if the type is known *here* it means - // it was *evaluated earlier*. We don't do this for method calls because we - // evaluate the method's self type eagerly, but not in any other case. - err.span_label( - span, - with_forced_trimmed_paths!(format!( - "here the type of `{ident}` is inferred to be `{ty}`", - )), - ); - break; - } - prev = ty; + let ty = eraser.fold_ty(ty); + if ty.references_error() { + break; } - if binding.hir_id == expr.hir_id { - // Do not look at expressions that come after the expression we were originally - // evaluating and had a type error. + if ty != prev + && param_args.is_empty() + && self.can_eq(self.param_env, ty, found).is_ok() + { + // We only point at the first place where the found type was inferred. + err.span_label( + segment.ident.span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + ); break; } - prev_span = Some(binding.span); + prev = ty; + } else { + let ty = eraser.fold_ty(self.node_ty(binding.hir_id)); + if ty.references_error() { + break; + } + if ty != prev + && let Some(span) = prev_span + && self.can_eq(self.param_env, ty, found).is_ok() + { + // We only point at the first place where the found type was inferred. + // We use the *previous* span because if the type is known *here* it means + // it was *evaluated earlier*. We don't do this for method calls because we + // evaluate the method's self type eagerly, but not in any other case. + err.span_label( + span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + ); + break; + } + prev = ty; + } + if binding.hir_id == expr.hir_id { + // Do not look at expressions that come after the expression we were originally + // evaluating and had a type error. + break; } + prev_span = Some(binding.span); } true } From ad82eedfa190b3cd39a535a6e858d79f69c89ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 3 Jan 2023 20:49:47 -0800 Subject: [PATCH 07/12] Use `BottomUpFolder` --- compiler/rustc_hir_typeck/src/demand.rs | 57 ++++++------------- .../rustc_infer/src/infer/opaque_types.rs | 2 +- 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 41bbe2d15c16f..abff263751916 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -13,12 +13,10 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder}; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::{ - self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable, -}; +use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -222,42 +220,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, expected: Ty<'tcx>, ) -> bool { - let tcx = self.tcx; let map = self.tcx.hir(); - // Hack to make equality checks on types with inference variables and regions useful. - struct TypeEraser<'tcx> { - tcx: TyCtxt<'tcx>, - } - impl<'tcx> TypeFolder<'tcx> for TypeEraser<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_region(&mut self, _r: ty::Region<'tcx>) -> ty::Region<'tcx> { - self.tcx().lifetimes.re_erased - } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() && !t.has_erasable_regions() { - return t; - } - match *t.kind() { - ty::Infer(ty::TyVar(_) | ty::FreshTy(_)) => { - self.tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))) - } - ty::Infer(ty::IntVar(_) | ty::FreshIntTy(_)) => { - self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) - } - ty::Infer(ty::FloatVar(_) | ty::FreshFloatTy(_)) => { - self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 })) - } - _ => t.super_fold_with(self), - } - } - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - ct.super_fold_with(self) - } - } - let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; let hir::def::Res::Local(hir_id) = p.res else { return false; }; @@ -298,7 +262,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(body_id) = node.body_id() else { return false; }; let body = map.body(body_id); expr_finder.visit_expr(body.value); - let mut eraser = TypeEraser { tcx }; + // Hack to make equality checks on types with inference variables and regions useful. + let mut eraser = BottomUpFolder { + tcx: self.tcx, + lt_op: |_| self.tcx.lifetimes.re_erased, + ct_op: |c| c, + ty_op: |t| match *t.kind() { + ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))), + ty::Infer(ty::IntVar(_)) => { + self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) + } + ty::Infer(ty::FloatVar(_)) => { + self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 })) + } + _ => t, + }, + }; let mut prev = eraser.fold_ty(ty); let mut prev_span = None; diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index a130fde47ed5c..749e960bfd030 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -61,7 +61,7 @@ impl<'tcx> InferCtxt<'tcx> { .as_local() .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some()) }; - let value = value.fold_with(&mut ty::fold::BottomUpFolder { + let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, ct_op: |ct| ct, From c905f5e1b0315f9637b2f7e52c807e46b2e24ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 00:18:57 +0000 Subject: [PATCH 08/12] Account for type error on method arg caused by earlier inference ```rust fn main() { let v = Vec::new(); v.push(0); v.push(0); v.push(""); } ``` now produces ``` error[E0308]: mismatched types --> $DIR/point-at-inference-3.rs:6:12 | LL | v.push(0); | - this is of type `{integer}`, which makes `v` to be inferred as `Vec<{integer}>` ... LL | v.push(""); | ---- ^^ expected integer, found `&str` | | | arguments to this function are incorrect | note: associated function defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ``` --- compiler/rustc_hir_typeck/src/demand.rs | 4 +++- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 12 ++++++++++++ .../ui/type/type-check/point-at-inference-3.rs | 10 ++++++++++ .../type/type-check/point-at-inference-3.stderr | 17 +++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/type/type-check/point-at-inference-3.rs create mode 100644 src/test/ui/type/type-check/point-at-inference-3.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index abff263751916..cff74cd9693dc 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -213,7 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } - fn point_at_expr_source_of_inferred_type( + pub fn point_at_expr_source_of_inferred_type( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, @@ -387,6 +387,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )), ); break; + } else if !param_args.is_empty() { + break; } prev = ty; } else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 55280487adda4..7d6b4aaebf4ea 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -798,6 +798,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { full_call_span, format!("arguments to this {} are incorrect", call_name), ); + if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) = + (callee_ty, &call_expr.kind) + { + // Type that would have accepted this argument if it hadn't been inferred earlier. + // FIXME: We leave an inference variable for now, but it'd be nice to get a more + // specific type to increase the accuracy of the diagnostic. + let expected = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: full_call_span, + }); + self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty); + } // Call out where the function is defined self.label_fn_like( &mut err, diff --git a/src/test/ui/type/type-check/point-at-inference-3.rs b/src/test/ui/type/type-check/point-at-inference-3.rs new file mode 100644 index 0000000000000..893306d41054d --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference-3.rs @@ -0,0 +1,10 @@ +fn main() { + let v = Vec::new(); + v.push(0); + //~^ NOTE this is of type `{integer}`, which makes `v` to be inferred as `Vec<{integer}>` + v.push(0); + v.push(""); //~ ERROR mismatched types + //~^ NOTE expected integer, found `&str` + //~| NOTE arguments to this function are incorrect + //~| NOTE associated function defined here +} diff --git a/src/test/ui/type/type-check/point-at-inference-3.stderr b/src/test/ui/type/type-check/point-at-inference-3.stderr new file mode 100644 index 0000000000000..01264edf6b6e2 --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference-3.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/point-at-inference-3.rs:6:12 + | +LL | v.push(0); + | - this is of type `{integer}`, which makes `v` to be inferred as `Vec<{integer}>` +... +LL | v.push(""); + | ---- ^^ expected integer, found `&str` + | | + | arguments to this function are incorrect + | +note: associated function defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From f571862d174e3d26002bf52138ead476bf068f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 00:37:53 +0000 Subject: [PATCH 09/12] Suggest changing argument on type error --- .../ui/type/type-check/point-at-inference-3.fixed | 12 ++++++++++++ .../ui/type/type-check/point-at-inference-3.rs | 12 +++++++----- .../ui/type/type-check/point-at-inference-3.stderr | 14 +++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/type/type-check/point-at-inference-3.fixed diff --git a/src/test/ui/type/type-check/point-at-inference-3.fixed b/src/test/ui/type/type-check/point-at-inference-3.fixed new file mode 100644 index 0000000000000..ff299940a5766 --- /dev/null +++ b/src/test/ui/type/type-check/point-at-inference-3.fixed @@ -0,0 +1,12 @@ +// run-rustfix +fn main() { + let mut v = Vec::new(); + v.push(0i32); + //~^ NOTE this is of type `i32`, which makes `v` to be inferred as `Vec` + v.push(0); + v.push(1i32); //~ ERROR mismatched types + //~^ NOTE expected `i32`, found `u32` + //~| NOTE arguments to this function are incorrect + //~| NOTE associated function defined here + //~| HELP change the type of the numeric literal from `u32` to `i32` +} diff --git a/src/test/ui/type/type-check/point-at-inference-3.rs b/src/test/ui/type/type-check/point-at-inference-3.rs index 893306d41054d..812a39e4aaf33 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.rs +++ b/src/test/ui/type/type-check/point-at-inference-3.rs @@ -1,10 +1,12 @@ +// run-rustfix fn main() { - let v = Vec::new(); + let mut v = Vec::new(); + v.push(0i32); + //~^ NOTE this is of type `i32`, which makes `v` to be inferred as `Vec` v.push(0); - //~^ NOTE this is of type `{integer}`, which makes `v` to be inferred as `Vec<{integer}>` - v.push(0); - v.push(""); //~ ERROR mismatched types - //~^ NOTE expected integer, found `&str` + v.push(1u32); //~ ERROR mismatched types + //~^ NOTE expected `i32`, found `u32` //~| NOTE arguments to this function are incorrect //~| NOTE associated function defined here + //~| HELP change the type of the numeric literal from `u32` to `i32` } diff --git a/src/test/ui/type/type-check/point-at-inference-3.stderr b/src/test/ui/type/type-check/point-at-inference-3.stderr index 01264edf6b6e2..4e77796946691 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.stderr +++ b/src/test/ui/type/type-check/point-at-inference-3.stderr @@ -1,16 +1,20 @@ error[E0308]: mismatched types - --> $DIR/point-at-inference-3.rs:6:12 + --> $DIR/point-at-inference-3.rs:7:12 | -LL | v.push(0); - | - this is of type `{integer}`, which makes `v` to be inferred as `Vec<{integer}>` +LL | v.push(0i32); + | ---- this is of type `i32`, which makes `v` to be inferred as `Vec` ... -LL | v.push(""); - | ---- ^^ expected integer, found `&str` +LL | v.push(1u32); + | ---- ^^^^ expected `i32`, found `u32` | | | arguments to this function are incorrect | note: associated function defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL +help: change the type of the numeric literal from `u32` to `i32` + | +LL | v.push(1i32); + | ~~~ error: aborting due to previous error From 98f3936aa7e99f54ff377a77f9b03cea2c2c29d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 01:00:21 +0000 Subject: [PATCH 10/12] review comment: potentially produce more suggestions for arg type mismatch --- compiler/rustc_hir_typeck/src/demand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index cff74cd9693dc..06f34abbea26b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -364,11 +364,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_expected.insert(error.expected, error.found); } } - for (param, (arg,arg_ty)) in param_args.iter() { + for (param, (arg, arg_ty)) in param_args.iter() { let Some(expected) = param_expected.get(param) else { continue; }; let Some(found) = param_found.get(param) else { continue; }; if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; } - self.suggest_deref_ref_or_into(err, arg, *expected, *found, None); + self.emit_coerce_suggestions(err, arg, *found, *expected, None, None); } let ty = eraser.fold_ty(ty); From b182259d391a033a3a7017a7be7d5494405fc8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 01:26:44 +0000 Subject: [PATCH 11/12] review comments: reword --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- src/test/ui/type/type-check/point-at-inference-3.fixed | 2 +- src/test/ui/type/type-check/point-at-inference-3.rs | 2 +- src/test/ui/type/type-check/point-at-inference-3.stderr | 2 +- src/test/ui/type/type-check/point-at-inference.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 06f34abbea26b..6cf515cbb0adc 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -334,7 +334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label( arg.span, &format!( - "this is of type `{arg_ty}`, which makes `{ident}` to be \ + "this is of type `{arg_ty}`, which causes `{ident}` to be \ inferred as `{ty}`", ), ); diff --git a/src/test/ui/type/type-check/point-at-inference-3.fixed b/src/test/ui/type/type-check/point-at-inference-3.fixed index ff299940a5766..870f2779b8c48 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.fixed +++ b/src/test/ui/type/type-check/point-at-inference-3.fixed @@ -2,7 +2,7 @@ fn main() { let mut v = Vec::new(); v.push(0i32); - //~^ NOTE this is of type `i32`, which makes `v` to be inferred as `Vec` + //~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec` v.push(0); v.push(1i32); //~ ERROR mismatched types //~^ NOTE expected `i32`, found `u32` diff --git a/src/test/ui/type/type-check/point-at-inference-3.rs b/src/test/ui/type/type-check/point-at-inference-3.rs index 812a39e4aaf33..a4471e12fc2af 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.rs +++ b/src/test/ui/type/type-check/point-at-inference-3.rs @@ -2,7 +2,7 @@ fn main() { let mut v = Vec::new(); v.push(0i32); - //~^ NOTE this is of type `i32`, which makes `v` to be inferred as `Vec` + //~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec` v.push(0); v.push(1u32); //~ ERROR mismatched types //~^ NOTE expected `i32`, found `u32` diff --git a/src/test/ui/type/type-check/point-at-inference-3.stderr b/src/test/ui/type/type-check/point-at-inference-3.stderr index 4e77796946691..c373c6722fdd7 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.stderr +++ b/src/test/ui/type/type-check/point-at-inference-3.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/point-at-inference-3.rs:7:12 | LL | v.push(0i32); - | ---- this is of type `i32`, which makes `v` to be inferred as `Vec` + | ---- this is of type `i32`, which causes `v` to be inferred as `Vec` ... LL | v.push(1u32); | ---- ^^^^ expected `i32`, found `u32` diff --git a/src/test/ui/type/type-check/point-at-inference.stderr b/src/test/ui/type/type-check/point-at-inference.stderr index 197511bf64ef2..70428fe841b9c 100644 --- a/src/test/ui/type/type-check/point-at-inference.stderr +++ b/src/test/ui/type/type-check/point-at-inference.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/point-at-inference.rs:12:9 | LL | foo.push(i); - | - this is of type `&{integer}`, which makes `foo` to be inferred as `Vec<&{integer}>` + | - this is of type `&{integer}`, which causes `foo` to be inferred as `Vec<&{integer}>` ... LL | bar(foo); | --- ^^^ expected `i32`, found `&{integer}` From f98f2fcb5e4985bd7b12bae4ec03f2d96197fb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 5 Jan 2023 17:06:34 +0000 Subject: [PATCH 12/12] fix rebase --- compiler/rustc_hir_typeck/src/demand.rs | 10 ++++------ src/test/ui/type/type-check/point-at-inference-3.fixed | 2 +- src/test/ui/type/type-check/point-at-inference-3.rs | 2 +- .../ui/type/type-check/point-at-inference-3.stderr | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6cf515cbb0adc..cf070eb962f09 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -226,12 +226,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; let hir::def::Res::Local(hir_id) = p.res else { return false; }; let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; }; - let parent = map.get_parent_node(pat.hir_id); let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. - })) = map.find(parent) else { return false; }; + })) = map.find_parent(pat.hir_id) else { return false; }; let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; }; if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() { return false; @@ -285,12 +284,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // In every expression where the binding is referenced, we will look at that // expression's type and see if it is where the incorrect found type was fully // "materialized" and point at it. We will also try to provide a suggestion there. - let parent = map.get_parent_node(binding.hir_id); - if let Some(hir::Node::Expr(expr)) - | Some(hir::Node::Stmt(hir::Stmt { + if let Some(hir::Node::Expr(expr) + | hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), .. - })) = &map.find(parent) + })) = &map.find_parent(binding.hir_id) && let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind && rcvr.hir_id == binding.hir_id && let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) diff --git a/src/test/ui/type/type-check/point-at-inference-3.fixed b/src/test/ui/type/type-check/point-at-inference-3.fixed index 870f2779b8c48..1a960133ceba9 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.fixed +++ b/src/test/ui/type/type-check/point-at-inference-3.fixed @@ -6,7 +6,7 @@ fn main() { v.push(0); v.push(1i32); //~ ERROR mismatched types //~^ NOTE expected `i32`, found `u32` - //~| NOTE arguments to this function are incorrect + //~| NOTE arguments to this method are incorrect //~| NOTE associated function defined here //~| HELP change the type of the numeric literal from `u32` to `i32` } diff --git a/src/test/ui/type/type-check/point-at-inference-3.rs b/src/test/ui/type/type-check/point-at-inference-3.rs index a4471e12fc2af..92910ae1a3114 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.rs +++ b/src/test/ui/type/type-check/point-at-inference-3.rs @@ -6,7 +6,7 @@ fn main() { v.push(0); v.push(1u32); //~ ERROR mismatched types //~^ NOTE expected `i32`, found `u32` - //~| NOTE arguments to this function are incorrect + //~| NOTE arguments to this method are incorrect //~| NOTE associated function defined here //~| HELP change the type of the numeric literal from `u32` to `i32` } diff --git a/src/test/ui/type/type-check/point-at-inference-3.stderr b/src/test/ui/type/type-check/point-at-inference-3.stderr index c373c6722fdd7..999c3148362f6 100644 --- a/src/test/ui/type/type-check/point-at-inference-3.stderr +++ b/src/test/ui/type/type-check/point-at-inference-3.stderr @@ -7,7 +7,7 @@ LL | v.push(0i32); LL | v.push(1u32); | ---- ^^^^ expected `i32`, found `u32` | | - | arguments to this function are incorrect + | arguments to this method are incorrect | note: associated function defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL