@@ -221,7 +221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
221221 }
222222
223223 /// Notes the point at which a variable is constrained to some type incompatible
224- /// with `expected_ty `.
224+ /// with some expectation given by `source `.
225225 pub fn note_source_of_type_mismatch_constraint (
226226 & self ,
227227 err : & mut Diagnostic ,
@@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
265265
266266 use rustc_infer:: infer:: type_variable:: * ;
267267 use rustc_middle:: infer:: unify_key:: * ;
268-
268+ // Replaces all of the variables in the given type with a fresh inference variable.
269269 let mut fudger = BottomUpFolder {
270270 tcx : self . tcx ,
271271 ty_op : |ty| {
@@ -301,7 +301,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
301301
302302 let expected_ty = match source {
303303 TypeMismatchSource :: Ty ( expected_ty) => expected_ty,
304- TypeMismatchSource :: Arg ( call_expr, idx) => {
304+ // Try to deduce what the possible value of `expr` would be if the
305+ // incompatible arg were compatible. For example, given `Vec<i32>`
306+ // and `vec.push(1u32)`, we ideally want to deduce that the type of
307+ // `vec` *should* have been `Vec<u32>`. This will allow us to then
308+ // run the subsequent code with this expectation, finding out exactly
309+ // when this type diverged from our expectation.
310+ TypeMismatchSource :: Arg { call_expr, incompatible_arg : idx } => {
305311 let hir:: ExprKind :: MethodCall ( segment, _, args, _) = call_expr. kind else {
306312 return false ;
307313 } ;
@@ -310,6 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
310316 } ;
311317 let possible_rcvr_ty = expr_finder. uses . iter ( ) . find_map ( |binding| {
312318 let possible_rcvr_ty = self . node_ty_opt ( binding. hir_id ) ?;
319+ // Fudge the receiver, so we can do new inference on it.
313320 let possible_rcvr_ty = possible_rcvr_ty. fold_with ( & mut fudger) ;
314321 let method = self
315322 . lookup_method (
@@ -321,6 +328,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
321328 args,
322329 )
323330 . ok ( ) ?;
331+ // Unify the method signature with our incompatible arg, to
332+ // do inference in the *opposite* direction and to find out
333+ // what our ideal rcvr ty would look like.
324334 let _ = self
325335 . at ( & ObligationCause :: dummy ( ) , self . param_env )
326336 . eq ( DefineOpaqueTypes :: No , method. sig . inputs ( ) [ idx + 1 ] , arg_ty)
@@ -339,11 +349,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
339349 }
340350 } ;
341351
352+ // If our expected_ty does not equal init_ty, then it *began* as incompatible.
353+ // No need to note in this case...
342354 if !self . can_eq ( self . param_env , expected_ty, init_ty. fold_with ( & mut fudger) ) {
343355 return false ;
344356 }
345357
346358 for window in expr_finder. uses . windows ( 2 ) {
359+ // Bindings always update their recorded type after the fact, so we
360+ // need to look at the *following* usage's type to see when the
361+ // binding became incompatible.
347362 let [ binding, next_usage] = * window else { continue ; } ;
348363
349364 // Don't go past the binding (always gonna be a nonsense label if so)
@@ -363,6 +378,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363378 && let hir:: ExprKind :: MethodCall ( segment, rcvr, args, _) = parent_expr. kind
364379 && rcvr. hir_id == binding. hir_id
365380 {
381+ // If our binding became incompatible while it was a receiver
382+ // to a method call, we may be able to make a better guess to
383+ // the source of a type mismatch.
366384 let Some ( rcvr_ty) = self . node_ty_opt ( rcvr. hir_id ) else { continue ; } ;
367385 let rcvr_ty = rcvr_ty. fold_with ( & mut fudger) ;
368386 let Ok ( method) =
@@ -371,14 +389,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
371389 continue ;
372390 } ;
373391
374- // NOTE: For future removers of `fudge_inference_if_ok`, you
375- // can replace this with another call to `lookup_method` but
376- // using `expected_ty` as the rcvr.
377- let ideal_method_sig: Result < _ , TypeError < ' tcx > > = self . fudge_inference_if_ok ( || {
378- let _ = self . at ( & ObligationCause :: dummy ( ) , self . param_env ) . eq ( rcvr_ty, expected_ty) ?;
379- Ok ( method. sig )
380- } ) ;
392+ let ideal_rcvr_ty = rcvr_ty. fold_with ( & mut fudger) ;
393+ let ideal_method = self
394+ . lookup_method ( ideal_rcvr_ty, segment, DUMMY_SP , parent_expr, rcvr, args)
395+ . ok ( )
396+ . and_then ( |method| {
397+ let _ = self . at ( & ObligationCause :: dummy ( ) , self . param_env )
398+ . eq ( DefineOpaqueTypes :: No , ideal_rcvr_ty, expected_ty)
399+ . ok ( ) ?;
400+ Some ( method)
401+ } ) ;
381402
403+ // Find what argument caused our rcvr to become incompatible
404+ // with the expected ty.
382405 for ( idx, ( expected_arg_ty, arg_expr) ) in
383406 std:: iter:: zip ( & method. sig . inputs ( ) [ 1 ..] , args) . enumerate ( )
384407 {
@@ -391,35 +414,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
391414 AllowTwoPhase :: No ,
392415 None ,
393416 ) ;
417+ self . select_obligations_where_possible ( |errs| {
418+ // Yeet the errors, we're already reporting errors.
419+ errs. clear ( ) ;
420+ } ) ;
421+ // If our rcvr, after inference due to unifying the signature
422+ // with the expected argument type, is still compatible with
423+ // the rcvr, then it must've not been the source of blame.
394424 if self . can_eq ( self . param_env , rcvr_ty, expected_ty) {
395425 continue ;
396426 }
397- err. span_label (
398- arg_expr. span ,
399- format ! ( "this argument has type `{arg_ty}`..." ) ,
400- ) ;
427+ err. span_label ( arg_expr. span , format ! ( "this argument has type `{arg_ty}`..." ) ) ;
401428 err. span_label (
402429 binding. span ,
403- format ! (
404- "... which constrains `{ident}` to have type `{next_use_ty}`"
405- ) ,
430+ format ! ( "... which causes `{ident}` to have type `{next_use_ty}`" ) ,
406431 ) ;
432+ // Using our "ideal" method signature, suggest a fix to this
433+ // blame arg, if possible. Don't do this if we're coming from
434+ // arg mismatch code, because we'll possibly suggest a mutually
435+ // incompatible fix at the original mismatch site.
407436 if matches ! ( source, TypeMismatchSource :: Ty ( _) )
408- && let Ok ( ideal_method_sig ) = ideal_method_sig
437+ && let Some ( ideal_method ) = ideal_method
409438 {
410439 self . emit_type_mismatch_suggestions (
411440 err,
412441 arg_expr,
413442 arg_ty,
414- ideal_method_sig . inputs ( ) [ idx + 1 ] ,
443+ self . resolve_vars_if_possible ( ideal_method . sig . inputs ( ) [ idx + 1 ] ) ,
415444 None ,
416445 None ,
417446 ) ;
418447 }
419448 return true ;
420449 }
421450 }
422-
423451 err. span_label (
424452 binding. span ,
425453 format ! ( "here the type of `{ident}` is inferred to be `{next_use_ty}`" ) ,
@@ -2092,6 +2120,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20922120}
20932121
20942122pub enum TypeMismatchSource < ' tcx > {
2123+ /// Expected the binding to have the given type, but it was found to have
2124+ /// a different type. Find out when that type first became incompatible.
20952125 Ty ( Ty < ' tcx > ) ,
2096- Arg ( & ' tcx hir:: Expr < ' tcx > , usize ) ,
2126+ /// When we fail during method argument checking, try to find out if a previous
2127+ /// expression has constrained the method's receiver in a way that makes the
2128+ /// argument's type incompatible.
2129+ Arg { call_expr : & ' tcx hir:: Expr < ' tcx > , incompatible_arg : usize } ,
20972130}
0 commit comments