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