@@ -221,7 +221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
221
221
}
222
222
223
223
/// Notes the point at which a variable is constrained to some type incompatible
224
- /// with `expected_ty `.
224
+ /// with some expectation given by `source `.
225
225
pub fn note_source_of_type_mismatch_constraint (
226
226
& self ,
227
227
err : & mut Diagnostic ,
@@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
265
265
266
266
use rustc_infer:: infer:: type_variable:: * ;
267
267
use rustc_middle:: infer:: unify_key:: * ;
268
-
268
+ // Replaces all of the variables in the given type with a fresh inference variable.
269
269
let mut fudger = BottomUpFolder {
270
270
tcx : self . tcx ,
271
271
ty_op : |ty| {
@@ -301,7 +301,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
301
301
302
302
let expected_ty = match source {
303
303
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 } => {
305
311
let hir:: ExprKind :: MethodCall ( segment, _, args, _) = call_expr. kind else {
306
312
return false ;
307
313
} ;
@@ -310,6 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
310
316
} ;
311
317
let possible_rcvr_ty = expr_finder. uses . iter ( ) . find_map ( |binding| {
312
318
let possible_rcvr_ty = self . node_ty_opt ( binding. hir_id ) ?;
319
+ // Fudge the receiver, so we can do new inference on it.
313
320
let possible_rcvr_ty = possible_rcvr_ty. fold_with ( & mut fudger) ;
314
321
let method = self
315
322
. lookup_method (
@@ -321,6 +328,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
321
328
args,
322
329
)
323
330
. 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.
324
334
let _ = self
325
335
. at ( & ObligationCause :: dummy ( ) , self . param_env )
326
336
. eq ( DefineOpaqueTypes :: No , method. sig . inputs ( ) [ idx + 1 ] , arg_ty)
@@ -339,11 +349,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
339
349
}
340
350
} ;
341
351
352
+ // If our expected_ty does not equal init_ty, then it *began* as incompatible.
353
+ // No need to note in this case...
342
354
if !self . can_eq ( self . param_env , expected_ty, init_ty. fold_with ( & mut fudger) ) {
343
355
return false ;
344
356
}
345
357
346
358
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.
347
362
let [ binding, next_usage] = * window else { continue ; } ;
348
363
349
364
// Don't go past the binding (always gonna be a nonsense label if so)
@@ -363,6 +378,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363
378
&& let hir:: ExprKind :: MethodCall ( segment, rcvr, args, _) = parent_expr. kind
364
379
&& rcvr. hir_id == binding. hir_id
365
380
{
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.
366
384
let Some ( rcvr_ty) = self . node_ty_opt ( rcvr. hir_id ) else { continue ; } ;
367
385
let rcvr_ty = rcvr_ty. fold_with ( & mut fudger) ;
368
386
let Ok ( method) =
@@ -371,14 +389,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
371
389
continue ;
372
390
} ;
373
391
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
+ } ) ;
381
402
403
+ // Find what argument caused our rcvr to become incompatible
404
+ // with the expected ty.
382
405
for ( idx, ( expected_arg_ty, arg_expr) ) in
383
406
std:: iter:: zip ( & method. sig . inputs ( ) [ 1 ..] , args) . enumerate ( )
384
407
{
@@ -391,35 +414,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
391
414
AllowTwoPhase :: No ,
392
415
None ,
393
416
) ;
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.
394
424
if self . can_eq ( self . param_env , rcvr_ty, expected_ty) {
395
425
continue ;
396
426
}
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}`..." ) ) ;
401
428
err. span_label (
402
429
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}`" ) ,
406
431
) ;
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.
407
436
if matches ! ( source, TypeMismatchSource :: Ty ( _) )
408
- && let Ok ( ideal_method_sig ) = ideal_method_sig
437
+ && let Some ( ideal_method ) = ideal_method
409
438
{
410
439
self . emit_type_mismatch_suggestions (
411
440
err,
412
441
arg_expr,
413
442
arg_ty,
414
- ideal_method_sig . inputs ( ) [ idx + 1 ] ,
443
+ self . resolve_vars_if_possible ( ideal_method . sig . inputs ( ) [ idx + 1 ] ) ,
415
444
None ,
416
445
None ,
417
446
) ;
418
447
}
419
448
return true ;
420
449
}
421
450
}
422
-
423
451
err. span_label (
424
452
binding. span ,
425
453
format ! ( "here the type of `{ident}` is inferred to be `{next_use_ty}`" ) ,
@@ -2092,6 +2120,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2092
2120
}
2093
2121
2094
2122
pub 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.
2095
2125
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 } ,
2097
2130
}
0 commit comments