10
10
11
11
use borrow_check:: nll:: constraints:: { OutlivesConstraint } ;
12
12
use borrow_check:: nll:: region_infer:: RegionInferenceContext ;
13
+ use borrow_check:: nll:: region_infer:: error_reporting:: region_name:: RegionNameSource ;
13
14
use borrow_check:: nll:: type_check:: Locations ;
15
+ use borrow_check:: nll:: universal_regions:: DefiningTy ;
14
16
use rustc:: hir:: def_id:: DefId ;
15
17
use rustc:: infer:: error_reporting:: nice_region_error:: NiceRegionError ;
16
18
use rustc:: infer:: InferCtxt ;
@@ -263,6 +265,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
263
265
debug ! ( "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}" ,
264
266
fr_is_local, outlived_fr_is_local, category) ;
265
267
match ( category, fr_is_local, outlived_fr_is_local) {
268
+ ( ConstraintCategory :: Return , true , false ) if self . is_closure_fn_mut ( infcx, fr) =>
269
+ self . report_fnmut_error ( mir, infcx, mir_def_id, fr, outlived_fr, span,
270
+ errors_buffer) ,
266
271
( ConstraintCategory :: Assignment , true , false ) |
267
272
( ConstraintCategory :: CallArgument , true , false ) =>
268
273
self . report_escaping_data_error ( mir, infcx, mir_def_id, fr, outlived_fr,
@@ -274,6 +279,85 @@ impl<'tcx> RegionInferenceContext<'tcx> {
274
279
} ;
275
280
}
276
281
282
+ /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
283
+ /// This function expects `fr` to be local and `outlived_fr` to not be local.
284
+ ///
285
+ /// ```text
286
+ /// error: captured variable cannot escape `FnMut` closure body
287
+ /// --> $DIR/issue-53040.rs:15:8
288
+ /// |
289
+ /// LL | || &mut v;
290
+ /// | -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
291
+ /// | |
292
+ /// | inferred to be a `FnMut` closure
293
+ /// |
294
+ /// = note: `FnMut` closures only have access to their captured variables while they are
295
+ /// executing...
296
+ /// = note: ...therefore, returned references to captured variables will escape the closure
297
+ /// ```
298
+ fn report_fnmut_error (
299
+ & self ,
300
+ mir : & Mir < ' tcx > ,
301
+ infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
302
+ mir_def_id : DefId ,
303
+ _fr : RegionVid ,
304
+ outlived_fr : RegionVid ,
305
+ span : Span ,
306
+ errors_buffer : & mut Vec < Diagnostic > ,
307
+ ) {
308
+ let mut diag = infcx. tcx . sess . struct_span_err (
309
+ span,
310
+ "captured variable cannot escape `FnMut` closure body" ,
311
+ ) ;
312
+
313
+ // We should check if the return type of this closure is in fact a closure - in that
314
+ // case, we can special case the error further.
315
+ let return_type_is_closure = self . universal_regions . unnormalized_output_ty . is_closure ( ) ;
316
+ let message = if return_type_is_closure {
317
+ "returns a closure that contains a reference to a captured variable, which then \
318
+ escapes the closure body"
319
+ } else {
320
+ "returns a reference to a captured variable which escapes the closure body"
321
+ } ;
322
+
323
+ diag. span_label (
324
+ span,
325
+ message,
326
+ ) ;
327
+
328
+ match self . give_region_a_name ( infcx, mir, mir_def_id, outlived_fr, & mut 1 ) . source {
329
+ RegionNameSource :: NamedEarlyBoundRegion ( fr_span) |
330
+ RegionNameSource :: NamedFreeRegion ( fr_span) |
331
+ RegionNameSource :: SynthesizedFreeEnvRegion ( fr_span, _) |
332
+ RegionNameSource :: CannotMatchHirTy ( fr_span, _) |
333
+ RegionNameSource :: MatchedHirTy ( fr_span) |
334
+ RegionNameSource :: MatchedAdtAndSegment ( fr_span) |
335
+ RegionNameSource :: AnonRegionFromUpvar ( fr_span, _) |
336
+ RegionNameSource :: AnonRegionFromOutput ( fr_span, _, _) => {
337
+ diag. span_label ( fr_span, "inferred to be a `FnMut` closure" ) ;
338
+ } ,
339
+ _ => { } ,
340
+ }
341
+
342
+ diag. note ( "`FnMut` closures only have access to their captured variables while they are \
343
+ executing...") ;
344
+ diag. note ( "...therefore, they cannot allow references to captured variables to escape" ) ;
345
+
346
+ diag. buffer ( errors_buffer) ;
347
+ }
348
+
349
+ /// Reports a error specifically for when data is escaping a closure.
350
+ ///
351
+ /// ```text
352
+ /// error: borrowed data escapes outside of function
353
+ /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5
354
+ /// |
355
+ /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
356
+ /// | - `x` is a reference that is only valid in the function body
357
+ /// LL | // but ref_obj will not, so warn.
358
+ /// LL | ref_obj(x)
359
+ /// | ^^^^^^^^^^ `x` escapes the function body here
360
+ /// ```
277
361
fn report_escaping_data_error (
278
362
& self ,
279
363
mir : & Mir < ' tcx > ,
@@ -305,31 +389,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
305
389
span, & format ! ( "borrowed data escapes outside of {}" , escapes_from) ,
306
390
) ;
307
391
308
- if let Some ( ( outlived_fr_name, outlived_fr_span) ) = outlived_fr_name_and_span {
309
- if let Some ( name) = outlived_fr_name {
310
- diag. span_label (
311
- outlived_fr_span,
312
- format ! ( "`{}` is declared here, outside of the {} body" , name, escapes_from) ,
313
- ) ;
314
- }
392
+ if let Some ( ( Some ( outlived_fr_name) , outlived_fr_span) ) = outlived_fr_name_and_span {
393
+ diag. span_label (
394
+ outlived_fr_span,
395
+ format ! (
396
+ "`{}` is declared here, outside of the {} body" ,
397
+ outlived_fr_name, escapes_from
398
+ ) ,
399
+ ) ;
315
400
}
316
401
317
- if let Some ( ( fr_name, fr_span) ) = fr_name_and_span {
318
- if let Some ( name) = fr_name {
319
- diag. span_label (
320
- fr_span,
321
- format ! ( "`{}` is a reference that is only valid in the {} body" ,
322
- name, escapes_from) ,
323
- ) ;
402
+ if let Some ( ( Some ( fr_name) , fr_span) ) = fr_name_and_span {
403
+ diag. span_label (
404
+ fr_span,
405
+ format ! (
406
+ "`{}` is a reference that is only valid in the {} body" ,
407
+ fr_name, escapes_from
408
+ ) ,
409
+ ) ;
324
410
325
- diag. span_label ( span, format ! ( "`{}` escapes the {} body here" ,
326
- name, escapes_from) ) ;
327
- }
411
+ diag. span_label ( span, format ! ( "`{}` escapes the {} body here" , fr_name, escapes_from) ) ;
328
412
}
329
413
330
414
diag. buffer ( errors_buffer) ;
331
415
}
332
416
417
+ /// Reports a region inference error for the general case with named/synthesized lifetimes to
418
+ /// explain what is happening.
419
+ ///
420
+ /// ```text
421
+ /// error: unsatisfied lifetime constraints
422
+ /// --> $DIR/regions-creating-enums3.rs:17:5
423
+ /// |
424
+ /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
425
+ /// | -- -- lifetime `'b` defined here
426
+ /// | |
427
+ /// | lifetime `'a` defined here
428
+ /// LL | ast::add(x, y)
429
+ /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
430
+ /// | is returning data with lifetime `'b`
431
+ /// ```
333
432
fn report_general_error (
334
433
& self ,
335
434
mir : & Mir < ' tcx > ,
@@ -380,6 +479,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
380
479
diag. buffer ( errors_buffer) ;
381
480
}
382
481
482
+ /// Adds a suggestion to errors where a `impl Trait` is returned.
483
+ ///
484
+ /// ```text
485
+ /// help: to allow this impl Trait to capture borrowed data with lifetime `'1`, add `'_` as
486
+ /// a constraint
487
+ /// |
488
+ /// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
489
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
490
+ /// ```
383
491
fn add_static_impl_trait_suggestion (
384
492
& self ,
385
493
infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
@@ -500,4 +608,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
500
608
. get ( & ( constraint. sup , constraint. sub ) ) ;
501
609
* opt_span_category. unwrap_or ( & ( constraint. category , mir. source_info ( loc) . span ) )
502
610
}
611
+
612
+ /// Returns `true` if a closure is inferred to be an `FnMut` closure.
613
+ crate fn is_closure_fn_mut (
614
+ & self ,
615
+ infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
616
+ fr : RegionVid ,
617
+ ) -> bool {
618
+ if let Some ( ty:: ReFree ( free_region) ) = self . to_error_region ( fr) {
619
+ if let ty:: BoundRegion :: BrEnv = free_region. bound_region {
620
+ if let DefiningTy :: Closure ( def_id, substs) = self . universal_regions . defining_ty {
621
+ let closure_kind_ty = substs. closure_kind_ty ( def_id, infcx. tcx ) ;
622
+ return Some ( ty:: ClosureKind :: FnMut ) == closure_kind_ty. to_opt_closure_kind ( ) ;
623
+ }
624
+ }
625
+ }
626
+
627
+ false
628
+ }
503
629
}
0 commit comments