]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
641351956379b14d0d8f55418990d85f449c8e57
[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::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         debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
598         // Find all paths
599         let (_path, r) =
600             self.find_constraint_paths_between_regions(fr1, |r| {
601                 self.liveness_constraints.contains(r, elem)
602             }).unwrap();
603         r
604     }
605
606     // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
607     crate fn find_outlives_blame_span(
608         &self,
609         mir: &Mir<'tcx>,
610         fr1: RegionVid,
611         fr2: RegionVid,
612     ) -> (ConstraintCategory, Span) {
613         let (category, _, span) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
614         (category, span)
615     }
616
617     fn retrieve_closure_constraint_info(
618         &self,
619         mir: &Mir<'tcx>,
620         constraint: &OutlivesConstraint
621     ) -> (ConstraintCategory, bool, Span) {
622         let loc = match constraint.locations {
623             Locations::All(span) => return (constraint.category, false, span),
624             Locations::Single(loc) => loc,
625         };
626
627         let opt_span_category = self
628             .closure_bounds_mapping[&loc]
629             .get(&(constraint.sup, constraint.sub));
630         opt_span_category
631             .map(|&(category, span)| (category, true, span))
632             .unwrap_or((constraint.category, false, mir.source_info(loc).span))
633     }
634
635     /// Returns `true` if a closure is inferred to be an `FnMut` closure.
636     crate fn is_closure_fn_mut(
637         &self,
638         infcx: &InferCtxt<'_, '_, 'tcx>,
639         fr: RegionVid,
640     ) -> bool {
641         if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
642             if let ty::BoundRegion::BrEnv = free_region.bound_region {
643                 if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
644                     let closure_kind_ty = substs.closure_kind_ty(def_id, infcx.tcx);
645                     return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
646                 }
647             }
648         }
649
650         false
651     }
652 }