Rollup merge of #108687 - compiler-errors:reformulate-point_at_expr_source_of_inferred_type, r=oli-obk
Reformulate `point_at_expr_source_of_inferred_type` to be more accurate Be more accurate when deducing where along the several usages of a binding it is constrained to be some type that is incompatible with an expectation. This also renames the method to `note_source_of_type_mismatch_constraint` because I prefer that name, though I guess I can revert that. (Also drive-by rename `note_result_coercion` -> `suggest_coercing_result_via_try_operator`, because it's suggesting, not noting!) This PR is (probably?) best reviewed per commit, but it does regress a bit only to fix it later on, so it could also be reviewed as a whole if that makes the final results more clear. r? `@estebank`
This commit is contained in:
@@ -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::lint::in_external_macro;
|
|||||||
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,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|| 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,
|
||||||
|
TypeMismatchSource::Ty(expected),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,37 +220,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
(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 some expectation given by `source`.
|
||||||
|
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>,
|
source: TypeMismatchSource<'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 +258,205 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
use rustc_infer::infer::type_variable::*;
|
||||||
|
use rustc_middle::infer::unify_key::*;
|
||||||
|
// Replaces all of the variables in the given type with a fresh inference variable.
|
||||||
|
let mut fudger = BottomUpFolder {
|
||||||
tcx: self.tcx,
|
tcx: self.tcx,
|
||||||
|
ty_op: |ty| {
|
||||||
|
if let ty::Infer(infer) = ty.kind() {
|
||||||
|
match infer {
|
||||||
|
ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
|
||||||
|
kind: TypeVariableOriginKind::MiscVariable,
|
||||||
|
span: DUMMY_SP,
|
||||||
|
}),
|
||||||
|
ty::InferTy::IntVar(_) => self.next_int_var(),
|
||||||
|
ty::InferTy::FloatVar(_) => self.next_float_var(),
|
||||||
|
_ => bug!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
},
|
||||||
lt_op: |_| self.tcx.lifetimes.re_erased,
|
lt_op: |_| self.tcx.lifetimes.re_erased,
|
||||||
ct_op: |c| c,
|
ct_op: |ct| {
|
||||||
ty_op: |t| match *t.kind() {
|
if let ty::ConstKind::Infer(_) = ct.kind() {
|
||||||
ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)),
|
self.next_const_var(
|
||||||
ty::Infer(ty::IntVar(_)) => self.tcx.mk_int_var(ty::IntVid { index: 0 }),
|
ct.ty(),
|
||||||
ty::Infer(ty::FloatVar(_)) => self.tcx.mk_float_var(ty::FloatVid { index: 0 }),
|
ConstVariableOrigin {
|
||||||
_ => t,
|
kind: ConstVariableOriginKind::MiscVariable,
|
||||||
|
span: DUMMY_SP,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ct
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut prev = eraser.fold_ty(ty);
|
|
||||||
let mut prev_span: Option<Span> = None;
|
|
||||||
|
|
||||||
for binding in expr_finder.uses {
|
let expected_ty = match source {
|
||||||
// In every expression where the binding is referenced, we will look at that
|
TypeMismatchSource::Ty(expected_ty) => expected_ty,
|
||||||
// expression's type and see if it is where the incorrect found type was fully
|
// Try to deduce what the possible value of `expr` would be if the
|
||||||
// "materialized" and point at it. We will also try to provide a suggestion there.
|
// incompatible arg were compatible. For example, given `Vec<i32>`
|
||||||
if let Some(hir::Node::Expr(expr)
|
// and `vec.push(1u32)`, we ideally want to deduce that the type of
|
||||||
| hir::Node::Stmt(hir::Stmt {
|
// `vec` *should* have been `Vec<u32>`. This will allow us to then
|
||||||
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
|
// run the subsequent code with this expectation, finding out exactly
|
||||||
..
|
// when this type diverged from our expectation.
|
||||||
})) = &map.find_parent(binding.hir_id)
|
TypeMismatchSource::Arg { call_expr, incompatible_arg: idx } => {
|
||||||
&& let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind
|
let hir::ExprKind::MethodCall(segment, _, args, _) = call_expr.kind else {
|
||||||
&& rcvr.hir_id == binding.hir_id
|
return false;
|
||||||
&& 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).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
|
let Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else {
|
||||||
// `&self`, so that we can compare it against the binding.
|
return false;
|
||||||
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 possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| {
|
||||||
let mut param_expected = FxHashMap::default();
|
let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?;
|
||||||
let mut param_found = FxHashMap::default();
|
// Fudge the receiver, so we can do new inference on it.
|
||||||
if self.can_eq(self.param_env, ty, found) {
|
let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
|
||||||
// We only point at the first place where the found type was inferred.
|
let method = self
|
||||||
for (param_ty, arg) in param_tys.zip(args) {
|
.lookup_method(
|
||||||
if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() {
|
possible_rcvr_ty,
|
||||||
// We found an argument that references a type parameter in `Self`,
|
segment,
|
||||||
// so we assume that this is the argument that caused the found
|
DUMMY_SP,
|
||||||
// type, which we know already because of `can_eq` above was first
|
call_expr,
|
||||||
// inferred in this method call.
|
binding,
|
||||||
let arg_ty = self.node_ty(arg.hir_id);
|
args,
|
||||||
if !arg.span.overlaps(mismatch_span) {
|
)
|
||||||
err.span_label(
|
.ok()?;
|
||||||
arg.span,
|
// Unify the method signature with our incompatible arg, to
|
||||||
&format!(
|
// do inference in the *opposite* direction and to find out
|
||||||
"this is of type `{arg_ty}`, which causes `{ident}` to be \
|
// what our ideal rcvr ty would look like.
|
||||||
inferred as `{ty}`",
|
let _ = self
|
||||||
),
|
.at(&ObligationCause::dummy(), self.param_env)
|
||||||
);
|
.eq(DefineOpaqueTypes::No, method.sig.inputs()[idx + 1], arg_ty)
|
||||||
}
|
.ok()?;
|
||||||
param_args.insert(param_ty, (arg, arg_ty));
|
self.select_obligations_where_possible(|errs| {
|
||||||
}
|
// Yeet the errors, we're already reporting errors.
|
||||||
}
|
errs.clear();
|
||||||
|
});
|
||||||
|
Some(self.resolve_vars_if_possible(possible_rcvr_ty))
|
||||||
|
});
|
||||||
|
if let Some(rcvr_ty) = possible_rcvr_ty {
|
||||||
|
rcvr_ty
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) { continue; }
|
|
||||||
self.emit_coerce_suggestions(err, arg, *found, *expected, None, 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)
|
|
||||||
{
|
|
||||||
// We only point at the first place where the found type was inferred.
|
|
||||||
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 our expected_ty does not equal init_ty, then it *began* as incompatible.
|
||||||
|
// No need to note in this case...
|
||||||
|
if !self.can_eq(self.param_env, expected_ty, init_ty.fold_with(&mut fudger)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for window in expr_finder.uses.windows(2) {
|
||||||
|
// Bindings always update their recorded type after the fact, so we
|
||||||
|
// need to look at the *following* usage's type to see when the
|
||||||
|
// binding became incompatible.
|
||||||
|
let [binding, next_usage] = *window else { continue; };
|
||||||
|
|
||||||
|
// Don't go past the binding (always gonna be a nonsense label if so)
|
||||||
if binding.hir_id == expr.hir_id {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
prev_span = Some(binding.span);
|
|
||||||
|
let Some(next_use_ty) = self.node_ty_opt(next_usage.hir_id) else { continue; };
|
||||||
|
|
||||||
|
// If the type is not constrained in a way making it not possible to
|
||||||
|
// equate with `expected_ty` by this point, skip.
|
||||||
|
if self.can_eq(self.param_env, expected_ty, next_use_ty.fold_with(&mut fudger)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let hir::Node::Expr(parent_expr) = hir.get_parent(binding.hir_id)
|
||||||
|
&& let hir::ExprKind::MethodCall(segment, rcvr, args, _) = parent_expr.kind
|
||||||
|
&& rcvr.hir_id == binding.hir_id
|
||||||
|
{
|
||||||
|
// If our binding became incompatible while it was a receiver
|
||||||
|
// to a method call, we may be able to make a better guess to
|
||||||
|
// the source of a type mismatch.
|
||||||
|
let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
|
||||||
|
let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
|
||||||
|
let Ok(method) =
|
||||||
|
self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
|
||||||
|
let ideal_method = self
|
||||||
|
.lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
|
||||||
|
.ok()
|
||||||
|
.and_then(|method| {
|
||||||
|
let _ = self.at(&ObligationCause::dummy(), self.param_env)
|
||||||
|
.eq(DefineOpaqueTypes::No, ideal_rcvr_ty, expected_ty)
|
||||||
|
.ok()?;
|
||||||
|
Some(method)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find what argument caused our rcvr to become incompatible
|
||||||
|
// with the expected ty.
|
||||||
|
for (idx, (expected_arg_ty, arg_expr)) in
|
||||||
|
std::iter::zip(&method.sig.inputs()[1..], args).enumerate()
|
||||||
|
{
|
||||||
|
let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; };
|
||||||
|
let arg_ty = arg_ty.fold_with(&mut fudger);
|
||||||
|
let _ = self.try_coerce(
|
||||||
|
arg_expr,
|
||||||
|
arg_ty,
|
||||||
|
*expected_arg_ty,
|
||||||
|
AllowTwoPhase::No,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
self.select_obligations_where_possible(|errs| {
|
||||||
|
// Yeet the errors, we're already reporting errors.
|
||||||
|
errs.clear();
|
||||||
|
});
|
||||||
|
// If our rcvr, after inference due to unifying the signature
|
||||||
|
// with the expected argument type, is still compatible with
|
||||||
|
// the rcvr, then it must've not been the source of blame.
|
||||||
|
if self.can_eq(self.param_env, rcvr_ty, expected_ty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
err.span_label(arg_expr.span, format!("this argument has type `{arg_ty}`..."));
|
||||||
|
err.span_label(
|
||||||
|
binding.span,
|
||||||
|
format!("... which causes `{ident}` to have type `{next_use_ty}`"),
|
||||||
|
);
|
||||||
|
// Using our "ideal" method signature, suggest a fix to this
|
||||||
|
// blame arg, if possible. Don't do this if we're coming from
|
||||||
|
// arg mismatch code, because we'll possibly suggest a mutually
|
||||||
|
// incompatible fix at the original mismatch site.
|
||||||
|
if matches!(source, TypeMismatchSource::Ty(_))
|
||||||
|
&& let Some(ideal_method) = ideal_method
|
||||||
|
{
|
||||||
|
self.emit_type_mismatch_suggestions(
|
||||||
|
err,
|
||||||
|
arg_expr,
|
||||||
|
arg_ty,
|
||||||
|
self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.span_label(
|
||||||
|
binding.span,
|
||||||
|
format!("here the type of `{ident}` is inferred to be `{next_use_ty}`"),
|
||||||
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
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 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>,
|
||||||
@@ -2094,3 +2118,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum TypeMismatchSource<'tcx> {
|
||||||
|
/// Expected the binding to have the given type, but it was found to have
|
||||||
|
/// a different type. Find out when that type first became incompatible.
|
||||||
|
Ty(Ty<'tcx>),
|
||||||
|
/// When we fail during method argument checking, try to find out if a previous
|
||||||
|
/// expression has constrained the method's receiver in a way that makes the
|
||||||
|
/// argument's type incompatible.
|
||||||
|
Arg { call_expr: &'tcx hir::Expr<'tcx>, incompatible_arg: usize },
|
||||||
|
}
|
||||||
|
|||||||
@@ -472,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
err_code: &str,
|
err_code: &str,
|
||||||
fn_def_id: Option<DefId>,
|
fn_def_id: Option<DefId>,
|
||||||
call_span: Span,
|
call_span: Span,
|
||||||
call_expr: &hir::Expr<'tcx>,
|
call_expr: &'tcx hir::Expr<'tcx>,
|
||||||
) {
|
) {
|
||||||
// Next, let's construct the error
|
// Next, let's construct the error
|
||||||
let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
|
let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
|
||||||
@@ -807,24 +807,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
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)
|
if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
|
||||||
|
&& provided_idx.as_usize() == expected_idx.as_usize()
|
||||||
{
|
{
|
||||||
// Type that would have accepted this argument if it hadn't been inferred earlier.
|
self.note_source_of_type_mismatch_constraint(
|
||||||
// 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,
|
&mut err,
|
||||||
rcvr,
|
rcvr,
|
||||||
expected,
|
crate::demand::TypeMismatchSource::Arg {
|
||||||
callee_ty,
|
call_expr,
|
||||||
provided_arg_span,
|
incompatible_arg: provided_idx.as_usize(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
|
|||||||
@@ -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`
|
||||||
| |
|
| |
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
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>`
|
//~^ NOTE this argument has type `i32`...
|
||||||
|
//~| NOTE ... which causes `v` to have type `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`
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
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>`
|
//~^ NOTE this argument has type `i32`...
|
||||||
|
//~| NOTE ... which causes `v` to have type `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`
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/point-at-inference-3.rs:7:12
|
--> $DIR/point-at-inference-3.rs:8:12
|
||||||
|
|
|
|
||||||
LL | v.push(0i32);
|
LL | v.push(0i32);
|
||||||
| ---- this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
|
| - ---- this argument has type `i32`...
|
||||||
|
| |
|
||||||
|
| ... which causes `v` to have type `Vec<i32>`
|
||||||
...
|
...
|
||||||
LL | v.push(1u32);
|
LL | v.push(1u32);
|
||||||
| ---- ^^^^ expected `i32`, found `u32`
|
| ---- ^^^^ expected `i32`, found `u32`
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ 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 this argument has type `i32`...
|
||||||
|
//~| NOTE ... which causes `s` to have type `S<i32, _>`
|
||||||
//~| NOTE an argument is missing
|
//~| NOTE an argument is missing
|
||||||
//~| HELP provide the argument
|
//~| HELP provide the argument
|
||||||
|
//~| HELP change the type of the numeric literal from `i32` to `u32`
|
||||||
let t: S<u32, _> = s;
|
let t: S<u32, _> = s;
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~| NOTE expected `S<u32, _>`, found `S<i32, _>`
|
//~| NOTE expected `S<u32, _>`, found `S<i32, _>`
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ 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:19:24
|
||||||
|
|
|
|
||||||
|
LL | s.infer(0i32);
|
||||||
|
| - ---- this argument has type `i32`...
|
||||||
|
| |
|
||||||
|
| ... which causes `s` to have type `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, _>`
|
||||||
| |
|
| |
|
||||||
@@ -24,6 +29,10 @@ LL | let t: S<u32, _> = s;
|
|||||||
|
|
|
|
||||||
= note: expected struct `S<u32, _>`
|
= note: expected struct `S<u32, _>`
|
||||||
found struct `S<i32, _>`
|
found struct `S<i32, _>`
|
||||||
|
help: change the type of the numeric literal from `i32` to `u32`
|
||||||
|
|
|
||||||
|
LL | s.infer(0u32);
|
||||||
|
| ~~~
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ 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}>`
|
| --- - this argument has type `&{integer}`...
|
||||||
|
| |
|
||||||
|
| ... which causes `foo` to have type `Vec<&{integer}>`
|
||||||
...
|
...
|
||||||
LL | bar(foo);
|
LL | bar(foo);
|
||||||
| --- ^^^ expected `Vec<i32>`, found `Vec<&{integer}>`
|
| --- ^^^ expected `Vec<i32>`, found `Vec<&{integer}>`
|
||||||
|
|||||||
@@ -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}`
|
||||||
|
|||||||
@@ -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<_>`
|
||||||
| |
|
| |
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ 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::<'_>}>`
|
| --- -------------------- this argument has type `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::<'_>}>`
|
| ... which causes `map` to have type `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}, ...>`
|
||||||
|
|
|
|
||||||
|
|||||||
Reference in New Issue
Block a user