@@ -12,7 +12,7 @@ use rustc_middle::mir::{
12
12
FakeReadCause , LocalDecl , LocalInfo , LocalKind , Location , Operand , Place , PlaceRef ,
13
13
ProjectionElem , Rvalue , Statement , StatementKind , Terminator , TerminatorKind , VarBindingForm ,
14
14
} ;
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 } ;
16
16
use rustc_mir_dataflow:: move_paths:: { InitKind , MoveOutIndex , MovePathIndex } ;
17
17
use rustc_span:: symbol:: sym;
18
18
use rustc_span:: { BytePos , MultiSpan , Span } ;
@@ -276,22 +276,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
276
276
}
277
277
}
278
278
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
- } ;
279
+ let opt_name =
280
+ self . describe_place_with_options ( place. as_ref ( ) , IncludingDowncast ( true ) ) ;
281
+ let note_msg = match opt_name {
282
+ Some ( ref name) => format ! ( "`{}`" , name) ,
283
+ None => "value" . to_owned ( ) ,
284
+ } ;
285
+
286
+ let tcx = self . infcx . tcx ;
287
+ let generics = tcx. generics_of ( self . mir_def_id ( ) ) ;
286
288
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 ( ) ) ;
289
+ if self . suggest_borrow_fn_like ( & mut err , ty , & move_site_vec , & note_msg ) {
290
+ // Suppress the next note, since we don't want to put more `Fn`-like bounds onto something that already has them
291
+ } else if needs_note {
290
292
if let Some ( hir_generics) = tcx
291
293
. typeck_root_def_id ( self . mir_def_id ( ) . to_def_id ( ) )
292
294
. as_local ( )
293
295
. and_then ( |def_id| tcx. hir ( ) . get_generics ( def_id) )
294
296
{
297
+ // Try to find predicates on *generic params* that would allow copying `ty`
295
298
let predicates: Result < Vec < _ > , _ > = tcx. infer_ctxt ( ) . enter ( |infcx| {
296
299
let mut fulfill_cx =
297
300
<dyn rustc_infer:: traits:: TraitEngine < ' _ > >:: new ( infcx. tcx ) ;
@@ -344,8 +347,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
344
347
}
345
348
346
349
let span = if let Some ( local) = place. as_local ( ) {
347
- let decl = & self . body . local_decls [ local] ;
348
- Some ( decl. source_info . span )
350
+ Some ( self . body . local_decls [ local] . source_info . span )
349
351
} else {
350
352
None
351
353
} ;
@@ -373,6 +375,81 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
373
375
}
374
376
}
375
377
378
+ fn suggest_borrow_fn_like (
379
+ & self ,
380
+ err : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
381
+ ty : Ty < ' tcx > ,
382
+ move_sites : & [ MoveSite ] ,
383
+ value_name : & str ,
384
+ ) -> bool {
385
+ let tcx = self . infcx . tcx ;
386
+
387
+ // Find out if the predicates show that the type is a Fn or FnMut
388
+ let find_fn_kind_from_did = |predicates : & [ ( ty:: Predicate < ' tcx > , Span ) ] , substs| {
389
+ predicates. iter ( ) . find_map ( |( pred, _) | {
390
+ let pred = if let Some ( substs) = substs {
391
+ pred. subst ( tcx, substs) . kind ( ) . skip_binder ( )
392
+ } else {
393
+ pred. kind ( ) . skip_binder ( )
394
+ } ;
395
+ if let ty:: PredicateKind :: Trait ( pred) = pred && pred. self_ty ( ) == ty {
396
+ if Some ( pred. def_id ( ) ) == tcx. lang_items ( ) . fn_trait ( ) {
397
+ return Some ( hir:: Mutability :: Not ) ;
398
+ } else if Some ( pred. def_id ( ) ) == tcx. lang_items ( ) . fn_mut_trait ( ) {
399
+ return Some ( hir:: Mutability :: Mut ) ;
400
+ }
401
+ }
402
+ None
403
+ } )
404
+ } ;
405
+
406
+ // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
407
+ // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
408
+ // These types seem reasonably opaque enough that they could be substituted with their
409
+ // borrowed variants in a function body when we see a move error.
410
+ let borrow_level = match ty. kind ( ) {
411
+ ty:: Param ( _) => find_fn_kind_from_did (
412
+ tcx. explicit_predicates_of ( self . mir_def_id ( ) . to_def_id ( ) ) . predicates ,
413
+ None ,
414
+ ) ,
415
+ ty:: Opaque ( did, substs) => {
416
+ find_fn_kind_from_did ( tcx. explicit_item_bounds ( * did) , Some ( * substs) )
417
+ }
418
+ ty:: Closure ( _, substs) => match substs. as_closure ( ) . kind ( ) {
419
+ ty:: ClosureKind :: Fn => Some ( hir:: Mutability :: Not ) ,
420
+ ty:: ClosureKind :: FnMut => Some ( hir:: Mutability :: Mut ) ,
421
+ _ => None ,
422
+ } ,
423
+ _ => None ,
424
+ } ;
425
+
426
+ let Some ( borrow_level) = borrow_level else { return false ; } ;
427
+ let sugg = move_sites
428
+ . iter ( )
429
+ . map ( |move_site| {
430
+ let move_out = self . move_data . moves [ ( * move_site) . moi ] ;
431
+ let moved_place = & self . move_data . move_paths [ move_out. path ] . place ;
432
+ let move_spans = self . move_spans ( moved_place. as_ref ( ) , move_out. source ) ;
433
+ let move_span = move_spans. args_or_use ( ) ;
434
+ let suggestion = if borrow_level == hir:: Mutability :: Mut {
435
+ "&mut " . to_string ( )
436
+ } else {
437
+ "&" . to_string ( )
438
+ } ;
439
+ ( move_span. shrink_to_lo ( ) , suggestion)
440
+ } )
441
+ . collect ( ) ;
442
+ err. multipart_suggestion_verbose (
443
+ & format ! (
444
+ "consider {}borrowing {value_name}" ,
445
+ if borrow_level == hir:: Mutability :: Mut { "mutably " } else { "" }
446
+ ) ,
447
+ sugg,
448
+ Applicability :: MaybeIncorrect ,
449
+ ) ;
450
+ true
451
+ }
452
+
376
453
pub ( crate ) fn report_move_out_while_borrowed (
377
454
& mut self ,
378
455
location : Location ,
0 commit comments