@@ -445,9 +445,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
445
445
}
446
446
}
447
447
448
- /// When after several dereferencing, the reference satisfies the trait
449
- /// bound. This function provides dereference suggestion for this
450
- /// specific situation.
448
+ /// Provide a suggestion to dereference arguments to functions and binary operators, if that
449
+ /// would satisfy trait bounds.
451
450
pub ( super ) fn suggest_dereferences (
452
451
& self ,
453
452
obligation : & PredicateObligation < ' tcx > ,
@@ -461,127 +460,100 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
461
460
&& let Some ( arg_ty) = typeck_results. expr_ty_adjusted_opt ( expr)
462
461
{
463
462
// Suggest dereferencing the argument to a function/method call if possible
463
+
464
+ // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)
464
465
let mut real_trait_pred = trait_pred;
465
466
while let Some ( ( parent_code, parent_trait_pred) ) = code. parent ( ) {
466
467
code = parent_code;
467
468
if let Some ( parent_trait_pred) = parent_trait_pred {
468
469
real_trait_pred = parent_trait_pred;
469
470
}
471
+ }
470
472
471
- // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
472
- // `ReBound`, and we don't particularly care about the regions.
473
- let real_ty =
474
- self . tcx . instantiate_bound_regions_with_erased ( real_trait_pred. self_ty ( ) ) ;
473
+ // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle
474
+ // `ReBound`, and we don't particularly care about the regions.
475
+ let real_ty = self . tcx . instantiate_bound_regions_with_erased ( real_trait_pred. self_ty ( ) ) ;
476
+ if !self . can_eq ( obligation. param_env , real_ty, arg_ty) {
477
+ return false ;
478
+ }
475
479
476
- if self . can_eq ( obligation. param_env , real_ty, arg_ty)
477
- && let ty:: Ref ( region, base_ty, mutbl) = * real_ty. kind ( )
480
+ // Potentially, we'll want to place our dereferences under a `&`. We don't try this for
481
+ // `&mut`, since we can't be sure users will get the side-effects they want from it.
482
+ // If this doesn't work, we'll try removing the `&` in `suggest_remove_reference`.
483
+ // FIXME(dianne): this misses the case where users need both to deref and remove `&`s.
484
+ // This method could be combined with `TypeErrCtxt::suggest_remove_reference` to handle
485
+ // that, similar to what `FnCtxt::suggest_deref_or_ref` does.
486
+ let ( is_under_ref, base_ty, span) = match expr. kind {
487
+ hir:: ExprKind :: AddrOf ( hir:: BorrowKind :: Ref , hir:: Mutability :: Not , subexpr)
488
+ if let & ty:: Ref ( region, base_ty, hir:: Mutability :: Not ) = real_ty. kind ( ) =>
478
489
{
479
- let autoderef = ( self . autoderef_steps ) ( base_ty) ;
480
- if let Some ( steps) =
481
- autoderef. into_iter ( ) . enumerate ( ) . find_map ( |( steps, ( ty, obligations) ) | {
482
- // Re-add the `&`
483
- let ty = Ty :: new_ref ( self . tcx , region, ty, mutbl) ;
484
-
485
- // Remapping bound vars here
486
- let real_trait_pred_and_ty = real_trait_pred
487
- . map_bound ( |inner_trait_pred| ( inner_trait_pred, ty) ) ;
488
- let obligation = self . mk_trait_obligation_with_new_self_ty (
489
- obligation. param_env ,
490
- real_trait_pred_and_ty,
491
- ) ;
492
- let may_hold = obligations
493
- . iter ( )
494
- . chain ( [ & obligation] )
495
- . all ( |obligation| self . predicate_may_hold ( obligation) )
496
- . then_some ( steps) ;
490
+ ( Some ( region) , base_ty, subexpr. span )
491
+ }
492
+ // Don't suggest `*&mut`, etc.
493
+ hir:: ExprKind :: AddrOf ( ..) => return false ,
494
+ _ => ( None , real_ty, obligation. cause . span ) ,
495
+ } ;
497
496
498
- may_hold
499
- } )
500
- {
501
- if steps > 0 {
502
- // Don't care about `&mut` because `DerefMut` is used less
503
- // often and user will not expect that an autoderef happens.
504
- if let hir:: Node :: Expr ( hir:: Expr {
505
- kind :
506
- hir:: ExprKind :: AddrOf (
507
- hir:: BorrowKind :: Ref ,
508
- hir:: Mutability :: Not ,
509
- expr,
510
- ) ,
511
- ..
512
- } ) = self . tcx . hir_node ( * arg_hir_id)
513
- {
514
- let derefs = "*" . repeat ( steps) ;
515
- err. span_suggestion_verbose (
516
- expr. span . shrink_to_lo ( ) ,
517
- "consider dereferencing here" ,
518
- derefs,
519
- Applicability :: MachineApplicable ,
520
- ) ;
521
- return true ;
522
- }
523
- }
524
- } else if real_trait_pred != trait_pred {
525
- // This branch addresses #87437.
526
-
527
- let span = obligation. cause . span ;
528
- // Remapping bound vars here
529
- let real_trait_pred_and_base_ty = real_trait_pred
530
- . map_bound ( |inner_trait_pred| ( inner_trait_pred, base_ty) ) ;
531
- let obligation = self . mk_trait_obligation_with_new_self_ty (
532
- obligation. param_env ,
533
- real_trait_pred_and_base_ty,
534
- ) ;
535
- let sized_obligation = Obligation :: new (
536
- self . tcx ,
537
- obligation. cause . clone ( ) ,
538
- obligation. param_env ,
539
- ty:: TraitRef :: new (
540
- self . tcx ,
541
- self . tcx . require_lang_item (
542
- hir:: LangItem :: Sized ,
543
- Some ( obligation. cause . span ) ,
544
- ) ,
545
- [ base_ty] ,
546
- ) ,
547
- ) ;
548
- if self . predicate_may_hold ( & obligation)
549
- && self . predicate_must_hold_modulo_regions ( & sized_obligation)
550
- // Do not suggest * if it is already a reference,
551
- // will suggest removing the borrow instead in that case.
552
- && !matches ! ( expr. kind, hir:: ExprKind :: AddrOf ( ..) )
553
- {
554
- let call_node = self . tcx . hir_node ( * call_hir_id) ;
555
- let msg = "consider dereferencing here" ;
556
- let is_receiver = matches ! (
557
- call_node,
558
- Node :: Expr ( hir:: Expr {
559
- kind: hir:: ExprKind :: MethodCall ( _, receiver_expr, ..) ,
560
- ..
561
- } )
562
- if receiver_expr. hir_id == * arg_hir_id
563
- ) ;
564
- if is_receiver {
565
- err. multipart_suggestion_verbose (
566
- msg,
567
- vec ! [
568
- ( span. shrink_to_lo( ) , "(*" . to_string( ) ) ,
569
- ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
570
- ] ,
571
- Applicability :: MachineApplicable ,
572
- )
573
- } else {
574
- err. span_suggestion_verbose (
575
- span. shrink_to_lo ( ) ,
576
- msg,
577
- '*' ,
578
- Applicability :: MachineApplicable ,
579
- )
580
- } ;
581
- return true ;
582
- }
583
- }
497
+ let autoderef = ( self . autoderef_steps ) ( base_ty) ;
498
+ let mut is_boxed = base_ty. is_box ( ) ;
499
+ if let Some ( steps) = autoderef. into_iter ( ) . position ( |( mut ty, obligations) | {
500
+ // Ensure one of the following for dereferencing to be valid: we're passing by
501
+ // reference, `ty` is `Copy`, or we're moving out of a (potentially nested) `Box`.
502
+ let can_deref = is_under_ref. is_some ( )
503
+ || self . type_is_copy_modulo_regions ( obligation. param_env , ty)
504
+ || ty. is_numeric ( ) // for inference vars (presumably but not provably `Copy`)
505
+ || is_boxed && self . type_is_sized_modulo_regions ( obligation. param_env , ty) ;
506
+ is_boxed &= ty. is_box ( ) ;
507
+
508
+ // Re-add the `&` if necessary
509
+ if let Some ( region) = is_under_ref {
510
+ ty = Ty :: new_ref ( self . tcx , region, ty, hir:: Mutability :: Not ) ;
584
511
}
512
+
513
+ // Remapping bound vars here
514
+ let real_trait_pred_and_ty =
515
+ real_trait_pred. map_bound ( |inner_trait_pred| ( inner_trait_pred, ty) ) ;
516
+ let obligation = self . mk_trait_obligation_with_new_self_ty (
517
+ obligation. param_env ,
518
+ real_trait_pred_and_ty,
519
+ ) ;
520
+
521
+ can_deref
522
+ && obligations
523
+ . iter ( )
524
+ . chain ( [ & obligation] )
525
+ . all ( |obligation| self . predicate_may_hold ( obligation) )
526
+ } ) && steps > 0
527
+ {
528
+ let derefs = "*" . repeat ( steps) ;
529
+ let msg = "consider dereferencing here" ;
530
+ let call_node = self . tcx . hir_node ( * call_hir_id) ;
531
+ let is_receiver = matches ! (
532
+ call_node,
533
+ Node :: Expr ( hir:: Expr {
534
+ kind: hir:: ExprKind :: MethodCall ( _, receiver_expr, ..) ,
535
+ ..
536
+ } )
537
+ if receiver_expr. hir_id == * arg_hir_id
538
+ ) ;
539
+ if is_receiver {
540
+ err. multipart_suggestion_verbose (
541
+ msg,
542
+ vec ! [
543
+ ( span. shrink_to_lo( ) , format!( "({derefs}" ) ) ,
544
+ ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
545
+ ] ,
546
+ Applicability :: MachineApplicable ,
547
+ )
548
+ } else {
549
+ err. span_suggestion_verbose (
550
+ span. shrink_to_lo ( ) ,
551
+ msg,
552
+ derefs,
553
+ Applicability :: MachineApplicable ,
554
+ )
555
+ } ;
556
+ return true ;
585
557
}
586
558
} else if let (
587
559
ObligationCauseCode :: BinOp { lhs_hir_id, rhs_hir_id : Some ( rhs_hir_id) , .. } ,
0 commit comments