]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
Give an error number for "borrowed data escapes outside of closure"
[rust.git] / src / librustc_mir / borrow_check / nll / region_infer / error_reporting / mod.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use borrow_check::nll::constraints::{OutlivesConstraint};
12 use borrow_check::nll::region_infer::RegionInferenceContext;
13 use borrow_check::nll::region_infer::error_reporting::region_name::RegionNameSource;
14 use borrow_check::nll::type_check::Locations;
15 use borrow_check::nll::universal_regions::DefiningTy;
16 use util::borrowck_errors::{BorrowckErrors, Origin};
17 use rustc::hir::def_id::DefId;
18 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
19 use rustc::infer::InferCtxt;
20 use rustc::mir::{ConstraintCategory, Location, Mir};
21 use rustc::ty::{self, RegionVid};
22 use rustc_data_structures::indexed_vec::IndexVec;
23 use rustc_errors::{Diagnostic, DiagnosticBuilder};
24 use std::collections::VecDeque;
25 use syntax::symbol::keywords;
26 use syntax_pos::Span;
27 use syntax::errors::Applicability;
28
29 mod region_name;
30 mod var_name;
31
32 use self::region_name::RegionName;
33
34 trait ConstraintDescription {
35     fn description(&self) -> &'static str;
36 }
37
38 impl ConstraintDescription for ConstraintCategory {
39     fn description(&self) -> &'static str {
40         // Must end with a space. Allows for empty names to be provided.
41         match self {
42             ConstraintCategory::Assignment => "assignment ",
43             ConstraintCategory::Return => "returning this value ",
44             ConstraintCategory::UseAsConst => "using this value as a constant ",
45             ConstraintCategory::UseAsStatic => "using this value as a static ",
46             ConstraintCategory::Cast => "cast ",
47             ConstraintCategory::CallArgument => "argument ",
48             ConstraintCategory::TypeAnnotation => "type annotation ",
49             ConstraintCategory::ClosureBounds => "closure body ",
50             ConstraintCategory::SizedBound => "proving this value is `Sized` ",
51             ConstraintCategory::CopyBound => "copying this value ",
52             ConstraintCategory::OpaqueType => "opaque type ",
53             ConstraintCategory::Boring
54             | ConstraintCategory::BoringNoLocation
55             | ConstraintCategory::Internal => "",
56         }
57     }
58 }
59
60 #[derive(Copy, Clone, PartialEq, Eq)]
61 enum Trace {
62     StartRegion,
63     FromOutlivesConstraint(OutlivesConstraint),
64     NotVisited,
65 }
66
67 impl<'tcx> RegionInferenceContext<'tcx> {
68     /// Tries to find the best constraint to blame for the fact that
69     /// `R: from_region`, where `R` is some region that meets
70     /// `target_test`. This works by following the constraint graph,
71     /// creating a constraint path that forces `R` to outlive
72     /// `from_region`, and then finding the best choices within that
73     /// path to blame.
74     fn best_blame_constraint(
75         &self,
76         mir: &Mir<'tcx>,
77         from_region: RegionVid,
78         target_test: impl Fn(RegionVid) -> bool,
79     ) -> (ConstraintCategory, Span, RegionVid) {
80         debug!("best_blame_constraint(from_region={:?})", from_region);
81
82         // Find all paths
83         let (path, target_region) = self
84             .find_constraint_paths_between_regions(from_region, target_test)
85             .unwrap();
86         debug!(
87             "best_blame_constraint: path={:#?}",
88             path.iter()
89                 .map(|&c| format!(
90                     "{:?} ({:?}: {:?})",
91                     c,
92                     self.constraint_sccs.scc(c.sup),
93                     self.constraint_sccs.scc(c.sub),
94                 ))
95                 .collect::<Vec<_>>()
96         );
97
98         // Classify each of the constraints along the path.
99         let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
100             .iter()
101             .map(|constraint| {
102                 if constraint.category == ConstraintCategory::ClosureBounds {
103                     self.retrieve_closure_constraint_info(mir, &constraint)
104                 } else {
105                     (constraint.category, constraint.locations.span(mir))
106                 }
107             })
108             .collect();
109         debug!(
110             "best_blame_constraint: categorized_path={:#?}",
111             categorized_path
112         );
113
114         // To find the best span to cite, we first try to look for the
115         // final constraint that is interesting and where the `sup` is
116         // not unified with the ultimate target region. The reason
117         // for this is that we have a chain of constraints that lead
118         // from the source to the target region, something like:
119         //
120         //    '0: '1 ('0 is the source)
121         //    '1: '2
122         //    '2: '3
123         //    '3: '4
124         //    '4: '5
125         //    '5: '6 ('6 is the target)
126         //
127         // Some of those regions are unified with `'6` (in the same
128         // SCC).  We want to screen those out. After that point, the
129         // "closest" constraint we have to the end is going to be the
130         // most likely to be the point where the value escapes -- but
131         // we still want to screen for an "interesting" point to
132         // highlight (e.g., a call site or something).
133         let target_scc = self.constraint_sccs.scc(target_region);
134         let best_choice = (0..path.len()).rev().find(|&i| {
135             let constraint = path[i];
136
137             let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
138
139             match categorized_path[i].0 {
140                 ConstraintCategory::OpaqueType
141                 | ConstraintCategory::Boring
142                 | ConstraintCategory::BoringNoLocation
143                 | ConstraintCategory::Internal => false,
144                 ConstraintCategory::TypeAnnotation
145                 | ConstraintCategory::Return => true,
146                 _ => constraint_sup_scc != target_scc,
147             }
148         });
149         if let Some(i) = best_choice {
150             let (category, span) = categorized_path[i];
151             return (category, span, target_region);
152         }
153
154         // If that search fails, that is.. unusual. Maybe everything
155         // is in the same SCC or something. In that case, find what
156         // appears to be the most interesting point to report to the
157         // user via an even more ad-hoc guess.
158         categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
159         debug!("best_blame_constraint: sorted_path={:#?}", categorized_path);
160
161         let &(category, span) = categorized_path.first().unwrap();
162
163         (category, span, target_region)
164     }
165
166     /// Walks the graph of constraints (where `'a: 'b` is considered
167     /// an edge `'a -> 'b`) to find all paths from `from_region` to
168     /// `to_region`. The paths are accumulated into the vector
169     /// `results`. The paths are stored as a series of
170     /// `ConstraintIndex` values -- in other words, a list of *edges*.
171     ///
172     /// Returns: a series of constraints as well as the region `R`
173     /// that passed the target test.
174     fn find_constraint_paths_between_regions(
175         &self,
176         from_region: RegionVid,
177         target_test: impl Fn(RegionVid) -> bool,
178     ) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
179         let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
180         context[from_region] = Trace::StartRegion;
181
182         // Use a deque so that we do a breadth-first search. We will
183         // stop at the first match, which ought to be the shortest
184         // path (fewest constraints).
185         let mut deque = VecDeque::new();
186         deque.push_back(from_region);
187
188         while let Some(r) = deque.pop_front() {
189             // Check if we reached the region we were looking for. If so,
190             // we can reconstruct the path that led to it and return it.
191             if target_test(r) {
192                 let mut result = vec![];
193                 let mut p = r;
194                 loop {
195                     match context[p] {
196                         Trace::NotVisited => {
197                             bug!("found unvisited region {:?} on path to {:?}", p, r)
198                         }
199                         Trace::FromOutlivesConstraint(c) => {
200                             result.push(c);
201                             p = c.sup;
202                         }
203
204                         Trace::StartRegion => {
205                             result.reverse();
206                             return Some((result, r));
207                         }
208                     }
209                 }
210             }
211
212             // Otherwise, walk over the outgoing constraints and
213             // enqueue any regions we find, keeping track of how we
214             // reached them.
215             let fr_static = self.universal_regions.fr_static;
216             for constraint in self.constraint_graph.outgoing_edges(r,
217                                                                    &self.constraints,
218                                                                    fr_static) {
219                 assert_eq!(constraint.sup, r);
220                 let sub_region = constraint.sub;
221                 if let Trace::NotVisited = context[sub_region] {
222                     context[sub_region] = Trace::FromOutlivesConstraint(constraint);
223                     deque.push_back(sub_region);
224                 }
225             }
226         }
227
228         None
229     }
230
231     /// Report an error because the universal region `fr` was required to outlive
232     /// `outlived_fr` but it is not known to do so. For example:
233     ///
234     /// ```
235     /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
236     /// ```
237     ///
238     /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
239     pub(super) fn report_error(
240         &self,
241         mir: &Mir<'tcx>,
242         infcx: &InferCtxt<'_, '_, 'tcx>,
243         mir_def_id: DefId,
244         fr: RegionVid,
245         outlived_fr: RegionVid,
246         errors_buffer: &mut Vec<Diagnostic>,
247     ) {
248         debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
249
250         let (category, span, _) = self.best_blame_constraint(
251             mir,
252             fr,
253             |r| r == outlived_fr
254         );
255
256         // Check if we can use one of the "nice region errors".
257         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
258             let tables = infcx.tcx.typeck_tables_of(mir_def_id);
259             let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables));
260             if let Some(_error_reported) = nice.try_report_from_nll() {
261                 return;
262             }
263         }
264
265         let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
266             self.universal_regions.is_local_free_region(fr),
267             self.universal_regions.is_local_free_region(outlived_fr),
268         );
269
270         debug!("report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
271                fr_is_local, outlived_fr_is_local, category);
272         match (category, fr_is_local, outlived_fr_is_local) {
273             (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) =>
274                 self.report_fnmut_error(mir, infcx, mir_def_id, fr, outlived_fr, span,
275                                         errors_buffer),
276             (ConstraintCategory::Assignment, true, false) |
277             (ConstraintCategory::CallArgument, true, false) =>
278                 self.report_escaping_data_error(mir, infcx, mir_def_id, fr, outlived_fr,
279                                                 category, span, errors_buffer),
280             _ =>
281                 self.report_general_error(mir, infcx, mir_def_id, fr, fr_is_local,
282                                           outlived_fr, outlived_fr_is_local,
283                                           category, span, errors_buffer),
284         };
285     }
286
287     /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
288     /// This function expects `fr` to be local and `outlived_fr` to not be local.
289     ///
290     /// ```text
291     /// error: captured variable cannot escape `FnMut` closure body
292     ///   --> $DIR/issue-53040.rs:15:8
293     ///    |
294     /// LL |     || &mut v;
295     ///    |     -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
296     ///    |     |
297     ///    |     inferred to be a `FnMut` closure
298     ///    |
299     ///    = note: `FnMut` closures only have access to their captured variables while they are
300     ///            executing...
301     ///    = note: ...therefore, returned references to captured variables will escape the closure
302     /// ```
303     fn report_fnmut_error(
304         &self,
305         mir: &Mir<'tcx>,
306         infcx: &InferCtxt<'_, '_, 'tcx>,
307         mir_def_id: DefId,
308         _fr: RegionVid,
309         outlived_fr: RegionVid,
310         span: Span,
311         errors_buffer: &mut Vec<Diagnostic>,
312     ) {
313         let mut diag = infcx.tcx.sess.struct_span_err(
314             span,
315             "captured variable cannot escape `FnMut` closure body",
316         );
317
318         // We should check if the return type of this closure is in fact a closure - in that
319         // case, we can special case the error further.
320         let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure();
321         let message = if return_type_is_closure {
322             "returns a closure that contains a reference to a captured variable, which then \
323              escapes the closure body"
324         } else {
325             "returns a reference to a captured variable which escapes the closure body"
326         };
327
328         diag.span_label(
329             span,
330             message,
331         );
332
333         match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1).source {
334             RegionNameSource::NamedEarlyBoundRegion(fr_span) |
335             RegionNameSource::NamedFreeRegion(fr_span) |
336             RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) |
337             RegionNameSource::CannotMatchHirTy(fr_span, _) |
338             RegionNameSource::MatchedHirTy(fr_span) |
339             RegionNameSource::MatchedAdtAndSegment(fr_span) |
340             RegionNameSource::AnonRegionFromUpvar(fr_span, _) |
341             RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => {
342                 diag.span_label(fr_span, "inferred to be a `FnMut` closure");
343             },
344             _ => {},
345         }
346
347         diag.note("`FnMut` closures only have access to their captured variables while they are \
348                    executing...");
349         diag.note("...therefore, they cannot allow references to captured variables to escape");
350
351         diag.buffer(errors_buffer);
352     }
353
354     /// Reports a error specifically for when data is escaping a closure.
355     ///
356     /// ```text
357     /// error: borrowed data escapes outside of function
358     ///   --> $DIR/lifetime-bound-will-change-warning.rs:44:5
359     ///    |
360     /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
361     ///    |              - `x` is a reference that is only valid in the function body
362     /// LL |     // but ref_obj will not, so warn.
363     /// LL |     ref_obj(x)
364     ///    |     ^^^^^^^^^^ `x` escapes the function body here
365     /// ```
366     fn report_escaping_data_error(
367         &self,
368         mir: &Mir<'tcx>,
369         infcx: &InferCtxt<'_, '_, 'tcx>,
370         mir_def_id: DefId,
371         fr: RegionVid,
372         outlived_fr: RegionVid,
373         category: ConstraintCategory,
374         span: Span,
375         errors_buffer: &mut Vec<Diagnostic>,
376     ) {
377         let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
378         let outlived_fr_name_and_span =
379             self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr);
380
381         let escapes_from = match self.universal_regions.defining_ty {
382             DefiningTy::Closure(..) => "closure",
383             DefiningTy::Generator(..) => "generator",
384             DefiningTy::FnDef(..) => "function",
385             DefiningTy::Const(..) => "const"
386         };
387
388         // Revert to the normal error in these cases.
389         // Assignments aren't "escapes" in function items.
390         if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
391             || (category == ConstraintCategory::Assignment && escapes_from == "function")
392             || escapes_from == "const"
393         {
394             return self.report_general_error(mir, infcx, mir_def_id,
395                                              fr, true, outlived_fr, false,
396                                              category, span, errors_buffer);
397         }
398
399         let mut diag = infcx.tcx.borrowed_data_escapes_closure(span, escapes_from, Origin::Mir);
400
401         if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
402             diag.span_label(
403                 outlived_fr_span,
404                 format!(
405                     "`{}` is declared here, outside of the {} body",
406                     outlived_fr_name, escapes_from
407                 ),
408             );
409         }
410
411         if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
412             diag.span_label(
413                 fr_span,
414                 format!(
415                     "`{}` is a reference that is only valid in the {} body",
416                     fr_name, escapes_from
417                 ),
418             );
419
420             diag.span_label(span, format!("`{}` escapes the {} body here", fr_name, escapes_from));
421         }
422
423         diag.buffer(errors_buffer);
424     }
425
426     /// Reports a region inference error for the general case with named/synthesized lifetimes to
427     /// explain what is happening.
428     ///
429     /// ```text
430     /// error: unsatisfied lifetime constraints
431     ///   --> $DIR/regions-creating-enums3.rs:17:5
432     ///    |
433     /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
434     ///    |                -- -- lifetime `'b` defined here
435     ///    |                |
436     ///    |                lifetime `'a` defined here
437     /// LL |     ast::add(x, y)
438     ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
439     ///    |                    is returning data with lifetime `'b`
440     /// ```
441     fn report_general_error(
442         &self,
443         mir: &Mir<'tcx>,
444         infcx: &InferCtxt<'_, '_, 'tcx>,
445         mir_def_id: DefId,
446         fr: RegionVid,
447         fr_is_local: bool,
448         outlived_fr: RegionVid,
449         outlived_fr_is_local: bool,
450         category: ConstraintCategory,
451         span: Span,
452         errors_buffer: &mut Vec<Diagnostic>,
453     ) {
454         let mut diag = infcx.tcx.sess.struct_span_err(
455             span,
456             "unsatisfied lifetime constraints", // FIXME
457         );
458
459         let counter = &mut 1;
460         let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter);
461         fr_name.highlight_region_name(&mut diag);
462         let outlived_fr_name = self.give_region_a_name(
463             infcx, mir, mir_def_id, outlived_fr, counter);
464         outlived_fr_name.highlight_region_name(&mut diag);
465
466         let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" };
467
468         match (category, outlived_fr_is_local, fr_is_local) {
469             (ConstraintCategory::Return, true, _) => {
470                 diag.span_label(span, format!(
471                     "{} was supposed to return data with lifetime `{}` but it is returning \
472                     data with lifetime `{}`",
473                     mir_def_name, outlived_fr_name, fr_name
474                 ));
475             },
476             _ => {
477                 diag.span_label(span, format!(
478                     "{}requires that `{}` must outlive `{}`",
479                     category.description(), fr_name, outlived_fr_name,
480                 ));
481             },
482         }
483
484         self.add_static_impl_trait_suggestion(
485             infcx, &mut diag, fr, fr_name, outlived_fr,
486         );
487
488         diag.buffer(errors_buffer);
489     }
490
491     /// Adds a suggestion to errors where a `impl Trait` is returned.
492     ///
493     /// ```text
494     /// help: to allow this impl Trait to capture borrowed data with lifetime `'1`, add `'_` as
495     ///       a constraint
496     ///    |
497     /// LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
498     ///    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
499     /// ```
500     fn add_static_impl_trait_suggestion(
501         &self,
502         infcx: &InferCtxt<'_, '_, 'tcx>,
503         diag: &mut DiagnosticBuilder<'_>,
504         fr: RegionVid,
505         // We need to pass `fr_name` - computing it again will label it twice.
506         fr_name: RegionName,
507         outlived_fr: RegionVid,
508     ) {
509         if let (
510             Some(f),
511             Some(ty::RegionKind::ReStatic)
512         ) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
513             if let Some(ty::TyS {
514                 sty: ty::TyKind::Opaque(did, substs),
515                 ..
516             }) = infcx.tcx.is_suitable_region(f)
517                     .map(|r| r.def_id)
518                     .map(|id| infcx.tcx.return_type_impl_trait(id))
519                     .unwrap_or(None)
520             {
521                 // Check whether or not the impl trait return type is intended to capture
522                 // data with the static lifetime.
523                 //
524                 // eg. check for `impl Trait + 'static` instead of `impl Trait`.
525                 let has_static_predicate = {
526                     let predicates_of = infcx.tcx.predicates_of(*did);
527                     let bounds = predicates_of.instantiate(infcx.tcx, substs);
528
529                     let mut found = false;
530                     for predicate in bounds.predicates {
531                         if let ty::Predicate::TypeOutlives(binder) = predicate {
532                             if let ty::OutlivesPredicate(
533                                 _,
534                                 ty::RegionKind::ReStatic
535                             ) = binder.skip_binder() {
536                                 found = true;
537                                 break;
538                             }
539                         }
540                     }
541
542                     found
543                 };
544
545                 debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}",
546                        has_static_predicate);
547                 let static_str = keywords::StaticLifetime.name();
548                 // If there is a static predicate, then the only sensible suggestion is to replace
549                 // fr with `'static`.
550                 if has_static_predicate {
551                     diag.help(
552                         &format!(
553                             "consider replacing `{}` with `{}`",
554                             fr_name, static_str,
555                         ),
556                     );
557                 } else {
558                     // Otherwise, we should suggest adding a constraint on the return type.
559                     let span = infcx.tcx.def_span(*did);
560                     if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
561                         let suggestable_fr_name = if fr_name.was_named() {
562                             fr_name.to_string()
563                         } else {
564                             "'_".to_string()
565                         };
566
567                         diag.span_suggestion_with_applicability(
568                             span,
569                             &format!(
570                                 "to allow this impl Trait to capture borrowed data with lifetime \
571                                  `{}`, add `{}` as a constraint",
572                                 fr_name, suggestable_fr_name,
573                             ),
574                             format!("{} + {}", snippet, suggestable_fr_name),
575                             Applicability::MachineApplicable,
576                         );
577                     }
578                 }
579             }
580         }
581     }
582
583     // Finds some region R such that `fr1: R` and `R` is live at
584     // `elem`.
585     crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
586         // Find all paths
587         let (_path, r) =
588             self.find_constraint_paths_between_regions(fr1, |r| {
589                 self.liveness_constraints.contains(r, elem)
590             }).unwrap();
591         r
592     }
593
594     // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
595     crate fn find_outlives_blame_span(
596         &self,
597         mir: &Mir<'tcx>,
598         fr1: RegionVid,
599         fr2: RegionVid,
600     ) -> (ConstraintCategory, Span) {
601         let (category, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
602         (category, span)
603     }
604
605     fn retrieve_closure_constraint_info(
606         &self,
607         mir: &Mir<'tcx>,
608         constraint: &OutlivesConstraint
609     ) -> (ConstraintCategory, Span) {
610         let loc = match constraint.locations {
611             Locations::All(span) => return (constraint.category, span),
612             Locations::Single(loc) => loc,
613         };
614
615         let opt_span_category = self
616             .closure_bounds_mapping[&loc]
617             .get(&(constraint.sup, constraint.sub));
618         *opt_span_category.unwrap_or(&(constraint.category, mir.source_info(loc).span))
619     }
620
621     /// Returns `true` if a closure is inferred to be an `FnMut` closure.
622     crate fn is_closure_fn_mut(
623         &self,
624         infcx: &InferCtxt<'_, '_, 'tcx>,
625         fr: RegionVid,
626     ) -> bool {
627         if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
628             if let ty::BoundRegion::BrEnv = free_region.bound_region {
629                 if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
630                     let closure_kind_ty = substs.closure_kind_ty(def_id, infcx.tcx);
631                     return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
632                 }
633             }
634         }
635
636         false
637     }
638 }