1 use crate::borrow_check::nll::constraints::OutlivesConstraint;
2 use crate::borrow_check::nll::region_infer::AppliedMemberConstraint;
3 use crate::borrow_check::nll::region_infer::RegionInferenceContext;
4 use crate::borrow_check::nll::type_check::Locations;
5 use crate::borrow_check::nll::universal_regions::DefiningTy;
6 use crate::borrow_check::nll::ConstraintDescription;
7 use crate::util::borrowck_errors::{BorrowckErrors, Origin};
8 use crate::borrow_check::Upvar;
9 use rustc::hir::def_id::DefId;
10 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
11 use rustc::infer::InferCtxt;
12 use rustc::infer::NLLRegionVariableOrigin;
13 use rustc::mir::{ConstraintCategory, Location, Body};
14 use rustc::ty::{self, RegionVid};
15 use rustc_data_structures::indexed_vec::IndexVec;
16 use rustc_errors::{Diagnostic, DiagnosticBuilder};
17 use std::collections::VecDeque;
18 use syntax::errors::Applicability;
19 use syntax::symbol::kw;
25 crate use self::region_name::{RegionName, RegionNameSource};
27 impl ConstraintDescription for ConstraintCategory {
28 fn description(&self) -> &'static str {
29 // Must end with a space. Allows for empty names to be provided.
31 ConstraintCategory::Assignment => "assignment ",
32 ConstraintCategory::Return => "returning this value ",
33 ConstraintCategory::Yield => "yielding this value ",
34 ConstraintCategory::UseAsConst => "using this value as a constant ",
35 ConstraintCategory::UseAsStatic => "using this value as a static ",
36 ConstraintCategory::Cast => "cast ",
37 ConstraintCategory::CallArgument => "argument ",
38 ConstraintCategory::TypeAnnotation => "type annotation ",
39 ConstraintCategory::ClosureBounds => "closure body ",
40 ConstraintCategory::SizedBound => "proving this value is `Sized` ",
41 ConstraintCategory::CopyBound => "copying this value ",
42 ConstraintCategory::OpaqueType => "opaque type ",
43 ConstraintCategory::Boring
44 | ConstraintCategory::BoringNoLocation
45 | ConstraintCategory::Internal => "",
50 #[derive(Copy, Clone, PartialEq, Eq)]
53 FromOutlivesConstraint(OutlivesConstraint),
57 impl<'tcx> RegionInferenceContext<'tcx> {
58 /// Tries to find the best constraint to blame for the fact that
59 /// `R: from_region`, where `R` is some region that meets
60 /// `target_test`. This works by following the constraint graph,
61 /// creating a constraint path that forces `R` to outlive
62 /// `from_region`, and then finding the best choices within that
64 fn best_blame_constraint(
67 from_region: RegionVid,
68 target_test: impl Fn(RegionVid) -> bool,
69 ) -> (ConstraintCategory, bool, Span) {
70 debug!("best_blame_constraint(from_region={:?})", from_region);
73 let (path, target_region) =
74 self.find_constraint_paths_between_regions(from_region, target_test)
77 "best_blame_constraint: path={:#?}",
82 self.constraint_sccs.scc(c.sup),
83 self.constraint_sccs.scc(c.sub),
88 // Classify each of the constraints along the path.
89 let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter()
91 if constraint.category == ConstraintCategory::ClosureBounds {
92 self.retrieve_closure_constraint_info(body, &constraint)
94 (constraint.category, false, constraint.locations.span(body))
99 "best_blame_constraint: categorized_path={:#?}",
103 // To find the best span to cite, we first try to look for the
104 // final constraint that is interesting and where the `sup` is
105 // not unified with the ultimate target region. The reason
106 // for this is that we have a chain of constraints that lead
107 // from the source to the target region, something like:
109 // '0: '1 ('0 is the source)
114 // '5: '6 ('6 is the target)
116 // Some of those regions are unified with `'6` (in the same
117 // SCC). We want to screen those out. After that point, the
118 // "closest" constraint we have to the end is going to be the
119 // most likely to be the point where the value escapes -- but
120 // we still want to screen for an "interesting" point to
121 // highlight (e.g., a call site or something).
122 let target_scc = self.constraint_sccs.scc(target_region);
123 let best_choice = (0..path.len()).rev().find(|&i| {
124 let constraint = path[i];
126 let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
128 match categorized_path[i].0 {
129 ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
130 ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
131 ConstraintCategory::TypeAnnotation | ConstraintCategory::Return |
132 ConstraintCategory::Yield => true,
133 _ => constraint_sup_scc != target_scc,
136 if let Some(i) = best_choice {
137 if let Some(next) = categorized_path.get(i + 1) {
138 if categorized_path[i].0 == ConstraintCategory::Return
139 && next.0 == ConstraintCategory::OpaqueType
141 // The return expression is being influenced by the return type being
142 // impl Trait, point at the return type and not the return expr.
146 return categorized_path[i];
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);
156 *categorized_path.first().unwrap()
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*.
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(
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;
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);
181 while let Some(r) = deque.pop_front() {
183 "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
186 self.region_value_str(r),
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.
192 let mut result = vec![];
196 Trace::NotVisited => {
197 bug!("found unvisited region {:?} on path to {:?}", p, r)
200 Trace::FromOutlivesConstraint(c) => {
205 Trace::StartRegion => {
207 return Some((result, r));
213 // Otherwise, walk over the outgoing constraints and
214 // enqueue any regions we find, keeping track of how we
217 // A constraint like `'r: 'x` can come from our constraint
219 let fr_static = self.universal_regions.fr_static;
220 let outgoing_edges_from_graph = self.constraint_graph
221 .outgoing_edges(r, &self.constraints, fr_static);
224 // But member constraints can also give rise to `'r: 'x`
225 // edges that were not part of the graph initially, so
226 // watch out for those.
227 let outgoing_edges_from_picks = self.applied_member_constraints(r)
229 .map(|&AppliedMemberConstraint { min_choice, member_constraint_index, .. }| {
230 let p_c = &self.member_constraints[member_constraint_index];
234 locations: Locations::All(p_c.definition_span),
235 category: ConstraintCategory::OpaqueType,
239 for constraint in outgoing_edges_from_graph.chain(outgoing_edges_from_picks) {
240 debug_assert_eq!(constraint.sup, r);
241 let sub_region = constraint.sub;
242 if let Trace::NotVisited = context[sub_region] {
243 context[sub_region] = Trace::FromOutlivesConstraint(constraint);
244 deque.push_back(sub_region);
252 /// Report an error because the universal region `fr` was required to outlive
253 /// `outlived_fr` but it is not known to do so. For example:
256 /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
259 /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
260 pub(super) fn report_error(
264 infcx: &InferCtxt<'_, 'tcx>,
267 outlived_fr: RegionVid,
268 errors_buffer: &mut Vec<Diagnostic>,
270 debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
272 let (category, _, span) = self.best_blame_constraint(body, fr, |r| {
273 self.provides_universal_region(r, fr, outlived_fr)
276 debug!("report_error: category={:?} {:?}", category, span);
277 // Check if we can use one of the "nice region errors".
278 if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
279 let tables = infcx.tcx.typeck_tables_of(mir_def_id);
280 let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables));
281 if let Some(diag) = nice.try_report_from_nll() {
282 diag.buffer(errors_buffer);
287 let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
288 self.universal_regions.is_local_free_region(fr),
289 self.universal_regions.is_local_free_region(outlived_fr),
293 "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
294 fr_is_local, outlived_fr_is_local, category
296 match (category, fr_is_local, outlived_fr_is_local) {
297 (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => {
298 self.report_fnmut_error(
309 (ConstraintCategory::Assignment, true, false)
310 | (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error(
321 _ => self.report_general_error(
329 outlived_fr_is_local,
337 /// We have a constraint `fr1: fr2` that is not satisfied, where
338 /// `fr2` represents some universal region. Here, `r` is some
339 /// region where we know that `fr1: r` and this function has the
340 /// job of determining whether `r` is "to blame" for the fact that
341 /// `fr1: fr2` is required.
343 /// This is true under two conditions:
346 /// - `fr2` is `'static` and `r` is some placeholder in a universe
347 /// that cannot be named by `fr1`; in that case, we will require
348 /// that `fr1: 'static` because it is the only way to `fr1: r` to
349 /// be satisfied. (See `add_incompatible_universe`.)
350 fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool {
352 "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})",
357 fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
360 debug!("provides_universal_region: result = {:?}", result);
364 /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
365 /// This function expects `fr` to be local and `outlived_fr` to not be local.
368 /// error: captured variable cannot escape `FnMut` closure body
369 /// --> $DIR/issue-53040.rs:15:8
372 /// | -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
374 /// | inferred to be a `FnMut` closure
376 /// = note: `FnMut` closures only have access to their captured variables while they are
378 /// = note: ...therefore, returned references to captured variables will escape the closure
380 fn report_fnmut_error(
384 infcx: &InferCtxt<'_, 'tcx>,
387 outlived_fr: RegionVid,
389 errors_buffer: &mut Vec<Diagnostic>,
394 .struct_span_err(span, "captured variable cannot escape `FnMut` closure body");
396 // We should check if the return type of this closure is in fact a closure - in that
397 // case, we can special case the error further.
398 let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure();
399 let message = if return_type_is_closure {
400 "returns a closure that contains a reference to a captured variable, which then \
401 escapes the closure body"
403 "returns a reference to a captured variable which escapes the closure body"
406 diag.span_label(span, message);
408 match self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_fr, &mut 1)
411 RegionNameSource::NamedEarlyBoundRegion(fr_span)
412 | RegionNameSource::NamedFreeRegion(fr_span)
413 | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
414 | RegionNameSource::CannotMatchHirTy(fr_span, _)
415 | RegionNameSource::MatchedHirTy(fr_span)
416 | RegionNameSource::MatchedAdtAndSegment(fr_span)
417 | RegionNameSource::AnonRegionFromUpvar(fr_span, _)
418 | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => {
419 diag.span_label(fr_span, "inferred to be a `FnMut` closure");
425 "`FnMut` closures only have access to their captured variables while they are \
428 diag.note("...therefore, they cannot allow references to captured variables to escape");
430 diag.buffer(errors_buffer);
433 /// Reports a error specifically for when data is escaping a closure.
436 /// error: borrowed data escapes outside of function
437 /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5
439 /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
440 /// | - `x` is a reference that is only valid in the function body
441 /// LL | // but ref_obj will not, so warn.
443 /// | ^^^^^^^^^^ `x` escapes the function body here
445 fn report_escaping_data_error(
449 infcx: &InferCtxt<'_, 'tcx>,
452 outlived_fr: RegionVid,
453 category: ConstraintCategory,
455 errors_buffer: &mut Vec<Diagnostic>,
457 let fr_name_and_span =
458 self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, fr);
459 let outlived_fr_name_and_span =
460 self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, outlived_fr);
462 let escapes_from = match self.universal_regions.defining_ty {
463 DefiningTy::Closure(..) => "closure",
464 DefiningTy::Generator(..) => "generator",
465 DefiningTy::FnDef(..) => "function",
466 DefiningTy::Const(..) => "const",
469 // Revert to the normal error in these cases.
470 // Assignments aren't "escapes" in function items.
471 if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
472 || (category == ConstraintCategory::Assignment && escapes_from == "function")
473 || escapes_from == "const"
475 return self.report_general_error(
492 .borrowed_data_escapes_closure(span, escapes_from, Origin::Mir);
494 if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
498 "`{}` is declared here, outside of the {} body",
499 outlived_fr_name, escapes_from
504 if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
508 "`{}` is a reference that is only valid in the {} body",
509 fr_name, escapes_from
515 format!("`{}` escapes the {} body here", fr_name, escapes_from),
519 diag.buffer(errors_buffer);
522 /// Reports a region inference error for the general case with named/synthesized lifetimes to
523 /// explain what is happening.
526 /// error: unsatisfied lifetime constraints
527 /// --> $DIR/regions-creating-enums3.rs:17:5
529 /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
530 /// | -- -- lifetime `'b` defined here
532 /// | lifetime `'a` defined here
533 /// LL | ast::add(x, y)
534 /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
535 /// | is returning data with lifetime `'b`
537 fn report_general_error(
541 infcx: &InferCtxt<'_, 'tcx>,
545 outlived_fr: RegionVid,
546 outlived_fr_is_local: bool,
547 category: ConstraintCategory,
549 errors_buffer: &mut Vec<Diagnostic>,
551 let mut diag = infcx.tcx.sess.struct_span_err(
553 "lifetime may not live long enough"
556 let counter = &mut 1;
557 let fr_name = self.give_region_a_name(
558 infcx, body, upvars, mir_def_id, fr, counter).unwrap();
559 fr_name.highlight_region_name(&mut diag);
560 let outlived_fr_name =
561 self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_fr, counter).unwrap();
562 outlived_fr_name.highlight_region_name(&mut diag);
564 let mir_def_name = if infcx.tcx.is_closure(mir_def_id) {
570 match (category, outlived_fr_is_local, fr_is_local) {
571 (ConstraintCategory::Return, true, _) => {
575 "{} was supposed to return data with lifetime `{}` but it is returning \
576 data with lifetime `{}`",
577 mir_def_name, outlived_fr_name, fr_name
585 "{}requires that `{}` must outlive `{}`",
586 category.description(),
594 self.add_static_impl_trait_suggestion(infcx, &mut diag, fr, fr_name, outlived_fr);
596 diag.buffer(errors_buffer);
599 /// Adds a suggestion to errors where a `impl Trait` is returned.
602 /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
605 /// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
606 /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
608 fn add_static_impl_trait_suggestion(
610 infcx: &InferCtxt<'_, 'tcx>,
611 diag: &mut DiagnosticBuilder<'_>,
613 // We need to pass `fr_name` - computing it again will label it twice.
615 outlived_fr: RegionVid,
617 if let (Some(f), Some(ty::RegionKind::ReStatic)) =
618 (self.to_error_region(fr), self.to_error_region(outlived_fr))
620 if let Some(ty::TyS {
621 sty: ty::Opaque(did, substs),
625 .is_suitable_region(f)
627 .map(|id| infcx.tcx.return_type_impl_trait(id))
630 // Check whether or not the impl trait return type is intended to capture
631 // data with the static lifetime.
633 // eg. check for `impl Trait + 'static` instead of `impl Trait`.
634 let has_static_predicate = {
635 let predicates_of = infcx.tcx.predicates_of(*did);
636 let bounds = predicates_of.instantiate(infcx.tcx, substs);
638 let mut found = false;
639 for predicate in bounds.predicates {
640 if let ty::Predicate::TypeOutlives(binder) = predicate {
641 if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) =
654 "add_static_impl_trait_suggestion: has_static_predicate={:?}",
657 let static_str = kw::StaticLifetime;
658 // If there is a static predicate, then the only sensible suggestion is to replace
659 // fr with `'static`.
660 if has_static_predicate {
662 "consider replacing `{}` with `{}`",
666 // Otherwise, we should suggest adding a constraint on the return type.
667 let span = infcx.tcx.def_span(*did);
668 if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
669 let suggestable_fr_name = if fr_name.was_named() {
675 diag.span_suggestion(
678 "to allow this `impl Trait` to capture borrowed data with lifetime \
679 `{}`, add `{}` as a constraint",
680 fr_name, suggestable_fr_name,
682 format!("{} + {}", snippet, suggestable_fr_name),
683 Applicability::MachineApplicable,
691 crate fn free_region_constraint_info(
696 infcx: &InferCtxt<'_, 'tcx>,
697 borrow_region: RegionVid,
698 outlived_region: RegionVid,
699 ) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
700 let (category, from_closure, span) = self.best_blame_constraint(
703 |r| self.provides_universal_region(r, borrow_region, outlived_region)
705 let outlived_fr_name =
706 self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_region, &mut 1);
707 (category, from_closure, span, outlived_fr_name)
710 // Finds some region R such that `fr1: R` and `R` is live at
712 crate fn find_sub_region_live_at(
717 debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
718 self.find_constraint_paths_between_regions(fr1, |r| {
719 // First look for some `r` such that `fr1: r` and `r` is live at `elem`
721 "find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
723 self.liveness_constraints.region_value_str(r),
725 self.liveness_constraints.contains(r, elem)
727 // If we fail to find that, we may find some `r` such that
728 // `fr1: r` and `r` is a placeholder from some universe
729 // `fr1` cannot name. This would force `fr1` to be
731 self.find_constraint_paths_between_regions(fr1, |r| {
732 self.cannot_name_placeholder(fr1, r)
736 // If we fail to find THAT, it may be that `fr1` is a
737 // placeholder that cannot "fit" into its SCC. In that
738 // case, there should be some `r` where `fr1: r`, both
739 // `fr1` and `r` are in the same SCC, and `fr1` is a
740 // placeholder that `r` cannot name. We can blame that
742 self.find_constraint_paths_between_regions(fr1, |r| {
743 self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
744 && self.cannot_name_placeholder(r, fr1)
751 // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
752 crate fn find_outlives_blame_span(
757 ) -> (ConstraintCategory, Span) {
758 let (category, _, span) = self.best_blame_constraint(
761 |r| self.provides_universal_region(r, fr1, fr2),
766 fn retrieve_closure_constraint_info(
769 constraint: &OutlivesConstraint,
770 ) -> (ConstraintCategory, bool, Span) {
771 let loc = match constraint.locations {
772 Locations::All(span) => return (constraint.category, false, span),
773 Locations::Single(loc) => loc,
776 let opt_span_category =
777 self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
779 .map(|&(category, span)| (category, true, span))
780 .unwrap_or((constraint.category, false, body.source_info(loc).span))
783 /// Returns `true` if a closure is inferred to be an `FnMut` closure.
784 crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, 'tcx>, fr: RegionVid) -> bool {
785 if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
786 if let ty::BoundRegion::BrEnv = free_region.bound_region {
787 if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
788 let closure_kind_ty = substs.closure_kind_ty(def_id, infcx.tcx);
789 return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
797 /// If `r2` represents a placeholder region, then this returns
798 /// `true` if `r1` cannot name that placeholder in its
799 /// value; otherwise, returns `false`.
800 fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
801 debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
803 match self.definitions[r2].origin {
804 NLLRegionVariableOrigin::Placeholder(placeholder) => {
805 let universe1 = self.definitions[r1].universe;
807 "cannot_name_value_of: universe1={:?} placeholder={:?}",
808 universe1, placeholder
810 universe1.cannot_name(placeholder.universe)
813 NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false,