| use borrow_check::nll::constraints::OutlivesConstraint; |
| use borrow_check::nll::region_infer::RegionInferenceContext; |
| use borrow_check::nll::type_check::Locations; |
| use borrow_check::nll::universal_regions::DefiningTy; |
| use borrow_check::nll::ConstraintDescription; |
| use rustc::hir::def_id::DefId; |
| use rustc::infer::error_reporting::nice_region_error::NiceRegionError; |
| use rustc::infer::InferCtxt; |
| use rustc::infer::NLLRegionVariableOrigin; |
| use rustc::mir::{ConstraintCategory, Location, Mir}; |
| use rustc::ty::{self, RegionVid}; |
| use rustc_data_structures::indexed_vec::IndexVec; |
| use rustc_errors::{Diagnostic, DiagnosticBuilder}; |
| use std::collections::VecDeque; |
| use syntax::errors::Applicability; |
| use syntax::symbol::keywords; |
| use syntax_pos::Span; |
| use util::borrowck_errors::{BorrowckErrors, Origin}; |
| |
| mod region_name; |
| mod var_name; |
| |
| crate use self::region_name::{RegionName, RegionNameSource}; |
| |
| impl ConstraintDescription for ConstraintCategory { |
| fn description(&self) -> &'static str { |
| // Must end with a space. Allows for empty names to be provided. |
| match self { |
| ConstraintCategory::Assignment => "assignment ", |
| ConstraintCategory::Return => "returning this value ", |
| ConstraintCategory::Yield => "yielding this value ", |
| ConstraintCategory::UseAsConst => "using this value as a constant ", |
| ConstraintCategory::UseAsStatic => "using this value as a static ", |
| ConstraintCategory::Cast => "cast ", |
| ConstraintCategory::CallArgument => "argument ", |
| ConstraintCategory::TypeAnnotation => "type annotation ", |
| ConstraintCategory::ClosureBounds => "closure body ", |
| ConstraintCategory::SizedBound => "proving this value is `Sized` ", |
| ConstraintCategory::CopyBound => "copying this value ", |
| ConstraintCategory::OpaqueType => "opaque type ", |
| ConstraintCategory::Boring |
| | ConstraintCategory::BoringNoLocation |
| | ConstraintCategory::Internal => "", |
| } |
| } |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq)] |
| enum Trace { |
| StartRegion, |
| FromOutlivesConstraint(OutlivesConstraint), |
| NotVisited, |
| } |
| |
| impl<'tcx> RegionInferenceContext<'tcx> { |
| /// Tries to find the best constraint to blame for the fact that |
| /// `R: from_region`, where `R` is some region that meets |
| /// `target_test`. This works by following the constraint graph, |
| /// creating a constraint path that forces `R` to outlive |
| /// `from_region`, and then finding the best choices within that |
| /// path to blame. |
| fn best_blame_constraint( |
| &self, |
| mir: &Mir<'tcx>, |
| from_region: RegionVid, |
| target_test: impl Fn(RegionVid) -> bool, |
| ) -> (ConstraintCategory, bool, Span) { |
| debug!("best_blame_constraint(from_region={:?})", from_region); |
| |
| // Find all paths |
| let (path, target_region) = |
| self.find_constraint_paths_between_regions(from_region, target_test) |
| .unwrap(); |
| debug!( |
| "best_blame_constraint: path={:#?}", |
| path.iter() |
| .map(|&c| format!( |
| "{:?} ({:?}: {:?})", |
| c, |
| self.constraint_sccs.scc(c.sup), |
| self.constraint_sccs.scc(c.sub), |
| )) |
| .collect::<Vec<_>>() |
| ); |
| |
| // Classify each of the constraints along the path. |
| let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter() |
| .map(|constraint| { |
| if constraint.category == ConstraintCategory::ClosureBounds { |
| self.retrieve_closure_constraint_info(mir, &constraint) |
| } else { |
| (constraint.category, false, constraint.locations.span(mir)) |
| } |
| }) |
| .collect(); |
| debug!( |
| "best_blame_constraint: categorized_path={:#?}", |
| categorized_path |
| ); |
| |
| // To find the best span to cite, we first try to look for the |
| // final constraint that is interesting and where the `sup` is |
| // not unified with the ultimate target region. The reason |
| // for this is that we have a chain of constraints that lead |
| // from the source to the target region, something like: |
| // |
| // '0: '1 ('0 is the source) |
| // '1: '2 |
| // '2: '3 |
| // '3: '4 |
| // '4: '5 |
| // '5: '6 ('6 is the target) |
| // |
| // Some of those regions are unified with `'6` (in the same |
| // SCC). We want to screen those out. After that point, the |
| // "closest" constraint we have to the end is going to be the |
| // most likely to be the point where the value escapes -- but |
| // we still want to screen for an "interesting" point to |
| // highlight (e.g., a call site or something). |
| let target_scc = self.constraint_sccs.scc(target_region); |
| let best_choice = (0..path.len()).rev().find(|&i| { |
| let constraint = path[i]; |
| |
| let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup); |
| |
| match categorized_path[i].0 { |
| ConstraintCategory::OpaqueType | ConstraintCategory::Boring | |
| ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false, |
| ConstraintCategory::TypeAnnotation | ConstraintCategory::Return | |
| ConstraintCategory::Yield => true, |
| _ => constraint_sup_scc != target_scc, |
| } |
| }); |
| if let Some(i) = best_choice { |
| return categorized_path[i]; |
| } |
| |
| // If that search fails, that is.. unusual. Maybe everything |
| // is in the same SCC or something. In that case, find what |
| // appears to be the most interesting point to report to the |
| // user via an even more ad-hoc guess. |
| categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0)); |
| debug!("`: sorted_path={:#?}", categorized_path); |
| |
| *categorized_path.first().unwrap() |
| } |
| |
| /// Walks the graph of constraints (where `'a: 'b` is considered |
| /// an edge `'a -> 'b`) to find all paths from `from_region` to |
| /// `to_region`. The paths are accumulated into the vector |
| /// `results`. The paths are stored as a series of |
| /// `ConstraintIndex` values -- in other words, a list of *edges*. |
| /// |
| /// Returns: a series of constraints as well as the region `R` |
| /// that passed the target test. |
| fn find_constraint_paths_between_regions( |
| &self, |
| from_region: RegionVid, |
| target_test: impl Fn(RegionVid) -> bool, |
| ) -> Option<(Vec<OutlivesConstraint>, RegionVid)> { |
| let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions); |
| context[from_region] = Trace::StartRegion; |
| |
| // Use a deque so that we do a breadth-first search. We will |
| // stop at the first match, which ought to be the shortest |
| // path (fewest constraints). |
| let mut deque = VecDeque::new(); |
| deque.push_back(from_region); |
| |
| while let Some(r) = deque.pop_front() { |
| debug!( |
| "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}", |
| from_region, |
| r, |
| self.region_value_str(r), |
| ); |
| |
| // Check if we reached the region we were looking for. If so, |
| // we can reconstruct the path that led to it and return it. |
| if target_test(r) { |
| let mut result = vec![]; |
| let mut p = r; |
| loop { |
| match context[p] { |
| Trace::NotVisited => { |
| bug!("found unvisited region {:?} on path to {:?}", p, r) |
| } |
| Trace::FromOutlivesConstraint(c) => { |
| result.push(c); |
| p = c.sup; |
| } |
| |
| Trace::StartRegion => { |
| result.reverse(); |
| return Some((result, r)); |
| } |
| } |
| } |
| } |
| |
| // Otherwise, walk over the outgoing constraints and |
| // enqueue any regions we find, keeping track of how we |
| // reached them. |
| let fr_static = self.universal_regions.fr_static; |
| for constraint in self.constraint_graph |
| .outgoing_edges(r, &self.constraints, fr_static) |
| { |
| assert_eq!(constraint.sup, r); |
| let sub_region = constraint.sub; |
| if let Trace::NotVisited = context[sub_region] { |
| context[sub_region] = Trace::FromOutlivesConstraint(constraint); |
| deque.push_back(sub_region); |
| } |
| } |
| } |
| |
| None |
| } |
| |
| /// Report an error because the universal region `fr` was required to outlive |
| /// `outlived_fr` but it is not known to do so. For example: |
| /// |
| /// ``` |
| /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } |
| /// ``` |
| /// |
| /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`. |
| pub(super) fn report_error( |
| &self, |
| mir: &Mir<'tcx>, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| mir_def_id: DefId, |
| fr: RegionVid, |
| outlived_fr: RegionVid, |
| errors_buffer: &mut Vec<Diagnostic>, |
| ) { |
| debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); |
| |
| let (category, _, span) = self.best_blame_constraint(mir, fr, |r| { |
| self.provides_universal_region(r, fr, outlived_fr) |
| }); |
| |
| // Check if we can use one of the "nice region errors". |
| if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { |
| let tables = infcx.tcx.typeck_tables_of(mir_def_id); |
| let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables)); |
| if let Some(_error_reported) = nice.try_report_from_nll() { |
| return; |
| } |
| } |
| |
| let (fr_is_local, outlived_fr_is_local): (bool, bool) = ( |
| self.universal_regions.is_local_free_region(fr), |
| self.universal_regions.is_local_free_region(outlived_fr), |
| ); |
| |
| debug!( |
| "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}", |
| fr_is_local, outlived_fr_is_local, category |
| ); |
| match (category, fr_is_local, outlived_fr_is_local) { |
| (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => { |
| self.report_fnmut_error( |
| mir, |
| infcx, |
| mir_def_id, |
| fr, |
| outlived_fr, |
| span, |
| errors_buffer, |
| ) |
| } |
| (ConstraintCategory::Assignment, true, false) |
| | (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error( |
| mir, |
| infcx, |
| mir_def_id, |
| fr, |
| outlived_fr, |
| category, |
| span, |
| errors_buffer, |
| ), |
| _ => self.report_general_error( |
| mir, |
| infcx, |
| mir_def_id, |
| fr, |
| fr_is_local, |
| outlived_fr, |
| outlived_fr_is_local, |
| category, |
| span, |
| errors_buffer, |
| ), |
| }; |
| } |
| |
| /// We have a constraint `fr1: fr2` that is not satisfied, where |
| /// `fr2` represents some universal region. Here, `r` is some |
| /// region where we know that `fr1: r` and this function has the |
| /// job of determining whether `r` is "to blame" for the fact that |
| /// `fr1: fr2` is required. |
| /// |
| /// This is true under two conditions: |
| /// |
| /// - `r == fr2` |
| /// - `fr2` is `'static` and `r` is some placeholder in a universe |
| /// that cannot be named by `fr1`; in that case, we will require |
| /// that `fr1: 'static` because it is the only way to `fr1: r` to |
| /// be satisfied. (See `add_incompatible_universe`.) |
| fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool { |
| debug!( |
| "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", |
| r, fr1, fr2 |
| ); |
| let result = { |
| r == fr2 || { |
| fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r) |
| } |
| }; |
| debug!("provides_universal_region: result = {:?}", result); |
| result |
| } |
| |
| /// Report a specialized error when `FnMut` closures return a reference to a captured variable. |
| /// This function expects `fr` to be local and `outlived_fr` to not be local. |
| /// |
| /// ```text |
| /// error: captured variable cannot escape `FnMut` closure body |
| /// --> $DIR/issue-53040.rs:15:8 |
| /// | |
| /// LL | || &mut v; |
| /// | -- ^^^^^^ creates a reference to a captured variable which escapes the closure body |
| /// | | |
| /// | inferred to be a `FnMut` closure |
| /// | |
| /// = note: `FnMut` closures only have access to their captured variables while they are |
| /// executing... |
| /// = note: ...therefore, returned references to captured variables will escape the closure |
| /// ``` |
| fn report_fnmut_error( |
| &self, |
| mir: &Mir<'tcx>, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| mir_def_id: DefId, |
| _fr: RegionVid, |
| outlived_fr: RegionVid, |
| span: Span, |
| errors_buffer: &mut Vec<Diagnostic>, |
| ) { |
| let mut diag = infcx |
| .tcx |
| .sess |
| .struct_span_err(span, "captured variable cannot escape `FnMut` closure body"); |
| |
| // We should check if the return type of this closure is in fact a closure - in that |
| // case, we can special case the error further. |
| let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure(); |
| let message = if return_type_is_closure { |
| "returns a closure that contains a reference to a captured variable, which then \ |
| escapes the closure body" |
| } else { |
| "returns a reference to a captured variable which escapes the closure body" |
| }; |
| |
| diag.span_label(span, message); |
| |
| match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1).unwrap().source { |
| RegionNameSource::NamedEarlyBoundRegion(fr_span) |
| | RegionNameSource::NamedFreeRegion(fr_span) |
| | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _) |
| | RegionNameSource::CannotMatchHirTy(fr_span, _) |
| | RegionNameSource::MatchedHirTy(fr_span) |
| | RegionNameSource::MatchedAdtAndSegment(fr_span) |
| | RegionNameSource::AnonRegionFromUpvar(fr_span, _) |
| | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => { |
| diag.span_label(fr_span, "inferred to be a `FnMut` closure"); |
| } |
| _ => {} |
| } |
| |
| diag.note( |
| "`FnMut` closures only have access to their captured variables while they are \ |
| executing...", |
| ); |
| diag.note("...therefore, they cannot allow references to captured variables to escape"); |
| |
| diag.buffer(errors_buffer); |
| } |
| |
| /// Reports a error specifically for when data is escaping a closure. |
| /// |
| /// ```text |
| /// error: borrowed data escapes outside of function |
| /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5 |
| /// | |
| /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) { |
| /// | - `x` is a reference that is only valid in the function body |
| /// LL | // but ref_obj will not, so warn. |
| /// LL | ref_obj(x) |
| /// | ^^^^^^^^^^ `x` escapes the function body here |
| /// ``` |
| fn report_escaping_data_error( |
| &self, |
| mir: &Mir<'tcx>, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| mir_def_id: DefId, |
| fr: RegionVid, |
| outlived_fr: RegionVid, |
| category: ConstraintCategory, |
| span: Span, |
| errors_buffer: &mut Vec<Diagnostic>, |
| ) { |
| let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr); |
| let outlived_fr_name_and_span = |
| self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr); |
| |
| let escapes_from = match self.universal_regions.defining_ty { |
| DefiningTy::Closure(..) => "closure", |
| DefiningTy::Generator(..) => "generator", |
| DefiningTy::FnDef(..) => "function", |
| DefiningTy::Const(..) => "const", |
| }; |
| |
| // Revert to the normal error in these cases. |
| // Assignments aren't "escapes" in function items. |
| if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none()) |
| || (category == ConstraintCategory::Assignment && escapes_from == "function") |
| || escapes_from == "const" |
| { |
| return self.report_general_error( |
| mir, |
| infcx, |
| mir_def_id, |
| fr, |
| true, |
| outlived_fr, |
| false, |
| category, |
| span, |
| errors_buffer, |
| ); |
| } |
| |
| let mut diag = infcx |
| .tcx |
| .borrowed_data_escapes_closure(span, escapes_from, Origin::Mir); |
| |
| if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span { |
| diag.span_label( |
| outlived_fr_span, |
| format!( |
| "`{}` is declared here, outside of the {} body", |
| outlived_fr_name, escapes_from |
| ), |
| ); |
| } |
| |
| if let Some((Some(fr_name), fr_span)) = fr_name_and_span { |
| diag.span_label( |
| fr_span, |
| format!( |
| "`{}` is a reference that is only valid in the {} body", |
| fr_name, escapes_from |
| ), |
| ); |
| |
| diag.span_label( |
| span, |
| format!("`{}` escapes the {} body here", fr_name, escapes_from), |
| ); |
| } |
| |
| diag.buffer(errors_buffer); |
| } |
| |
| /// Reports a region inference error for the general case with named/synthesized lifetimes to |
| /// explain what is happening. |
| /// |
| /// ```text |
| /// error: unsatisfied lifetime constraints |
| /// --> $DIR/regions-creating-enums3.rs:17:5 |
| /// | |
| /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> { |
| /// | -- -- lifetime `'b` defined here |
| /// | | |
| /// | lifetime `'a` defined here |
| /// LL | ast::add(x, y) |
| /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it |
| /// | is returning data with lifetime `'b` |
| /// ``` |
| fn report_general_error( |
| &self, |
| mir: &Mir<'tcx>, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| mir_def_id: DefId, |
| fr: RegionVid, |
| fr_is_local: bool, |
| outlived_fr: RegionVid, |
| outlived_fr_is_local: bool, |
| category: ConstraintCategory, |
| span: Span, |
| errors_buffer: &mut Vec<Diagnostic>, |
| ) { |
| let mut diag = infcx.tcx.sess.struct_span_err( |
| span, |
| "lifetime may not live long enough" |
| ); |
| |
| let counter = &mut 1; |
| let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter).unwrap(); |
| fr_name.highlight_region_name(&mut diag); |
| let outlived_fr_name = |
| self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, counter).unwrap(); |
| outlived_fr_name.highlight_region_name(&mut diag); |
| |
| let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { |
| "closure" |
| } else { |
| "function" |
| }; |
| |
| match (category, outlived_fr_is_local, fr_is_local) { |
| (ConstraintCategory::Return, true, _) => { |
| diag.span_label( |
| span, |
| format!( |
| "{} was supposed to return data with lifetime `{}` but it is returning \ |
| data with lifetime `{}`", |
| mir_def_name, outlived_fr_name, fr_name |
| ), |
| ); |
| } |
| _ => { |
| diag.span_label( |
| span, |
| format!( |
| "{}requires that `{}` must outlive `{}`", |
| category.description(), |
| fr_name, |
| outlived_fr_name, |
| ), |
| ); |
| } |
| } |
| |
| self.add_static_impl_trait_suggestion(infcx, &mut diag, fr, fr_name, outlived_fr); |
| |
| diag.buffer(errors_buffer); |
| } |
| |
| /// Adds a suggestion to errors where a `impl Trait` is returned. |
| /// |
| /// ```text |
| /// help: to allow this impl Trait to capture borrowed data with lifetime `'1`, add `'_` as |
| /// a constraint |
| /// | |
| /// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a { |
| /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| /// ``` |
| fn add_static_impl_trait_suggestion( |
| &self, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| diag: &mut DiagnosticBuilder<'_>, |
| fr: RegionVid, |
| // We need to pass `fr_name` - computing it again will label it twice. |
| fr_name: RegionName, |
| outlived_fr: RegionVid, |
| ) { |
| if let (Some(f), Some(ty::RegionKind::ReStatic)) = |
| (self.to_error_region(fr), self.to_error_region(outlived_fr)) |
| { |
| if let Some(ty::TyS { |
| sty: ty::TyKind::Opaque(did, substs), |
| .. |
| }) = infcx |
| .tcx |
| .is_suitable_region(f) |
| .map(|r| r.def_id) |
| .map(|id| infcx.tcx.return_type_impl_trait(id)) |
| .unwrap_or(None) |
| { |
| // Check whether or not the impl trait return type is intended to capture |
| // data with the static lifetime. |
| // |
| // eg. check for `impl Trait + 'static` instead of `impl Trait`. |
| let has_static_predicate = { |
| let predicates_of = infcx.tcx.predicates_of(*did); |
| let bounds = predicates_of.instantiate(infcx.tcx, substs); |
| |
| let mut found = false; |
| for predicate in bounds.predicates { |
| if let ty::Predicate::TypeOutlives(binder) = predicate { |
| if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) = |
| binder.skip_binder() |
| { |
| found = true; |
| break; |
| } |
| } |
| } |
| |
| found |
| }; |
| |
| debug!( |
| "add_static_impl_trait_suggestion: has_static_predicate={:?}", |
| has_static_predicate |
| ); |
| let static_str = keywords::StaticLifetime.name(); |
| // If there is a static predicate, then the only sensible suggestion is to replace |
| // fr with `'static`. |
| if has_static_predicate { |
| diag.help(&format!( |
| "consider replacing `{}` with `{}`", |
| fr_name, static_str, |
| )); |
| } else { |
| // Otherwise, we should suggest adding a constraint on the return type. |
| let span = infcx.tcx.def_span(*did); |
| if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) { |
| let suggestable_fr_name = if fr_name.was_named() { |
| fr_name.to_string() |
| } else { |
| "'_".to_string() |
| }; |
| |
| diag.span_suggestion_with_applicability( |
| span, |
| &format!( |
| "to allow this impl Trait to capture borrowed data with lifetime \ |
| `{}`, add `{}` as a constraint", |
| fr_name, suggestable_fr_name, |
| ), |
| format!("{} + {}", snippet, suggestable_fr_name), |
| Applicability::MachineApplicable, |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| crate fn free_region_constraint_info( |
| &self, |
| mir: &Mir<'tcx>, |
| mir_def_id: DefId, |
| infcx: &InferCtxt<'_, '_, 'tcx>, |
| borrow_region: RegionVid, |
| outlived_region: RegionVid, |
| ) -> (ConstraintCategory, bool, Span, Option<RegionName>) { |
| let (category, from_closure, span) = |
| self.best_blame_constraint(mir, borrow_region, |r| r == outlived_region); |
| let outlived_fr_name = |
| self.give_region_a_name(infcx, mir, mir_def_id, outlived_region, &mut 1); |
| (category, from_closure, span, outlived_fr_name) |
| } |
| |
| // Finds some region R such that `fr1: R` and `R` is live at |
| // `elem`. |
| crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { |
| debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); |
| self.find_constraint_paths_between_regions(fr1, |r| { |
| // First look for some `r` such that `fr1: r` and `r` is live at `elem` |
| debug!( |
| "find_sub_region_live_at: liveness_constraints for {:?} are {:?}", |
| r, |
| self.liveness_constraints.region_value_str(r), |
| ); |
| self.liveness_constraints.contains(r, elem) |
| }).or_else(|| { |
| // If we fail to find that, we may find some `r` such that |
| // `fr1: r` and `r` is a placeholder from some universe |
| // `fr1` cannot name. This would force `fr1` to be |
| // `'static`. |
| self.find_constraint_paths_between_regions(fr1, |r| { |
| self.cannot_name_placeholder(fr1, r) |
| }) |
| }) |
| .or_else(|| { |
| // If we fail to find THAT, it may be that `fr1` is a |
| // placeholder that cannot "fit" into its SCC. In that |
| // case, there should be some `r` where `fr1: r`, both |
| // `fr1` and `r` are in the same SCC, and `fr1` is a |
| // placeholder that `r` cannot name. We can blame that |
| // edge. |
| self.find_constraint_paths_between_regions(fr1, |r| { |
| self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r) |
| && self.cannot_name_placeholder(r, fr1) |
| }) |
| }) |
| .map(|(_path, r)| r) |
| .unwrap() |
| } |
| |
| // Finds a good span to blame for the fact that `fr1` outlives `fr2`. |
| crate fn find_outlives_blame_span( |
| &self, |
| mir: &Mir<'tcx>, |
| fr1: RegionVid, |
| fr2: RegionVid, |
| ) -> (ConstraintCategory, Span) { |
| let (category, _, span) = |
| self.best_blame_constraint(mir, fr1, |r| self.provides_universal_region(r, fr1, fr2)); |
| (category, span) |
| } |
| |
| fn retrieve_closure_constraint_info( |
| &self, |
| mir: &Mir<'tcx>, |
| constraint: &OutlivesConstraint, |
| ) -> (ConstraintCategory, bool, Span) { |
| let loc = match constraint.locations { |
| Locations::All(span) => return (constraint.category, false, span), |
| Locations::Single(loc) => loc, |
| }; |
| |
| let opt_span_category = |
| self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub)); |
| opt_span_category |
| .map(|&(category, span)| (category, true, span)) |
| .unwrap_or((constraint.category, false, mir.source_info(loc).span)) |
| } |
| |
| /// Returns `true` if a closure is inferred to be an `FnMut` closure. |
| crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, '_, 'tcx>, fr: RegionVid) -> bool { |
| if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { |
| if let ty::BoundRegion::BrEnv = free_region.bound_region { |
| if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty { |
| let closure_kind_ty = substs.closure_kind_ty(def_id, infcx.tcx); |
| return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind(); |
| } |
| } |
| } |
| |
| false |
| } |
| |
| /// If `r2` represents a placeholder region, then this returns |
| /// true if `r1` cannot name that placeholder in its |
| /// value. Otherwise, returns false. |
| fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool { |
| debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); |
| |
| match self.definitions[r2].origin { |
| NLLRegionVariableOrigin::Placeholder(placeholder) => { |
| let universe1 = self.definitions[r1].universe; |
| debug!( |
| "cannot_name_value_of: universe1={:?} placeholder={:?}", |
| universe1, placeholder |
| ); |
| universe1.cannot_name(placeholder.universe) |
| } |
| |
| NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false, |
| } |
| } |
| } |