@@ -443,9 +443,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
443443 }
444444 }
445445
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.
449448 pub ( super ) fn suggest_dereferences (
450449 & self ,
451450 obligation : & PredicateObligation < ' tcx > ,
@@ -459,127 +458,100 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
459458 && let Some ( arg_ty) = typeck_results. expr_ty_adjusted_opt ( expr)
460459 {
461460 // 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)
462463 let mut real_trait_pred = trait_pred;
463464 while let Some ( ( parent_code, parent_trait_pred) ) = code. parent ( ) {
464465 code = parent_code;
465466 if let Some ( parent_trait_pred) = parent_trait_pred {
466467 real_trait_pred = parent_trait_pred;
467468 }
469+ }
468470
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+ }
473477
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 ( ) =>
476487 {
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+ } ;
495494
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 ) ;
582509 }
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 ;
583555 }
584556 } else if let (
585557 ObligationCauseCode :: BinOp { lhs_hir_id, rhs_hir_id : Some ( rhs_hir_id) , .. } ,
0 commit comments