From 29aee6a125ac65a01932cb0ece5485e7cf8cfe87 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 2 Mar 2023 23:49:24 +0000 Subject: [PATCH] Restore suggestion based off of backwards inference from bad usage to method call --- compiler/rustc_hir_typeck/src/demand.rs | 141 +++++++++++------- .../type/type-check/point-at-inference.fixed | 2 +- .../type/type-check/point-at-inference.stderr | 4 + 3 files changed, 88 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 0d3e4bde497..f219068b4e8 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -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")); expr_finder.visit_expr(body.value); - let fudge_ty = |ty: Ty<'tcx>| { - use rustc_infer::infer::type_variable::*; - use rustc_middle::infer::unify_key::*; - ty.fold_with(&mut BottomUpFolder { - 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 + use rustc_infer::infer::type_variable::*; + use rustc_middle::infer::unify_key::*; + + let mut fudger = BottomUpFolder { + 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!(), } - }, - 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 - } - }, - }) + } 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 + } + }, }; - let fudge_equals_found_ty = |use_ty: Ty<'tcx>| { - let use_ty = fudge_ty(use_ty); - self.can_eq(self.param_env, expected_ty, use_ty) - }; - - if !fudge_equals_found_ty(init_ty) { + if !self.can_eq(self.param_env, expected_ty, init_ty.fold_with(&mut fudger)) { 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 // 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; } @@ -326,26 +320,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && rcvr.hir_id == binding.hir_id { let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; }; - let rcvr_ty = fudge_ty(rcvr_ty); - if let Ok(method) = + 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; + }; + + // 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 arg_ty = fudge_ty(arg_ty); - let _ = self.try_coerce(arg_expr, arg_ty, *expected_arg_ty, AllowTwoPhase::No, None); - if !self.can_eq(self.param_env, rcvr_ty, expected_ty) { - 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}`") - ); - return true; - } + 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, + ); + 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 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; } } diff --git a/tests/ui/type/type-check/point-at-inference.fixed b/tests/ui/type/type-check/point-at-inference.fixed index 6419e42e70d..f41fbe59fba 100644 --- a/tests/ui/type/type-check/point-at-inference.fixed +++ b/tests/ui/type/type-check/point-at-inference.fixed @@ -6,7 +6,7 @@ fn main() { let mut foo = vec![]; baz(&foo); for i in &v { - foo.push(i); + foo.push(*i); } baz(&foo); bar(foo); //~ ERROR E0308 diff --git a/tests/ui/type/type-check/point-at-inference.stderr b/tests/ui/type/type-check/point-at-inference.stderr index 5d46368b1fd..5fc94d4d1b6 100644 --- a/tests/ui/type/type-check/point-at-inference.stderr +++ b/tests/ui/type/type-check/point-at-inference.stderr @@ -18,6 +18,10 @@ note: function defined here | LL | fn bar(_: Vec) {} | ^^^ ----------- +help: consider dereferencing the borrow + | +LL | foo.push(*i); + | + error: aborting due to previous error