fix SCCs containing mixture of universes
And add a test showing a universe violation getting caught.
This commit is contained in:
parent
f0e3a09bb8
commit
fced2b1200
3 changed files with 61 additions and 28 deletions
|
|
@ -319,23 +319,34 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
|
||||
for variable in self.definitions.indices() {
|
||||
let scc = self.constraint_sccs.scc(variable);
|
||||
|
||||
match self.definitions[variable].origin {
|
||||
NLLRegionVariableOrigin::FreeRegion => {
|
||||
// For each free, universally quantified region X:
|
||||
|
||||
// Add all nodes in the CFG to liveness constraints
|
||||
let variable_scc = self.constraint_sccs.scc(variable);
|
||||
self.liveness_constraints.add_all_points(variable);
|
||||
self.scc_values.add_all_points(variable_scc);
|
||||
self.scc_values.add_all_points(scc);
|
||||
|
||||
// Add `end(X)` into the set for X.
|
||||
self.add_element_to_scc_of(variable, variable);
|
||||
self.scc_values.add_element(scc, variable);
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::BoundRegion(ui) => {
|
||||
// Each placeholder region X outlives its
|
||||
// associated universe but nothing else.
|
||||
self.add_element_to_scc_of(variable, ui);
|
||||
// associated universe but nothing else. Every
|
||||
// placeholder region is always in a universe that
|
||||
// contains `ui` -- but when placeholder regions
|
||||
// are placed into an SCC, that SCC may include
|
||||
// things from other universes that do not include
|
||||
// `ui`.
|
||||
let scc_universe = self.scc_universes[scc];
|
||||
if ui.is_subset_of(scc_universe) {
|
||||
self.scc_values.add_element(scc, ui);
|
||||
} else {
|
||||
self.add_incompatible_universe(scc);
|
||||
}
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::Existential => {
|
||||
|
|
@ -383,13 +394,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_universes[scc]
|
||||
}
|
||||
|
||||
/// Adds `elem` to the value of the SCC in which `v` appears.
|
||||
fn add_element_to_scc_of(&mut self, v: RegionVid, elem: impl ToElementIndex) {
|
||||
debug!("add_live_element({:?}, {:?})", v, elem);
|
||||
let scc = self.constraint_sccs.scc(v);
|
||||
self.scc_values.add_element(scc, elem);
|
||||
}
|
||||
|
||||
/// Perform region inference and report errors if we see any
|
||||
/// unsatisfiable constraints. If this is a closure, returns the
|
||||
/// region requirements to propagate to our creator, if any.
|
||||
|
|
@ -516,22 +520,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// merge the bits.
|
||||
self.scc_values.add_region(scc_a, scc_b);
|
||||
} else {
|
||||
// Otherwise, the only way for `A` to outlive `B`
|
||||
// is for it to outlive static. This is actually stricter
|
||||
// than necessary: ideally, we'd support bounds like `for<'a: 'b`>`
|
||||
// that might then allow us to approximate `'a` with `'b` and not
|
||||
// `'static`. But it will have to do for now.
|
||||
//
|
||||
// The code here is a bit hacky: we grab the current
|
||||
// value of the SCC in which `'static` appears, but
|
||||
// this value may not be fully computed yet. That's ok
|
||||
// though: it will contain the base liveness values,
|
||||
// which include (a) the static free region element
|
||||
// and (b) all the points in the CFG, so it is "good
|
||||
// enough" to bring it in here for our purposes.
|
||||
let fr_static = self.universal_regions.fr_static;
|
||||
let scc_static = constraint_sccs.scc(fr_static);
|
||||
self.scc_values.add_region(scc_a, scc_static);
|
||||
self.add_incompatible_universe(scc_a);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -563,6 +552,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
.all(|u| u.is_subset_of(universe_a))
|
||||
}
|
||||
|
||||
/// Extend `scc` so that it can outlive some placeholder region
|
||||
/// from a universe it can't name; at present, the only way for
|
||||
/// this to be true is if `scc` outlives `'static`. This is
|
||||
/// actually stricter than necessary: ideally, we'd support bounds
|
||||
/// like `for<'a: 'b`>` that might then allow us to approximate
|
||||
/// `'a` with `'b` and not `'static`. But it will have to do for
|
||||
/// now.
|
||||
fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) {
|
||||
let fr_static = self.universal_regions.fr_static;
|
||||
self.scc_values.add_all_points(scc);
|
||||
self.scc_values.add_element(scc, fr_static);
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
/// whether the "type tests" produced by typeck were satisfied;
|
||||
/// type tests encode type-outlives relationships like `T:
|
||||
|
|
|
|||
17
src/test/ui/nll/relate_tys/universe-violation.rs
Normal file
17
src/test/ui/nll/relate_tys/universe-violation.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Test that the NLL `relate_tys` code correctly deduces that a
|
||||
// function returning either argument CANNOT be upcast to one
|
||||
// that returns always its first argument.
|
||||
//
|
||||
// compile-flags:-Zno-leak-check
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
fn make_it() -> fn(&'static u32) -> &'static u32 {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: fn(_) -> _ = make_it();
|
||||
let b: fn(&u32) -> &u32 = a;
|
||||
drop(a);
|
||||
}
|
||||
14
src/test/ui/nll/relate_tys/universe-violation.stderr
Normal file
14
src/test/ui/nll/relate_tys/universe-violation.stderr
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
error: higher-ranked subtype error
|
||||
--> $DIR/universe-violation.rs:14:9
|
||||
|
|
||||
LL | let a: fn(_) -> _ = make_it();
|
||||
| ^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/universe-violation.rs:15:9
|
||||
|
|
||||
LL | let b: fn(&u32) -> &u32 = a;
|
||||
| ^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue