Make note_source_of_type_mismatch_constraint simpler

This commit is contained in:
Michael Goulet 2023-03-02 22:55:37 +00:00
parent 4087deaccd
commit 42c4373ad1
13 changed files with 93 additions and 240 deletions

View File

@ -1,6 +1,5 @@
use crate::FnCtxt; use crate::FnCtxt;
use rustc_ast::util::parser::PREC_POSTFIX; use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan; use rustc_errors::MultiSpan;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir; use rustc_hir as hir;
@ -13,15 +12,13 @@
use rustc_middle::middle::stability::EvalResult; use rustc_middle::middle::stability::EvalResult;
use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder}; use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeFoldable};
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitableExt};
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_target::abi::FieldIdx; use rustc_target::abi::FieldIdx;
use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches;
use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::ObligationCause;
use super::method::probe; use super::method::probe;
@ -62,9 +59,10 @@ pub fn emit_type_mismatch_suggestions(
|| self.suggest_into(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.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected) || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
|| self.note_result_coercion(err, expr, expected, expr_ty); || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
if !suggested { if !suggested {
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span); self.note_source_of_type_mismatch_constraint(err, expr, expected);
} }
} }
@ -218,37 +216,34 @@ pub fn demand_coerce_diag(
(expected, Some(err)) (expected, Some(err))
} }
pub fn point_at_expr_source_of_inferred_type( /// Notes the point at which a variable is constrained to some type incompatible
/// with `expected_ty`.
pub fn note_source_of_type_mismatch_constraint(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
found: Ty<'tcx>, expected_ty: Ty<'tcx>,
expected: Ty<'tcx>,
mismatch_span: Span,
) -> bool { ) -> bool {
let map = self.tcx.hir(); let hir = self.tcx.hir();
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; 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::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
let hir::def::Res::Local(hir_id) = p.res else { return false; }; let hir::def::Res::Local(local_hir_id) = p.res else { return false; };
let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; }; let hir::Node::Pat(pat) = hir.get(local_hir_id) else { return false; };
let Some(hir::Node::Local(hir::Local { let (init_ty_hir_id, init) = match hir.get_parent(pat.hir_id) {
ty: None, hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init),
init: Some(init), hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)),
.. _ => return false,
})) = map.find_parent(pat.hir_id) else { return false; }; };
let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; }; let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { return false; };
if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() {
return false;
}
// Locate all the usages of the relevant binding. // Locate all the usages of the relevant binding.
struct FindExprs<'hir> { struct FindExprs<'tcx> {
hir_id: hir::HirId, hir_id: hir::HirId,
uses: Vec<&'hir hir::Expr<'hir>>, uses: Vec<&'tcx hir::Expr<'tcx>>,
} }
impl<'v> Visitor<'v> for FindExprs<'v> { impl<'tcx> Visitor<'tcx> for FindExprs<'tcx> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind
&& let hir::def::Res::Local(hir_id) = path.res && let hir::def::Res::Local(hir_id) = path.res
&& hir_id == self.hir_id && hir_id == self.hir_id
@ -259,180 +254,71 @@ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
} }
} }
let mut expr_finder = FindExprs { hir_id, uses: vec![] }; let mut expr_finder = FindExprs { hir_id: local_hir_id, uses: init.into_iter().collect() };
let id = map.get_parent_item(hir_id); let body =
let hir_id: hir::HirId = id.into(); hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body"));
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); expr_finder.visit_expr(body.value);
// 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(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)),
ty::Infer(ty::IntVar(_)) => self.tcx.mk_int_var(ty::IntVid { index: 0 }),
ty::Infer(ty::FloatVar(_)) => self.tcx.mk_float_var(ty::FloatVid { index: 0 }),
_ => t,
},
};
let mut prev = eraser.fold_ty(ty);
let mut prev_span: Option<Span> = None;
for binding in expr_finder.uses { let fudge_equals_found_ty = |use_ty: Ty<'tcx>| {
// In every expression where the binding is referenced, we will look at that use rustc_infer::infer::type_variable::*;
// expression's type and see if it is where the incorrect found type was fully use rustc_middle::infer::unify_key::*;
// "materialized" and point at it. We will also try to provide a suggestion there. let use_ty = use_ty.fold_with(&mut BottomUpFolder {
if let Some(hir::Node::Expr(expr) tcx: self.tcx,
| hir::Node::Stmt(hir::Stmt { ty_op: |ty| {
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), if let ty::Infer(infer) = ty.kind() {
.. match infer {
})) = &map.find_parent(binding.hir_id) ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
&& let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind kind: TypeVariableOriginKind::MiscVariable,
&& rcvr.hir_id == binding.hir_id span: DUMMY_SP,
&& let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) }),
{ ty::InferTy::IntVar(_) => self.next_int_var(),
// We special case methods, because they can influence inference through the ty::InferTy::FloatVar(_) => self.next_float_var(),
// call's arguments and we can provide a more explicit span. _ => bug!(),
let sig = self.tcx.fn_sig(def_id).subst_identity();
let def_self_ty = sig.input(0).skip_binder();
let param_tys = sig.inputs().skip_binder().iter().skip(1);
// If there's an arity mismatch, pointing out the call as the source of an inference
// can be misleading, so we skip it.
if param_tys.len() != args.len() {
continue;
}
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* this analysis is done.
let ty = match self.lookup_probe_for_diagnostic(
segment.ident,
rcvr_ty,
expr,
probe::ProbeScope::TraitsInScope,
None,
) {
Ok(pick) => eraser.fold_ty(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) {
// We only point at the first place where the found type was inferred.
for (param_ty, arg) in param_tys.zip(args) {
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_ty = self.node_ty(arg.hir_id);
if !arg.span.overlaps(mismatch_span) {
err.span_label(
arg.span,
&format!(
"this is of type `{arg_ty}`, which causes `{ident}` to be \
inferred as `{ty}`",
),
);
}
param_args.insert(param_ty, (arg, arg_ty));
} }
} else {
ty
} }
} },
lt_op: |_| self.tcx.lifetimes.re_erased,
ct_op: |ct| {
if let ty::ConstKind::Infer(_) = ct.kind() {
self.next_const_var(
ct.ty(),
ConstVariableOrigin {
kind: ConstVariableOriginKind::MiscVariable,
span: DUMMY_SP,
},
)
} else {
ct
}
},
});
self.can_eq(self.param_env, expected_ty, use_ty)
};
// Here we find, for a type param `T`, the type that `T` is in the current if !fudge_equals_found_ty(init_ty) {
// method call *and* in the original expected type. That way, we can see if we return false;
// 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) { continue; }
self.emit_coerce_suggestions(err, arg, *found, *expected, None, None);
}
let ty = eraser.fold_ty(ty); for window in expr_finder.uses.windows(2) {
if ty.references_error() { let [binding, next_usage] = *window else { continue; };
break; let Some(next_use_ty) = self.node_ty_opt(next_usage.hir_id) else { continue; };
} if !fudge_equals_found_ty(next_use_ty) {
if ty != prev err.span_label(
&& param_args.is_empty() binding.span,
&& self.can_eq(self.param_env, ty, found) format!("here the type of `{ident}` is inferred to be `{next_use_ty}`"),
{ );
// We only point at the first place where the found type was inferred. return true;
if !segment.ident.span.overlaps(mismatch_span) {
err.span_label(
segment.ident.span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);}
break;
} else if !param_args.is_empty() {
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)
{
// 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.
if !span.overlaps(mismatch_span) {
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 if next_usage.hir_id == expr.hir_id {
// evaluating and had a type error.
break; break;
} }
prev_span = Some(binding.span);
} }
true
// We must've not found something that constrained the expr.
false
} }
fn annotate_expected_due_to_let_ty( fn annotate_expected_due_to_let_ty(
@ -708,7 +594,7 @@ fn annotate_alternative_method_deref(
); );
} }
pub(crate) fn note_result_coercion( pub(crate) fn suggest_coercing_result_via_try_operator(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
expr: &hir::Expr<'tcx>, expr: &hir::Expr<'tcx>,

View File

@ -807,24 +807,10 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
full_call_span, full_call_span,
format!("arguments to this {} are incorrect", call_name), format!("arguments to this {} are incorrect", call_name),
); );
if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) =
(callee_ty, &call_expr.kind) // TODO: We would like to point out when the rcvr was constrained
{ // such that the arg mismatch occurs.
// 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,
provided_arg_span,
);
}
// Call out where the function is defined // Call out where the function is defined
self.label_fn_like( self.label_fn_like(
&mut err, &mut err,

View File

@ -67,9 +67,6 @@ LL | x == 5
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/assignment-in-if.rs:44:18 --> $DIR/assignment-in-if.rs:44:18
| |
LL | if y = (Foo { foo: x }) {
| - here the type of `x` is inferred to be `usize`
...
LL | if x == x && x = x && x == x { LL | if x == x && x = x && x == x {
| ------ ^ expected `bool`, found `usize` | ------ ^ expected `bool`, found `usize`
| | | |
@ -78,9 +75,6 @@ LL | if x == x && x = x && x == x {
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/assignment-in-if.rs:44:22 --> $DIR/assignment-in-if.rs:44:22
| |
LL | if y = (Foo { foo: x }) {
| - here the type of `x` is inferred to be `usize`
...
LL | if x == x && x = x && x == x { LL | if x == x && x = x && x == x {
| ^ expected `bool`, found `usize` | ^ expected `bool`, found `usize`
@ -98,9 +92,6 @@ LL | if x == x && x == x && x == x {
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/assignment-in-if.rs:51:28 --> $DIR/assignment-in-if.rs:51:28
| |
LL | if y = (Foo { foo: x }) {
| - here the type of `x` is inferred to be `usize`
...
LL | if x == x && x == x && x = x { LL | if x == x && x == x && x = x {
| ---------------- ^ expected `bool`, found `usize` | ---------------- ^ expected `bool`, found `usize`
| | | |

View File

@ -2,7 +2,6 @@
fn main() { fn main() {
let mut v = Vec::new(); let mut v = Vec::new();
v.push(0i32); v.push(0i32);
//~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
v.push(0); v.push(0);
v.push(1i32); //~ ERROR mismatched types v.push(1i32); //~ ERROR mismatched types
//~^ NOTE expected `i32`, found `u32` //~^ NOTE expected `i32`, found `u32`

View File

@ -2,7 +2,6 @@
fn main() { fn main() {
let mut v = Vec::new(); let mut v = Vec::new();
v.push(0i32); v.push(0i32);
//~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
v.push(0); v.push(0);
v.push(1u32); //~ ERROR mismatched types v.push(1u32); //~ ERROR mismatched types
//~^ NOTE expected `i32`, found `u32` //~^ NOTE expected `i32`, found `u32`

View File

@ -1,9 +1,6 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/point-at-inference-3.rs:7:12 --> $DIR/point-at-inference-3.rs:6:12
| |
LL | v.push(0i32);
| ---- this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
...
LL | v.push(1u32); LL | v.push(1u32);
| ---- ^^^^ expected `i32`, found `u32` | ---- ^^^^ expected `i32`, found `u32`
| | | |

View File

@ -11,6 +11,7 @@ fn main() {
let s = S(None); let s = S(None);
s.infer(0i32); s.infer(0i32);
//~^ ERROR this method takes 2 arguments but 1 argument was supplied //~^ ERROR this method takes 2 arguments but 1 argument was supplied
//~| NOTE here the type of `s` is inferred to be `S<i32, _>`
//~| NOTE an argument is missing //~| NOTE an argument is missing
//~| HELP provide the argument //~| HELP provide the argument
let t: S<u32, _> = s; let t: S<u32, _> = s;

View File

@ -15,8 +15,11 @@ LL | s.infer(0i32, /* b */);
| ~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/point-at-inference-4.rs:16:24 --> $DIR/point-at-inference-4.rs:17:24
| |
LL | s.infer(0i32);
| - here the type of `s` is inferred to be `S<i32, _>`
...
LL | let t: S<u32, _> = s; LL | let t: S<u32, _> = s;
| --------- ^ expected `S<u32, _>`, found `S<i32, _>` | --------- ^ expected `S<u32, _>`, found `S<i32, _>`
| | | |

View File

@ -6,7 +6,7 @@ fn main() {
let mut foo = vec![]; let mut foo = vec![];
baz(&foo); baz(&foo);
for i in &v { for i in &v {
foo.push(*i); foo.push(i);
} }
baz(&foo); baz(&foo);
bar(foo); //~ ERROR E0308 bar(foo); //~ ERROR E0308

View File

@ -2,7 +2,7 @@ error[E0308]: mismatched types
--> $DIR/point-at-inference.rs:12:9 --> $DIR/point-at-inference.rs:12:9
| |
LL | foo.push(i); LL | foo.push(i);
| - this is of type `&{integer}`, which causes `foo` to be inferred as `Vec<&{integer}>` | --- here the type of `foo` is inferred to be `Vec<&{integer}>`
... ...
LL | bar(foo); LL | bar(foo);
| --- ^^^ expected `Vec<i32>`, found `Vec<&{integer}>` | --- ^^^ expected `Vec<i32>`, found `Vec<&{integer}>`
@ -16,10 +16,6 @@ note: function defined here
| |
LL | fn bar(_: Vec<i32>) {} LL | fn bar(_: Vec<i32>) {}
| ^^^ ----------- | ^^^ -----------
help: consider dereferencing the borrow
|
LL | foo.push(*i);
| +
error: aborting due to previous error error: aborting due to previous error

View File

@ -7,7 +7,6 @@ LL | primes.contains(3);
| | expected `&_`, found integer | | expected `&_`, found integer
| | help: consider borrowing here: `&3` | | help: consider borrowing here: `&3`
| arguments to this method are incorrect | arguments to this method are incorrect
| here the type of `primes` is inferred to be `[_]`
| |
= note: expected reference `&_` = note: expected reference `&_`
found type `{integer}` found type `{integer}`

View File

@ -1,8 +1,6 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/bad-type-in-vec-push.rs:11:17 --> $DIR/bad-type-in-vec-push.rs:11:17
| |
LL | vector.sort();
| ------ here the type of `vector` is inferred to be `Vec<_>`
LL | result.push(vector); LL | result.push(vector);
| ---- ^^^^^^ expected integer, found `Vec<_>` | ---- ^^^^^^ expected integer, found `Vec<_>`
| | | |

View File

@ -2,9 +2,7 @@ error[E0308]: mismatched types
--> $DIR/issue-107775.rs:35:16 --> $DIR/issue-107775.rs:35:16
| |
LL | map.insert(1, Struct::do_something); LL | map.insert(1, Struct::do_something);
| - -------------------- this is of type `fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}`, which causes `map` to be inferred as `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>` | --- here the type of `map` is inferred to be `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
| |
| this is of type `{integer}`, which causes `map` to be inferred as `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
LL | Self { map } LL | Self { map }
| ^^^ expected `HashMap<u16, fn(u8) -> Pin<...>>`, found `HashMap<{integer}, ...>` | ^^^ expected `HashMap<u16, fn(u8) -> Pin<...>>`, found `HashMap<{integer}, ...>`
| |