@@ -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 } ;
@@ -151,6 +151,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
151
151
. args_or_use ( )
152
152
} )
153
153
. collect :: < Vec < Span > > ( ) ;
154
+
154
155
let reinits = maybe_reinitialized_locations. len ( ) ;
155
156
if reinits == 1 {
156
157
err. span_label ( reinit_spans[ 0 ] , "this reinitialization might get skipped" ) ;
@@ -276,76 +277,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
276
277
}
277
278
}
278
279
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
+ }
345
293
294
+ if needs_note {
346
295
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 )
349
297
} else {
350
298
None
351
299
} ;
@@ -373,6 +321,144 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
373
321
}
374
322
}
375
323
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
+
376
462
pub ( crate ) fn report_move_out_while_borrowed (
377
463
& mut self ,
378
464
location : Location ,
0 commit comments