1
Fork 0

Restore suggestion based off of backwards inference from bad usage to method call

This commit is contained in:
Michael Goulet 2023-03-02 23:49:24 +00:00
parent e72c45ad98
commit 29aee6a125
3 changed files with 88 additions and 59 deletions

View file

@ -259,49 +259,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body")); hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body"));
expr_finder.visit_expr(body.value); expr_finder.visit_expr(body.value);
let fudge_ty = |ty: Ty<'tcx>| { use rustc_infer::infer::type_variable::*;
use rustc_infer::infer::type_variable::*; use rustc_middle::infer::unify_key::*;
use rustc_middle::infer::unify_key::*;
ty.fold_with(&mut BottomUpFolder { let mut fudger = BottomUpFolder {
tcx: self.tcx, tcx: self.tcx,
ty_op: |ty| { ty_op: |ty| {
if let ty::Infer(infer) = ty.kind() { if let ty::Infer(infer) = ty.kind() {
match infer { match infer {
ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin { ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable, kind: TypeVariableOriginKind::MiscVariable,
span: DUMMY_SP, span: DUMMY_SP,
}), }),
ty::InferTy::IntVar(_) => self.next_int_var(), ty::InferTy::IntVar(_) => self.next_int_var(),
ty::InferTy::FloatVar(_) => self.next_float_var(), ty::InferTy::FloatVar(_) => self.next_float_var(),
_ => bug!(), _ => bug!(),
}
} else {
ty
} }
}, } else {
lt_op: |_| self.tcx.lifetimes.re_erased, ty
ct_op: |ct| { }
if let ty::ConstKind::Infer(_) = ct.kind() { },
self.next_const_var( lt_op: |_| self.tcx.lifetimes.re_erased,
ct.ty(), ct_op: |ct| {
ConstVariableOrigin { if let ty::ConstKind::Infer(_) = ct.kind() {
kind: ConstVariableOriginKind::MiscVariable, self.next_const_var(
span: DUMMY_SP, ct.ty(),
}, ConstVariableOrigin {
) kind: ConstVariableOriginKind::MiscVariable,
} else { span: DUMMY_SP,
ct },
} )
}, } else {
}) ct
}
},
}; };
let fudge_equals_found_ty = |use_ty: Ty<'tcx>| { if !self.can_eq(self.param_env, expected_ty, init_ty.fold_with(&mut fudger)) {
let use_ty = fudge_ty(use_ty);
self.can_eq(self.param_env, expected_ty, use_ty)
};
if !fudge_equals_found_ty(init_ty) {
return false; return false;
} }
@ -317,7 +311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the type is not constrained in a way making it not possible to // If the type is not constrained in a way making it not possible to
// equate with `expected_ty` by this point, skip. // equate with `expected_ty` by this point, skip.
if fudge_equals_found_ty(next_use_ty) { if self.can_eq(self.param_env, expected_ty, next_use_ty.fold_with(&mut fudger)) {
continue; continue;
} }
@ -326,26 +320,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& rcvr.hir_id == binding.hir_id && rcvr.hir_id == binding.hir_id
{ {
let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; }; let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
let rcvr_ty = fudge_ty(rcvr_ty); let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
if let Ok(method) = let Ok(method) =
self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args) self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
else {
continue;
};
// NOTE: For future removers of `fudge_inference_if_ok`, you
// can replace this with another call to `lookup_method` but
// using `expected_ty` as the rcvr.
let ideal_method_sig: Result<_, TypeError<'tcx>> = self.fudge_inference_if_ok(|| {
let _ = self.at(&ObligationCause::dummy(), self.param_env).eq(rcvr_ty, expected_ty)?;
Ok(method.sig)
});
for (idx, (expected_arg_ty, arg_expr)) in
std::iter::zip(&method.sig.inputs()[1..], args).enumerate()
{ {
for (expected_arg_ty, arg_expr) in std::iter::zip(&method.sig.inputs()[1..], args) { let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; };
let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; }; let arg_ty = arg_ty.fold_with(&mut fudger);
let arg_ty = fudge_ty(arg_ty); let _ = self.try_coerce(
let _ = self.try_coerce(arg_expr, arg_ty, *expected_arg_ty, AllowTwoPhase::No, None); arg_expr,
if !self.can_eq(self.param_env, rcvr_ty, expected_ty) { arg_ty,
err.span_label( *expected_arg_ty,
arg_expr.span, AllowTwoPhase::No,
format!("this argument has type `{arg_ty}`...") None,
); );
err.span_label( if self.can_eq(self.param_env, rcvr_ty, expected_ty) {
binding.span, continue;
format!("... which constrains `{ident}` to have type `{next_use_ty}`")
);
return true;
}
} }
err.span_label(
arg_expr.span,
format!("this argument has type `{arg_ty}`..."),
);
err.span_label(
binding.span,
format!(
"... which constrains `{ident}` to have type `{next_use_ty}`"
),
);
if let Ok(ideal_method_sig) = ideal_method_sig {
self.emit_type_mismatch_suggestions(
err,
arg_expr,
arg_ty,
ideal_method_sig.inputs()[idx + 1],
None,
None,
);
}
return true;
} }
} }

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

@ -18,6 +18,10 @@ 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