1010
1111use borrow_check:: nll:: constraints:: { OutlivesConstraint } ;
1212use borrow_check:: nll:: region_infer:: RegionInferenceContext ;
13+ use borrow_check:: nll:: region_infer:: error_reporting:: region_name:: RegionNameSource ;
1314use borrow_check:: nll:: type_check:: Locations ;
15+ use borrow_check:: nll:: universal_regions:: DefiningTy ;
1416use rustc:: hir:: def_id:: DefId ;
1517use rustc:: infer:: error_reporting:: nice_region_error:: NiceRegionError ;
1618use rustc:: infer:: InferCtxt ;
@@ -263,6 +265,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
263265 debug ! ( "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}" ,
264266 fr_is_local, outlived_fr_is_local, category) ;
265267 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) ,
266271 ( ConstraintCategory :: Assignment , true , false ) |
267272 ( ConstraintCategory :: CallArgument , true , false ) =>
268273 self . report_escaping_data_error ( mir, infcx, mir_def_id, fr, outlived_fr,
@@ -274,6 +279,85 @@ impl<'tcx> RegionInferenceContext<'tcx> {
274279 } ;
275280 }
276281
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+ /// ```
277361 fn report_escaping_data_error (
278362 & self ,
279363 mir : & Mir < ' tcx > ,
@@ -305,31 +389,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
305389 span, & format ! ( "borrowed data escapes outside of {}" , escapes_from) ,
306390 ) ;
307391
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+ ) ;
315400 }
316401
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+ ) ;
324410
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) ) ;
328412 }
329413
330414 diag. buffer ( errors_buffer) ;
331415 }
332416
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+ /// ```
333432 fn report_general_error (
334433 & self ,
335434 mir : & Mir < ' tcx > ,
@@ -380,6 +479,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
380479 diag. buffer ( errors_buffer) ;
381480 }
382481
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+ /// ```
383491 fn add_static_impl_trait_suggestion (
384492 & self ,
385493 infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
@@ -500,4 +608,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
500608 . get ( & ( constraint. sup , constraint. sub ) ) ;
501609 * opt_span_category. unwrap_or ( & ( constraint. category , mir. source_info ( loc) . span ) )
502610 }
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+ }
503629}
0 commit comments