@@ -12,7 +12,7 @@ use rustc_middle::mir::{
1212 FakeReadCause , LocalDecl , LocalInfo , LocalKind , Location , Operand , Place , PlaceRef ,
1313 ProjectionElem , Rvalue , Statement , StatementKind , Terminator , TerminatorKind , VarBindingForm ,
1414} ;
15- use rustc_middle:: ty:: { self , suggest_constraining_type_params, PredicateKind , Ty } ;
15+ use rustc_middle:: ty:: { self , subst :: Subst , suggest_constraining_type_params, PredicateKind , Ty } ;
1616use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
1717use rustc_span:: symbol:: sym;
1818use rustc_span:: { BytePos , MultiSpan , Span } ;
@@ -151,6 +151,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
151151 . args_or_use ( )
152152 } )
153153 . collect :: < Vec < Span > > ( ) ;
154+
154155 let reinits = maybe_reinitialized_locations. len ( ) ;
155156 if reinits == 1 {
156157 err. span_label ( reinit_spans[ 0 ] , "this reinitialization might get skipped" ) ;
@@ -276,76 +277,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
276277 }
277278 }
278279
279- if needs_note {
280- let opt_name =
281- self . describe_place_with_options ( place. as_ref ( ) , IncludingDowncast ( true ) ) ;
282- let note_msg = match opt_name {
283- Some ( ref name) => format ! ( "`{}`" , name) ,
284- None => "value" . to_owned ( ) ,
285- } ;
286-
287- // Try to find predicates on *generic params* that would allow copying `ty`
288- let tcx = self . infcx . tcx ;
289- let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
290- if let Some ( hir_generics) = tcx
291- . typeck_root_def_id ( self . mir_def_id ( ) . to_def_id ( ) )
292- . as_local ( )
293- . and_then ( |def_id| tcx. hir ( ) . get_generics ( def_id) )
294- {
295- let predicates: Result < Vec < _ > , _ > = tcx. infer_ctxt ( ) . enter ( |infcx| {
296- let mut fulfill_cx =
297- <dyn rustc_infer:: traits:: TraitEngine < ' _ > >:: new ( infcx. tcx ) ;
298-
299- let copy_did = infcx. tcx . lang_items ( ) . copy_trait ( ) . unwrap ( ) ;
300- let cause = ObligationCause :: new (
301- span,
302- self . mir_hir_id ( ) ,
303- rustc_infer:: traits:: ObligationCauseCode :: MiscObligation ,
304- ) ;
305- fulfill_cx. register_bound (
306- & infcx,
307- self . param_env ,
308- // Erase any region vids from the type, which may not be resolved
309- infcx. tcx . erase_regions ( ty) ,
310- copy_did,
311- cause,
312- ) ;
313- // Select all, including ambiguous predicates
314- let errors = fulfill_cx. select_all_or_error ( & infcx) ;
315-
316- // Only emit suggestion if all required predicates are on generic
317- errors
318- . into_iter ( )
319- . map ( |err| match err. obligation . predicate . kind ( ) . skip_binder ( ) {
320- PredicateKind :: Trait ( predicate) => {
321- match predicate. self_ty ( ) . kind ( ) {
322- ty:: Param ( param_ty) => Ok ( (
323- generics. type_param ( param_ty, tcx) ,
324- predicate. trait_ref . print_only_trait_path ( ) . to_string ( ) ,
325- ) ) ,
326- _ => Err ( ( ) ) ,
327- }
328- }
329- _ => Err ( ( ) ) ,
330- } )
331- . collect ( )
332- } ) ;
333-
334- if let Ok ( predicates) = predicates {
335- suggest_constraining_type_params (
336- tcx,
337- hir_generics,
338- & mut err,
339- predicates. iter ( ) . map ( |( param, constraint) | {
340- ( param. name . as_str ( ) , & * * constraint, None )
341- } ) ,
342- ) ;
343- }
344- }
280+ let opt_name =
281+ self . describe_place_with_options ( place. as_ref ( ) , IncludingDowncast ( true ) ) ;
282+ let note_msg = match opt_name {
283+ Some ( ref name) => format ! ( "`{}`" , name) ,
284+ None => "value" . to_owned ( ) ,
285+ } ;
286+ if self . suggest_borrow_fn_like ( & mut err, ty, & move_site_vec, & note_msg) {
287+ // Suppress the next suggestion since we don't want to put more bounds onto
288+ // something that already has `Fn`-like bounds (or is a closure), so we can't
289+ // restrict anyways.
290+ } else {
291+ self . suggest_adding_copy_bounds ( & mut err, ty, span) ;
292+ }
345293
294+ if needs_note {
346295 let span = if let Some ( local) = place. as_local ( ) {
347- let decl = & self . body . local_decls [ local] ;
348- Some ( decl. source_info . span )
296+ Some ( self . body . local_decls [ local] . source_info . span )
349297 } else {
350298 None
351299 } ;
@@ -373,6 +321,144 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
373321 }
374322 }
375323
324+ fn suggest_borrow_fn_like (
325+ & self ,
326+ err : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
327+ ty : Ty < ' tcx > ,
328+ move_sites : & [ MoveSite ] ,
329+ value_name : & str ,
330+ ) -> bool {
331+ let tcx = self . infcx . tcx ;
332+
333+ // Find out if the predicates show that the type is a Fn or FnMut
334+ let find_fn_kind_from_did = |predicates : & [ ( ty:: Predicate < ' tcx > , Span ) ] , substs| {
335+ predicates. iter ( ) . find_map ( |( pred, _) | {
336+ let pred = if let Some ( substs) = substs {
337+ pred. subst ( tcx, substs) . kind ( ) . skip_binder ( )
338+ } else {
339+ pred. kind ( ) . skip_binder ( )
340+ } ;
341+ if let ty:: PredicateKind :: Trait ( pred) = pred && pred. self_ty ( ) == ty {
342+ if Some ( pred. def_id ( ) ) == tcx. lang_items ( ) . fn_trait ( ) {
343+ return Some ( hir:: Mutability :: Not ) ;
344+ } else if Some ( pred. def_id ( ) ) == tcx. lang_items ( ) . fn_mut_trait ( ) {
345+ return Some ( hir:: Mutability :: Mut ) ;
346+ }
347+ }
348+ None
349+ } )
350+ } ;
351+
352+ // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
353+ // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
354+ // These types seem reasonably opaque enough that they could be substituted with their
355+ // borrowed variants in a function body when we see a move error.
356+ let borrow_level = match ty. kind ( ) {
357+ ty:: Param ( _) => find_fn_kind_from_did (
358+ tcx. explicit_predicates_of ( self . mir_def_id ( ) . to_def_id ( ) ) . predicates ,
359+ None ,
360+ ) ,
361+ ty:: Opaque ( did, substs) => {
362+ find_fn_kind_from_did ( tcx. explicit_item_bounds ( * did) , Some ( * substs) )
363+ }
364+ ty:: Closure ( _, substs) => match substs. as_closure ( ) . kind ( ) {
365+ ty:: ClosureKind :: Fn => Some ( hir:: Mutability :: Not ) ,
366+ ty:: ClosureKind :: FnMut => Some ( hir:: Mutability :: Mut ) ,
367+ _ => None ,
368+ } ,
369+ _ => None ,
370+ } ;
371+
372+ let Some ( borrow_level) = borrow_level else { return false ; } ;
373+ let sugg = move_sites
374+ . iter ( )
375+ . map ( |move_site| {
376+ let move_out = self . move_data . moves [ ( * move_site) . moi ] ;
377+ let moved_place = & self . move_data . move_paths [ move_out. path ] . place ;
378+ let move_spans = self . move_spans ( moved_place. as_ref ( ) , move_out. source ) ;
379+ let move_span = move_spans. args_or_use ( ) ;
380+ let suggestion = if borrow_level == hir:: Mutability :: Mut {
381+ "&mut " . to_string ( )
382+ } else {
383+ "&" . to_string ( )
384+ } ;
385+ ( move_span. shrink_to_lo ( ) , suggestion)
386+ } )
387+ . collect ( ) ;
388+ err. multipart_suggestion_verbose (
389+ & format ! (
390+ "consider {}borrowing {value_name}" ,
391+ if borrow_level == hir:: Mutability :: Mut { "mutably " } else { "" }
392+ ) ,
393+ sugg,
394+ Applicability :: MaybeIncorrect ,
395+ ) ;
396+ true
397+ }
398+
399+ fn suggest_adding_copy_bounds (
400+ & self ,
401+ err : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
402+ ty : Ty < ' tcx > ,
403+ span : Span ,
404+ ) {
405+ let tcx = self . infcx . tcx ;
406+ let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
407+
408+ let Some ( hir_generics) = tcx
409+ . typeck_root_def_id ( self . mir_def_id ( ) . to_def_id ( ) )
410+ . as_local ( )
411+ . and_then ( |def_id| tcx. hir ( ) . get_generics ( def_id) )
412+ else { return ; } ;
413+ // Try to find predicates on *generic params* that would allow copying `ty`
414+ let predicates: Result < Vec < _ > , _ > = tcx. infer_ctxt ( ) . enter ( |infcx| {
415+ let mut fulfill_cx = <dyn rustc_infer:: traits:: TraitEngine < ' _ > >:: new ( infcx. tcx ) ;
416+
417+ let copy_did = infcx. tcx . lang_items ( ) . copy_trait ( ) . unwrap ( ) ;
418+ let cause = ObligationCause :: new (
419+ span,
420+ self . mir_hir_id ( ) ,
421+ rustc_infer:: traits:: ObligationCauseCode :: MiscObligation ,
422+ ) ;
423+ fulfill_cx. register_bound (
424+ & infcx,
425+ self . param_env ,
426+ // Erase any region vids from the type, which may not be resolved
427+ infcx. tcx . erase_regions ( ty) ,
428+ copy_did,
429+ cause,
430+ ) ;
431+ // Select all, including ambiguous predicates
432+ let errors = fulfill_cx. select_all_or_error ( & infcx) ;
433+
434+ // Only emit suggestion if all required predicates are on generic
435+ errors
436+ . into_iter ( )
437+ . map ( |err| match err. obligation . predicate . kind ( ) . skip_binder ( ) {
438+ PredicateKind :: Trait ( predicate) => match predicate. self_ty ( ) . kind ( ) {
439+ ty:: Param ( param_ty) => Ok ( (
440+ generics. type_param ( param_ty, tcx) ,
441+ predicate. trait_ref . print_only_trait_path ( ) . to_string ( ) ,
442+ ) ) ,
443+ _ => Err ( ( ) ) ,
444+ } ,
445+ _ => Err ( ( ) ) ,
446+ } )
447+ . collect ( )
448+ } ) ;
449+
450+ if let Ok ( predicates) = predicates {
451+ suggest_constraining_type_params (
452+ tcx,
453+ hir_generics,
454+ err,
455+ predicates
456+ . iter ( )
457+ . map ( |( param, constraint) | ( param. name . as_str ( ) , & * * constraint, None ) ) ,
458+ ) ;
459+ }
460+ }
461+
376462 pub ( crate ) fn report_move_out_while_borrowed (
377463 & mut self ,
378464 location : Location ,
0 commit comments