@@ -445,9 +445,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
445445 }
446446 }
447447
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.
451450 pub ( super ) fn suggest_dereferences (
452451 & self ,
453452 obligation : & PredicateObligation < ' tcx > ,
@@ -461,127 +460,100 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
461460 && let Some ( arg_ty) = typeck_results. expr_ty_adjusted_opt ( expr)
462461 {
463462 // 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)
464465 let mut real_trait_pred = trait_pred;
465466 while let Some ( ( parent_code, parent_trait_pred) ) = code. parent ( ) {
466467 code = parent_code;
467468 if let Some ( parent_trait_pred) = parent_trait_pred {
468469 real_trait_pred = parent_trait_pred;
469470 }
471+ }
470472
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+ }
475479
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 ( ) =>
478489 {
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+ } ;
497496
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 ) ;
584511 }
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 ;
585557 }
586558 } else if let (
587559 ObligationCauseCode :: BinOp { lhs_hir_id, rhs_hir_id : Some ( rhs_hir_id) , .. } ,
0 commit comments