about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-03-02 23:49:24 +0000
committerMichael Goulet <michael@errs.io>2023-04-12 23:20:11 +0000
commit29aee6a125ac65a01932cb0ece5485e7cf8cfe87 (patch)
tree5f834161b1dc5b47ad05b2b179ea6893f65b7992
parente72c45ad987b296baee79865b7e2ca00c518fb8b (diff)
downloadrust-29aee6a125ac65a01932cb0ece5485e7cf8cfe87.tar.gz
rust-29aee6a125ac65a01932cb0ece5485e7cf8cfe87.zip
Restore suggestion based off of backwards inference from bad usage to method call
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs141
-rw-r--r--tests/ui/type/type-check/point-at-inference.fixed2
-rw-r--r--tests/ui/type/type-check/point-at-inference.stderr4
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
-                    }
-                },
-                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
+        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!(),
                     }
-                },
-            })
-        };
-
-        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)
+                } 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
+                }
+            },
         };
 
-        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<i32>) {}
    |    ^^^ -----------
+help: consider dereferencing the borrow
+   |
+LL |         foo.push(*i);
+   |                  +
 
 error: aborting due to previous error