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