Skip to content

Commit 740117f

Browse files
committed
fix bug in NLL error reporting
Account for incompatible universes and higher-ranked subtyping.
1 parent a1be20c commit 740117f

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed

src/librustc/ty/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1522,10 +1522,17 @@ impl UniverseIndex {
15221522

15231523
/// True if `self` can name a name from `other` -- in other words,
15241524
/// if the set of names in `self` is a superset of those in
1525-
/// `other`.
1525+
/// `other` (`self >= other`).
15261526
pub fn can_name(self, other: UniverseIndex) -> bool {
15271527
self.private >= other.private
15281528
}
1529+
1530+
/// True if `self` cannot name some names from `other` -- in other
1531+
/// words, if the set of names in `self` is a strict subset of
1532+
/// those in `other` (`self < other`).
1533+
pub fn cannot_name(self, other: UniverseIndex) -> bool {
1534+
self.private < other.private
1535+
}
15291536
}
15301537

15311538
/// The "placeholder index" fully defines a placeholder region.

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

+90-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use borrow_check::nll::ConstraintDescription;
1616
use rustc::hir::def_id::DefId;
1717
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
1818
use rustc::infer::InferCtxt;
19+
use rustc::infer::NLLRegionVariableOrigin;
1920
use rustc::mir::{ConstraintCategory, Location, Mir};
2021
use rustc::ty::{self, RegionVid};
2122
use rustc_data_structures::indexed_vec::IndexVec;
@@ -177,6 +178,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
177178
deque.push_back(from_region);
178179

179180
while let Some(r) = deque.pop_front() {
181+
debug!(
182+
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
183+
from_region,
184+
r,
185+
self.region_value_str(r),
186+
);
187+
180188
// Check if we reached the region we were looking for. If so,
181189
// we can reconstruct the path that led to it and return it.
182190
if target_test(r) {
@@ -238,7 +246,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
238246
) {
239247
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
240248

241-
let (category, _, span) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr);
249+
let (category, _, span) = self.best_blame_constraint(mir, fr, |r| {
250+
self.provides_universal_region(r, fr, outlived_fr)
251+
});
242252

243253
// Check if we can use one of the "nice region errors".
244254
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
@@ -296,6 +306,33 @@ impl<'tcx> RegionInferenceContext<'tcx> {
296306
};
297307
}
298308

309+
/// We have a constraint `fr1: fr2` that is not satisfied, where
310+
/// `fr2` represents some universal region. Here, `r` is some
311+
/// region where we know that `fr1: r` and this function has the
312+
/// job of determining whether `r` is "to blame" for the fact that
313+
/// `fr1: fr2` is required.
314+
///
315+
/// This is true under two conditions:
316+
///
317+
/// - `r == fr2`
318+
/// - `fr2` is `'static` and `r` is some placeholder in a universe
319+
/// that cannot be named by `fr1`; in that case, we will require
320+
/// that `fr1: 'static` because it is the only way to `fr1: r` to
321+
/// be satisfied. (See `add_incompatible_universe`.)
322+
fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool {
323+
debug!(
324+
"provides_universal_region(r={:?}, fr1={:?}, fr2={:?})",
325+
r, fr1, fr2
326+
);
327+
let result = {
328+
r == fr2 || {
329+
fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
330+
}
331+
};
332+
debug!("provides_universal_region: result = {:?}", result);
333+
result
334+
}
335+
299336
/// Report a specialized error when `FnMut` closures return a reference to a captured variable.
300337
/// This function expects `fr` to be local and `outlived_fr` to not be local.
301338
///
@@ -636,11 +673,37 @@ impl<'tcx> RegionInferenceContext<'tcx> {
636673
// `elem`.
637674
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
638675
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
639-
// Find all paths
640-
let (_path, r) = self.find_constraint_paths_between_regions(fr1, |r| {
676+
self.find_constraint_paths_between_regions(fr1, |r| {
677+
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
678+
debug!(
679+
"find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
680+
r,
681+
self.liveness_constraints.region_value_str(r),
682+
);
641683
self.liveness_constraints.contains(r, elem)
642-
}).unwrap();
643-
r
684+
}).or_else(|| {
685+
// If we fail to find that, we may find some `r` such that
686+
// `fr1: r` and `r` is a placeholder from some universe
687+
// `fr1` cannot name. This would force `fr1` to be
688+
// `'static`.
689+
self.find_constraint_paths_between_regions(fr1, |r| {
690+
self.cannot_name_placeholder(fr1, r)
691+
})
692+
})
693+
.or_else(|| {
694+
// If we fail to find THAT, it may be that `fr1` is a
695+
// placeholder that cannot "fit" into its SCC. In that
696+
// case, there should be some `r` where `fr1: r`, both
697+
// `fr1` and `r` are in the same SCC, and `fr1` is a
698+
// placeholder that `r` cannot name. We can blame that
699+
// edge.
700+
self.find_constraint_paths_between_regions(fr1, |r| {
701+
self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
702+
&& self.cannot_name_placeholder(r, fr1)
703+
})
704+
})
705+
.map(|(_path, r)| r)
706+
.unwrap()
644707
}
645708

646709
// Finds a good span to blame for the fact that `fr1` outlives `fr2`.
@@ -650,7 +713,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
650713
fr1: RegionVid,
651714
fr2: RegionVid,
652715
) -> (ConstraintCategory, Span) {
653-
let (category, _, span) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
716+
let (category, _, span) =
717+
self.best_blame_constraint(mir, fr1, |r| self.provides_universal_region(r, fr1, fr2));
654718
(category, span)
655719
}
656720

@@ -684,4 +748,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
684748

685749
false
686750
}
751+
752+
/// If `r2` represents a placeholder region, then this returns
753+
/// true if `r1` cannot name that placeholder in its
754+
/// value. Otherwise, returns false.
755+
fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
756+
debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
757+
758+
match self.definitions[r2].origin {
759+
NLLRegionVariableOrigin::Placeholder(placeholder) => {
760+
let universe1 = self.definitions[r1].universe;
761+
debug!(
762+
"cannot_name_value_of: universe1={:?} placeholder={:?}",
763+
universe1, placeholder
764+
);
765+
universe1.cannot_name(placeholder.universe)
766+
}
767+
768+
NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false,
769+
}
770+
}
687771
}

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
345345
if scc_universe.can_name(placeholder.universe) {
346346
self.scc_values.add_element(scc, placeholder);
347347
} else {
348+
debug!(
349+
"init_free_and_bound_regions: placeholder {:?} is \
350+
not compatible with universe {:?} of its SCC {:?}",
351+
placeholder,
352+
scc_universe,
353+
scc,
354+
);
348355
self.add_incompatible_universe(scc);
349356
}
350357
}
@@ -471,6 +478,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
471478
let mut constraints: Vec<_> = self.constraints.iter().collect();
472479
constraints.sort();
473480
constraints
481+
.into_iter()
482+
.map(|c| (c, self.constraint_sccs.scc(c.sup), self.constraint_sccs.scc(c.sub)))
483+
.collect::<Vec<_>>()
474484
});
475485

476486
// To propagate constraints, we walk the DAG induced by the
@@ -560,6 +570,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
560570
/// `'a` with `'b` and not `'static`. But it will have to do for
561571
/// now.
562572
fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) {
573+
debug!("add_incompatible_universe(scc={:?})", scc);
574+
563575
let fr_static = self.universal_regions.fr_static;
564576
self.scc_values.add_all_points(scc);
565577
self.scc_values.add_element(scc, fr_static);

src/librustc_mir/borrow_check/nll/type_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ impl MirTypeckRegionConstraints<'tcx> {
782782
Some(&v) => v,
783783
None => {
784784
let origin = NLLRegionVariableOrigin::Placeholder(placeholder);
785-
let region = infcx.next_nll_region_var(origin);
785+
let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe);
786786
self.placeholder_index_to_region.push(region);
787787
region
788788
}

0 commit comments

Comments
 (0)