introduce new subtyping
This commit is contained in:
parent
b913df06f2
commit
d9afd2bb38
16 changed files with 1015 additions and 120 deletions
|
@ -39,7 +39,6 @@ pub struct DepGraph {
|
|||
fingerprints: Lrc<Lock<IndexVec<DepNodeIndex, Fingerprint>>>
|
||||
}
|
||||
|
||||
|
||||
newtype_index!(DepNodeIndex);
|
||||
|
||||
impl DepNodeIndex {
|
||||
|
|
|
@ -617,6 +617,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
|
||||
// If the user gave `-Zno-leak-check`, then skip the leak
|
||||
// check completely. This is wildly unsound and also not
|
||||
// unlikely to cause an ICE or two. It is intended for use
|
||||
// only during a transition period, in which the MIR typeck
|
||||
// uses the "universe-style" check, and the rest of typeck
|
||||
// uses the more conservative leak check. Since the leak
|
||||
// check is more conservative, we can't test the
|
||||
// universe-style check without disabling it.
|
||||
if self.tcx.sess.opts.debugging_opts.no_leak_check {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
// The inputs to a skolemized variable can only
|
||||
|
|
|
@ -377,6 +377,8 @@ pub enum NLLRegionVariableOrigin {
|
|||
// elsewhere. This origin indices we've got one of those.
|
||||
FreeRegion,
|
||||
|
||||
BoundRegion(ty::UniverseIndex),
|
||||
|
||||
Existential,
|
||||
}
|
||||
|
||||
|
@ -384,6 +386,7 @@ impl NLLRegionVariableOrigin {
|
|||
pub fn is_universal(self) -> bool {
|
||||
match self {
|
||||
NLLRegionVariableOrigin::FreeRegion => true,
|
||||
NLLRegionVariableOrigin::BoundRegion(..) => true,
|
||||
NLLRegionVariableOrigin::Existential => false,
|
||||
}
|
||||
}
|
||||
|
@ -1394,6 +1397,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
fn universe(&self) -> ty::UniverseIndex {
|
||||
self.universe.get()
|
||||
}
|
||||
|
||||
/// Create and return a new subunivese of the current universe;
|
||||
/// update `self.universe` to that new subuniverse. At present,
|
||||
/// used only in the NLL subtyping code, which uses the new
|
||||
/// universe-based scheme instead of the more limited leak-check
|
||||
/// scheme.
|
||||
pub fn create_subuniverse(&self) -> ty::UniverseIndex {
|
||||
let u = self.universe.get().subuniverse();
|
||||
self.universe.set(u);
|
||||
u
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
|
||||
|
|
|
@ -1353,6 +1353,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||
"generate build artifacts that are compatible with linker-based LTO."),
|
||||
no_parallel_llvm: bool = (false, parse_bool, [UNTRACKED],
|
||||
"don't run LLVM in parallel (while keeping codegen-units and ThinLTO)"),
|
||||
no_leak_check: bool = (false, parse_bool, [UNTRACKED],
|
||||
"disables the 'leak check' for subtyping; unsound, but useful for tests"),
|
||||
}
|
||||
|
||||
pub fn default_lib_output() -> CrateType {
|
||||
|
|
|
@ -1481,7 +1481,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
|
|||
/// type name in a non-zero universe is a skolemized type -- an
|
||||
/// idealized representative of "types in general" that we use for
|
||||
/// checking generic functions.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub struct UniverseIndex(u32);
|
||||
|
||||
impl UniverseIndex {
|
||||
|
@ -1489,6 +1489,19 @@ impl UniverseIndex {
|
|||
/// visible.
|
||||
pub const ROOT: Self = UniverseIndex(0);
|
||||
|
||||
/// The "max universe" -- this isn't really a valid universe, but
|
||||
/// it's useful sometimes as a "starting value" when you are
|
||||
/// taking the minimum of a (non-empty!) set of universes.
|
||||
pub const MAX: Self = UniverseIndex(::std::u32::MAX);
|
||||
|
||||
/// Creates a universe index from the given integer. Not to be
|
||||
/// used lightly lest you pick a bad value. But sometimes we
|
||||
/// convert universe indicies into integers and back for various
|
||||
/// reasons.
|
||||
pub fn from_u32(index: u32) -> Self {
|
||||
UniverseIndex(index)
|
||||
}
|
||||
|
||||
/// A "subuniverse" corresponds to being inside a `forall` quantifier.
|
||||
/// So, for example, suppose we have this type in universe `U`:
|
||||
///
|
||||
|
@ -1504,6 +1517,11 @@ impl UniverseIndex {
|
|||
UniverseIndex(self.0.checked_add(1).unwrap())
|
||||
}
|
||||
|
||||
/// True if the names in this universe are a subset of the names in `other`.
|
||||
pub fn is_subset_of(self, other: UniverseIndex) -> bool {
|
||||
self.0 <= other.0
|
||||
}
|
||||
|
||||
pub fn as_u32(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
@ -1513,6 +1531,12 @@ impl UniverseIndex {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for UniverseIndex {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "U{}", self.as_u32())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for UniverseIndex {
|
||||
fn from(index: u32) -> Self {
|
||||
UniverseIndex(index)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//! state of region inference. This code handles emitting the region
|
||||
//! context internal state.
|
||||
|
||||
use rustc::infer::NLLRegionVariableOrigin;
|
||||
use std::io::{self, Write};
|
||||
use super::{OutlivesConstraint, RegionInferenceContext};
|
||||
|
||||
|
@ -27,8 +28,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
writeln!(out, "| Free Region Mapping")?;
|
||||
|
||||
for region in self.regions() {
|
||||
if self.definitions[region].origin.is_universal() {
|
||||
let classification = self.universal_regions
|
||||
if let NLLRegionVariableOrigin::FreeRegion = self.definitions[region].origin {
|
||||
let classification = self
|
||||
.universal_regions
|
||||
.region_classification(region)
|
||||
.unwrap();
|
||||
let outlived_by = self.universal_regions.regions_outlived_by(region);
|
||||
|
@ -49,9 +51,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
for region in self.regions() {
|
||||
writeln!(
|
||||
out,
|
||||
"| {r:rw$} | {v}",
|
||||
"| {r:rw$} | {ui:4?} | {v}",
|
||||
r = format!("{:?}", region),
|
||||
rw = REGION_WIDTH,
|
||||
ui = self.region_universe(region),
|
||||
v = self.region_value_str(region),
|
||||
)?;
|
||||
}
|
||||
|
|
|
@ -401,16 +401,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// `elem`.
|
||||
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
|
||||
// Find all paths
|
||||
let (_path, r) = self
|
||||
.find_constraint_paths_between_regions(fr1, |r| {
|
||||
let (_path, r) =
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.liveness_constraints.contains(r, elem)
|
||||
})
|
||||
.unwrap();
|
||||
}).unwrap();
|
||||
r
|
||||
}
|
||||
|
||||
// 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) -> Span {
|
||||
crate fn find_outlives_blame_span(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
fr1: RegionVid,
|
||||
fr2: RegionVid,
|
||||
) -> Span {
|
||||
let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
|
||||
span
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use borrow_check::nll::constraints::graph::ConstraintGraph;
|
|||
use borrow_check::nll::constraints::{
|
||||
ConstraintIndex, ConstraintSccIndex, ConstraintSet, OutlivesConstraint,
|
||||
};
|
||||
use borrow_check::nll::region_infer::values::ToElementIndex;
|
||||
use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex};
|
||||
use borrow_check::nll::type_check::Locations;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::canonical::QueryRegionConstraint;
|
||||
|
@ -37,7 +37,7 @@ mod dump_mir;
|
|||
mod error_reporting;
|
||||
mod graphviz;
|
||||
pub mod values;
|
||||
use self::values::{RegionValueElements, RegionValues, LivenessValues};
|
||||
use self::values::{LivenessValues, RegionValueElements, RegionValues};
|
||||
|
||||
use super::ToRegionVid;
|
||||
|
||||
|
@ -66,6 +66,11 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
/// of each region.
|
||||
constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
|
||||
|
||||
/// Contains the minimum universe of any variable within the same
|
||||
/// SCC. We will ensure that no SCC contains values that are not
|
||||
/// visible from this index.
|
||||
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
|
||||
|
||||
/// The final inferred values of the region variables; we compute
|
||||
/// one value per SCC. To get the value for any given *region*,
|
||||
/// you first find which scc it is a part of.
|
||||
|
@ -85,6 +90,12 @@ struct RegionDefinition<'tcx> {
|
|||
/// info.)
|
||||
origin: NLLRegionVariableOrigin,
|
||||
|
||||
/// Which universe is this region variable defined in? This is
|
||||
/// most often `ty::UniverseIndex::ROOT`, but when we encounter
|
||||
/// forall-quantifiers like `for<'a> { 'a = 'b }`, we would create
|
||||
/// the variable for `'a` in a subuniverse.
|
||||
universe: ty::UniverseIndex,
|
||||
|
||||
/// If this is 'static or an early-bound region, then this is
|
||||
/// `Some(X)` where `X` is the name of the region.
|
||||
external_name: Option<ty::Region<'tcx>>,
|
||||
|
@ -207,39 +218,71 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// Create a RegionDefinition for each inference variable.
|
||||
let definitions: IndexVec<_, _> = var_infos
|
||||
.into_iter()
|
||||
.map(|info| RegionDefinition::new(info.origin))
|
||||
.map(|info| RegionDefinition::new(info.universe, info.origin))
|
||||
.collect();
|
||||
|
||||
// Compute the max universe used anywhere amongst the regions.
|
||||
let max_universe = definitions
|
||||
.iter()
|
||||
.map(|d| d.universe)
|
||||
.max()
|
||||
.unwrap_or(ty::UniverseIndex::ROOT);
|
||||
|
||||
let constraints = Rc::new(outlives_constraints); // freeze constraints
|
||||
let constraint_graph = Rc::new(constraints.graph(definitions.len()));
|
||||
let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph));
|
||||
|
||||
let mut scc_values = RegionValues::new(
|
||||
elements,
|
||||
universal_regions.len(),
|
||||
);
|
||||
let mut scc_values = RegionValues::new(elements, universal_regions.len(), max_universe);
|
||||
|
||||
for region in liveness_constraints.rows() {
|
||||
let scc = constraint_sccs.scc(region);
|
||||
scc_values.merge_liveness(scc, region, &liveness_constraints);
|
||||
}
|
||||
|
||||
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
|
||||
|
||||
let mut result = Self {
|
||||
definitions,
|
||||
liveness_constraints,
|
||||
constraints,
|
||||
constraint_graph,
|
||||
constraint_sccs,
|
||||
scc_universes,
|
||||
scc_values,
|
||||
type_tests,
|
||||
universal_regions,
|
||||
};
|
||||
|
||||
result.init_universal_regions();
|
||||
result.init_free_and_bound_regions();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Each SCC is the combination of many region variables which
|
||||
/// have been equated. Therefore, we can associate a universe with
|
||||
/// each SCC which is minimum of all the universes of its
|
||||
/// constituent regions -- this is because whatever value the SCC
|
||||
/// takes on must be a value that each of the regions within the
|
||||
/// SCC could have as well. This implies that the SCC must have
|
||||
/// the minimum, or narrowest, universe.
|
||||
fn compute_scc_universes(
|
||||
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
|
||||
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
) -> IndexVec<ConstraintSccIndex, ty::UniverseIndex> {
|
||||
let num_sccs = constraints_scc.num_sccs();
|
||||
let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
|
||||
|
||||
for (region_vid, region_definition) in definitions.iter_enumerated() {
|
||||
let scc = constraints_scc.scc(region_vid);
|
||||
let scc_universe = &mut scc_universes[scc];
|
||||
*scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe);
|
||||
}
|
||||
|
||||
debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
|
||||
|
||||
scc_universes
|
||||
}
|
||||
|
||||
/// Initializes the region variables for each universally
|
||||
/// quantified region (lifetime parameter). The first N variables
|
||||
/// always correspond to the regions appearing in the function
|
||||
|
@ -260,7 +303,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
/// and (b) any universally quantified regions that it outlives,
|
||||
/// which in this case is just itself. R1 (`'b`) in contrast also
|
||||
/// outlives `'a` and hence contains R0 and R1.
|
||||
fn init_universal_regions(&mut self) {
|
||||
fn init_free_and_bound_regions(&mut self) {
|
||||
// Update the names (if any)
|
||||
for (external_name, variable) in self.universal_regions.named_universal_regions() {
|
||||
debug!(
|
||||
|
@ -271,22 +314,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.definitions[variable].external_name = Some(external_name);
|
||||
}
|
||||
|
||||
// For each universally quantified region X:
|
||||
let universal_regions = self.universal_regions.clone();
|
||||
for variable in universal_regions.universal_regions() {
|
||||
// These should be free-region variables.
|
||||
assert!(match self.definitions[variable].origin {
|
||||
NLLRegionVariableOrigin::FreeRegion => true,
|
||||
NLLRegionVariableOrigin::Existential => false,
|
||||
});
|
||||
for variable in self.definitions.indices() {
|
||||
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);
|
||||
// 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);
|
||||
|
||||
// Add `end(X)` into the set for X.
|
||||
self.add_element_to_scc_of(variable, variable);
|
||||
// Add `end(X)` into the set for X.
|
||||
self.add_element_to_scc_of(variable, variable);
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::BoundRegion(ui) => {
|
||||
// Each placeholder region X outlives its
|
||||
// associated universe but nothing else.
|
||||
self.add_element_to_scc_of(variable, ui);
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::Existential => {
|
||||
// For existential, regions, nothing to do.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,6 +368,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.scc_values.region_value_str(scc)
|
||||
}
|
||||
|
||||
/// Returns access to the value of `r` for debugging purposes.
|
||||
crate fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
|
||||
let scc = self.constraint_sccs.scc(r.to_region_vid());
|
||||
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);
|
||||
|
@ -431,8 +488,32 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// ...compute the value of `B`...
|
||||
self.propagate_constraint_sccs_if_new(scc_b, visited);
|
||||
|
||||
// ...and add elements from `B` into `A`.
|
||||
self.scc_values.add_region(scc_a, scc_b);
|
||||
// ...and add elements from `B` into `A`. One complication
|
||||
// arises because of universes: If `B` contains something
|
||||
// that `A` cannot name, then `A` can only contain `B` if
|
||||
// it outlives static.
|
||||
if self.universe_compatible(scc_b, scc_a) {
|
||||
// `A` can name everything that is in `B`, so just
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
|
@ -442,6 +523,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
/// True if all the elements in the value of `scc_b` are nameable
|
||||
/// in `scc_a`. Used during constraint propagation, and only once
|
||||
/// the value of `scc_b` has been computed.
|
||||
fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
|
||||
let universe_a = self.scc_universes[scc_a];
|
||||
|
||||
// Quick check: if scc_b's declared universe is a subset of
|
||||
// scc_a's declared univese (typically, both are ROOT), then
|
||||
// it cannot contain any problematic universe elements.
|
||||
if self.scc_universes[scc_b].is_subset_of(universe_a) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we have to iterate over the universe elements in
|
||||
// B's value, and check whether all of them are nameable
|
||||
// from universe_a
|
||||
self.scc_values
|
||||
.subuniverses_contained_in(scc_b)
|
||||
.all(|u| u.is_subset_of(universe_a))
|
||||
}
|
||||
|
||||
/// 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:
|
||||
|
@ -785,8 +887,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
return true;
|
||||
}
|
||||
|
||||
self.scc_values
|
||||
.contains_points(sup_region_scc, sub_region_scc)
|
||||
self.scc_values.contains_points(sup_region_scc, sub_region_scc)
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
|
@ -830,6 +931,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::BoundRegion(universe) => {
|
||||
self.check_bound_universal_region(infcx, mir, mir_def_id, fr, universe);
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::Existential => {
|
||||
// nothing to check here
|
||||
}
|
||||
|
@ -858,6 +963,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
|
||||
|
||||
// Because this free region must be in the ROOT universe, we
|
||||
// know it cannot contain any bound universes.
|
||||
assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT);
|
||||
debug_assert!(
|
||||
self.scc_values
|
||||
.subuniverses_contained_in(longer_fr_scc)
|
||||
.next()
|
||||
.is_none()
|
||||
);
|
||||
|
||||
// Find every region `o` such that `fr: o`
|
||||
// (because `fr` includes `end(o)`).
|
||||
for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) {
|
||||
|
@ -910,10 +1025,68 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_bound_universal_region<'gcx>(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
_mir_def_id: DefId,
|
||||
longer_fr: RegionVid,
|
||||
universe: ty::UniverseIndex,
|
||||
) {
|
||||
debug!("check_bound_universal_region(fr={:?})", longer_fr);
|
||||
|
||||
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
|
||||
|
||||
// If we have some bound universal region `'a`, then the only
|
||||
// elements it can contain is itself -- we don't know anything
|
||||
// else about it!
|
||||
let error_element = match {
|
||||
self.scc_values
|
||||
.elements_contained_in(longer_fr_scc)
|
||||
.find(|element| match element {
|
||||
RegionElement::Location(_) => true,
|
||||
RegionElement::RootUniversalRegion(_) => true,
|
||||
RegionElement::SubUniversalRegion(ui) => *ui != universe,
|
||||
})
|
||||
} {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Find the region that introduced this `error_element`.
|
||||
let error_region = match error_element {
|
||||
RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
|
||||
RegionElement::RootUniversalRegion(r) => r,
|
||||
RegionElement::SubUniversalRegion(error_ui) => self
|
||||
.definitions
|
||||
.iter_enumerated()
|
||||
.filter_map(|(r, definition)| match definition.origin {
|
||||
NLLRegionVariableOrigin::BoundRegion(ui) if error_ui == ui => Some(r),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let span = self.find_outlives_blame_span(mir, longer_fr, error_region);
|
||||
|
||||
// Obviously, this error message is far from satisfactory.
|
||||
// At present, though, it only appears in unit tests --
|
||||
// the AST-based checker uses a more conservative check,
|
||||
// so to even see this error, one must pass in a special
|
||||
// flag.
|
||||
let mut diag = infcx
|
||||
.tcx
|
||||
.sess
|
||||
.struct_span_err(span, &format!("higher-ranked subtype error"));
|
||||
diag.emit();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
fn new(rv_origin: RegionVariableOrigin) -> Self {
|
||||
fn new(universe: ty::UniverseIndex, rv_origin: RegionVariableOrigin) -> Self {
|
||||
// Create a new region definition. Note that, for free
|
||||
// regions, the `external_name` field gets updated later in
|
||||
// `init_universal_regions`.
|
||||
|
@ -925,6 +1098,7 @@ impl<'tcx> RegionDefinition<'tcx> {
|
|||
|
||||
Self {
|
||||
origin,
|
||||
universe,
|
||||
external_name: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,14 @@
|
|||
// except according to those terms.
|
||||
|
||||
use rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc::ty::RegionVid;
|
||||
use rustc::ty::{self, RegionVid};
|
||||
use rustc_data_structures::bitvec::SparseBitMatrix;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Maps between the various kinds of elements of a region value to
|
||||
/// the internal indices that w use.
|
||||
/// Maps between a `Location` and a `PointIndex` (and vice versa).
|
||||
crate struct RegionValueElements {
|
||||
/// For each basic block, how many points are contained within?
|
||||
statements_before_block: IndexVec<BasicBlock, usize>,
|
||||
|
@ -98,6 +97,12 @@ impl RegionValueElements {
|
|||
/// graph. Constructed efficiently from `RegionValueElements`.
|
||||
newtype_index!(PointIndex { DEBUG_FORMAT = "PointIndex({})" });
|
||||
|
||||
/// A single integer representing a (non-zero) `UniverseIndex`.
|
||||
/// Computed just by subtracting one from `UniverseIndex`; this is
|
||||
/// because the `0` value for `UniverseIndex` represents the root
|
||||
/// universe, and we don't need/want a bit for that one.
|
||||
newtype_index!(PlaceholderIndex { DEBUG_FORMAT = "PointIndex({})" });
|
||||
|
||||
/// An individual element in a region value -- the value of a
|
||||
/// particular region variable consists of a set of these elements.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -108,6 +113,10 @@ crate enum RegionElement {
|
|||
/// A universally quantified region from the root universe (e.g.,
|
||||
/// a lifetime parameter).
|
||||
RootUniversalRegion(RegionVid),
|
||||
|
||||
/// A subuniverse from a subuniverse (e.g., instantiated from a
|
||||
/// `for<'a> fn(&'a u32)` type).
|
||||
SubUniversalRegion(ty::UniverseIndex),
|
||||
}
|
||||
|
||||
/// When we initially compute liveness, we use a bit matrix storing
|
||||
|
@ -135,11 +144,7 @@ impl<N: Idx> LivenessValues<N> {
|
|||
|
||||
/// Adds the given element to the value for the given region. Returns true if
|
||||
/// the element is newly added (i.e., was not already present).
|
||||
crate fn add_element(
|
||||
&mut self,
|
||||
row: N,
|
||||
location: Location,
|
||||
) -> bool {
|
||||
crate fn add_element(&mut self, row: N, location: Location) -> bool {
|
||||
debug!("LivenessValues::add(r={:?}, location={:?})", row, location);
|
||||
let index = self.elements.point_from_location(location);
|
||||
self.points.add(row, index)
|
||||
|
@ -164,20 +169,38 @@ impl<N: Idx> LivenessValues<N> {
|
|||
.into_iter()
|
||||
.flat_map(|set| set.iter())
|
||||
.map(|p| self.elements.to_location(p))
|
||||
.map(RegionElement::Location)
|
||||
.map(RegionElement::Location),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the values for a set of regions. These are stored in a
|
||||
/// compact `SparseBitMatrix` representation, with one row per region
|
||||
/// variable. The columns consist of either universal regions or
|
||||
/// points in the CFG.
|
||||
/// Stores the full values for a set of regions (in contrast to
|
||||
/// `LivenessValues`, which only stores those points in the where a
|
||||
/// region is live). The full value for a region may contain points in
|
||||
/// the CFG, but also free regions as well as bound universe
|
||||
/// placeholders.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo(x: &'a u32) -> &'a u32 {
|
||||
/// let y: &'0 u32 = x; // let's call this `'0`
|
||||
/// y
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, the variable `'0` would contain the free region `'a`,
|
||||
/// because (since it is returned) it must live for at least `'a`. But
|
||||
/// it would also contain various points from within the function.
|
||||
#[derive(Clone)]
|
||||
crate struct RegionValues<N: Idx> {
|
||||
elements: Rc<RegionValueElements>,
|
||||
points: SparseBitMatrix<N, PointIndex>,
|
||||
free_regions: SparseBitMatrix<N, RegionVid>,
|
||||
|
||||
/// Placeholders represent bound regions -- so something like `'a`
|
||||
/// in for<'a> fn(&'a u32)`.
|
||||
placeholders: SparseBitMatrix<N, PlaceholderIndex>,
|
||||
}
|
||||
|
||||
impl<N: Idx> RegionValues<N> {
|
||||
|
@ -187,21 +210,20 @@ impl<N: Idx> RegionValues<N> {
|
|||
crate fn new(
|
||||
elements: &Rc<RegionValueElements>,
|
||||
num_universal_regions: usize,
|
||||
max_universe: ty::UniverseIndex,
|
||||
) -> Self {
|
||||
let num_placeholders = max_universe.as_usize();
|
||||
Self {
|
||||
elements: elements.clone(),
|
||||
points: SparseBitMatrix::new(elements.num_points),
|
||||
free_regions: SparseBitMatrix::new(num_universal_regions),
|
||||
placeholders: SparseBitMatrix::new(num_placeholders),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the given element to the value for the given region. Returns true if
|
||||
/// the element is newly added (i.e., was not already present).
|
||||
crate fn add_element(
|
||||
&mut self,
|
||||
r: N,
|
||||
elem: impl ToElementIndex,
|
||||
) -> bool {
|
||||
crate fn add_element(&mut self, r: N, elem: impl ToElementIndex) -> bool {
|
||||
debug!("add(r={:?}, elem={:?})", r, elem);
|
||||
elem.add_to_row(self, r)
|
||||
}
|
||||
|
@ -214,16 +236,13 @@ impl<N: Idx> RegionValues<N> {
|
|||
/// Add all elements in `r_from` to `r_to` (because e.g. `r_to:
|
||||
/// r_from`).
|
||||
crate fn add_region(&mut self, r_to: N, r_from: N) -> bool {
|
||||
self.points.merge(r_from, r_to) | self.free_regions.merge(r_from, r_to)
|
||||
// FIXME universes?
|
||||
self.points.merge(r_from, r_to)
|
||||
| self.free_regions.merge(r_from, r_to)
|
||||
| self.placeholders.merge(r_from, r_to)
|
||||
}
|
||||
|
||||
/// True if the region `r` contains the given element.
|
||||
crate fn contains(
|
||||
&self,
|
||||
r: N,
|
||||
elem: impl ToElementIndex,
|
||||
) -> bool {
|
||||
crate fn contains(&self, r: N, elem: impl ToElementIndex) -> bool {
|
||||
elem.contained_in_row(self, r)
|
||||
}
|
||||
|
||||
|
@ -255,10 +274,7 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
/// Returns the locations contained within a given region `r`.
|
||||
crate fn locations_outlived_by<'a>(
|
||||
&'a self,
|
||||
r: N,
|
||||
) -> impl Iterator<Item = Location> + 'a {
|
||||
crate fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
|
||||
self.points
|
||||
.row(r)
|
||||
.into_iter()
|
||||
|
@ -277,19 +293,32 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
/// Returns all the elements contained in a given region's value.
|
||||
crate fn elements_contained_in<'a>(
|
||||
crate fn subuniverses_contained_in<'a>(
|
||||
&'a self,
|
||||
r: N,
|
||||
) -> impl Iterator<Item = RegionElement> + 'a {
|
||||
let points_iter = self
|
||||
.locations_outlived_by(r)
|
||||
.map(RegionElement::Location);
|
||||
) -> impl Iterator<Item = ty::UniverseIndex> + 'a {
|
||||
self.placeholders
|
||||
.row(r)
|
||||
.into_iter()
|
||||
.flat_map(|set| set.iter())
|
||||
.map(|p| ty::UniverseIndex::from_u32((p.index() + 1) as u32))
|
||||
}
|
||||
|
||||
/// Returns all the elements contained in a given region's value.
|
||||
crate fn elements_contained_in<'a>(&'a self, r: N) -> impl Iterator<Item = RegionElement> + 'a {
|
||||
let points_iter = self.locations_outlived_by(r).map(RegionElement::Location);
|
||||
|
||||
let free_regions_iter = self
|
||||
.universal_regions_outlived_by(r)
|
||||
.map(RegionElement::RootUniversalRegion);
|
||||
|
||||
points_iter.chain(free_regions_iter)
|
||||
let subuniverses_iter = self
|
||||
.subuniverses_contained_in(r)
|
||||
.map(RegionElement::SubUniversalRegion);
|
||||
|
||||
points_iter
|
||||
.chain(free_regions_iter)
|
||||
.chain(subuniverses_iter)
|
||||
}
|
||||
|
||||
/// Returns a "pretty" string value of the region. Meant for debugging.
|
||||
|
@ -299,58 +328,46 @@ impl<N: Idx> RegionValues<N> {
|
|||
}
|
||||
|
||||
crate trait ToElementIndex: Debug + Copy {
|
||||
fn add_to_row<N: Idx>(
|
||||
self,
|
||||
values: &mut RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool;
|
||||
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool;
|
||||
|
||||
fn contained_in_row<N: Idx>(
|
||||
self,
|
||||
values: &RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool;
|
||||
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool;
|
||||
}
|
||||
|
||||
impl ToElementIndex for Location {
|
||||
fn add_to_row<N: Idx>(
|
||||
self,
|
||||
values: &mut RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool {
|
||||
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
|
||||
let index = values.elements.point_from_location(self);
|
||||
values.points.add(row, index)
|
||||
}
|
||||
|
||||
fn contained_in_row<N: Idx>(
|
||||
self,
|
||||
values: &RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool {
|
||||
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
|
||||
let index = values.elements.point_from_location(self);
|
||||
values.points.contains(row, index)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToElementIndex for RegionVid {
|
||||
fn add_to_row<N: Idx>(
|
||||
self,
|
||||
values: &mut RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool {
|
||||
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
|
||||
values.free_regions.add(row, self)
|
||||
}
|
||||
|
||||
fn contained_in_row<N: Idx>(
|
||||
self,
|
||||
values: &RegionValues<N>,
|
||||
row: N,
|
||||
) -> bool {
|
||||
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
|
||||
values.free_regions.contains(row, self)
|
||||
}
|
||||
}
|
||||
|
||||
crate fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
|
||||
impl ToElementIndex for ty::UniverseIndex {
|
||||
fn add_to_row<N: Idx>(self, values: &mut RegionValues<N>, row: N) -> bool {
|
||||
let index = PlaceholderIndex::new(self.as_usize() - 1);
|
||||
values.placeholders.add(row, index)
|
||||
}
|
||||
|
||||
fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
|
||||
let index = PlaceholderIndex::new(self.as_usize() - 1);
|
||||
values.placeholders.contains(row, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
|
||||
let mut result = String::new();
|
||||
result.push_str("{");
|
||||
|
||||
|
@ -394,6 +411,17 @@ crate fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) ->
|
|||
push_sep(&mut result);
|
||||
result.push_str(&format!("{:?}", fr));
|
||||
}
|
||||
|
||||
RegionElement::SubUniversalRegion(ur) => {
|
||||
if let Some((location1, location2)) = open_location {
|
||||
push_sep(&mut result);
|
||||
push_location_range(&mut result, location1, location2);
|
||||
open_location = None;
|
||||
}
|
||||
|
||||
push_sep(&mut result);
|
||||
result.push_str(&format!("{:?}", ur));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ use rustc::mir::*;
|
|||
use rustc::traits::query::type_op;
|
||||
use rustc::traits::query::{Fallible, NoSolution};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants, RegionVid};
|
||||
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
|
@ -73,6 +73,7 @@ macro_rules! span_mirbug_and_err {
|
|||
mod constraint_conversion;
|
||||
mod input_output;
|
||||
mod liveness;
|
||||
mod relate_tys;
|
||||
|
||||
/// Type checks the given `mir` in the context of the inference
|
||||
/// context `infcx`. Returns any region constraints that have yet to
|
||||
|
@ -796,16 +797,38 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, locations: Locations) -> Fallible<()> {
|
||||
let param_env = self.param_env;
|
||||
self.fully_perform_op(
|
||||
relate_tys::sub_types(
|
||||
self.infcx,
|
||||
sub,
|
||||
sup,
|
||||
locations,
|
||||
param_env.and(type_op::subtype::Subtype::new(sub, sup)),
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
||||
fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> Fallible<()> {
|
||||
let param_env = self.param_env;
|
||||
self.fully_perform_op(locations, param_env.and(type_op::eq::Eq::new(b, a)))
|
||||
relate_tys::eq_types(
|
||||
self.infcx,
|
||||
a,
|
||||
b,
|
||||
locations,
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
||||
fn eq_canonical_type_and_type(
|
||||
&mut self,
|
||||
a: CanonicalTy<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
) -> Fallible<()> {
|
||||
relate_tys::eq_canonical_type_and_type(
|
||||
self.infcx,
|
||||
a,
|
||||
b,
|
||||
locations,
|
||||
self.borrowck_context.as_mut().map(|x| &mut **x),
|
||||
)
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
|
@ -877,20 +900,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
);
|
||||
};
|
||||
}
|
||||
StatementKind::UserAssertTy(ref c_ty, ref local) => {
|
||||
let local_ty = mir.local_decls()[*local].ty;
|
||||
let (ty, _) = self.infcx
|
||||
.instantiate_canonical_with_fresh_inference_vars(stmt.source_info.span, c_ty);
|
||||
debug!(
|
||||
"check_stmt: user_assert_ty ty={:?} local_ty={:?}",
|
||||
ty, local_ty
|
||||
);
|
||||
if let Err(terr) = self.eq_types(ty, local_ty, Locations::All) {
|
||||
StatementKind::UserAssertTy(c_ty, local) => {
|
||||
let local_ty = mir.local_decls()[local].ty;
|
||||
if let Err(terr) = self.eq_canonical_type_and_type(c_ty, local_ty, Locations::All) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
stmt,
|
||||
"bad type assert ({:?} = {:?}): {:?}",
|
||||
ty,
|
||||
c_ty,
|
||||
local_ty,
|
||||
terr
|
||||
);
|
||||
|
|
510
src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
Normal file
510
src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs
Normal file
|
@ -0,0 +1,510 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::nll::constraints::OutlivesConstraint;
|
||||
use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
use borrow_check::nll::ToRegionVid;
|
||||
use rustc::infer::canonical::{Canonical, CanonicalVarInfos};
|
||||
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
|
||||
use rustc::traits::query::Fallible;
|
||||
use rustc::ty::fold::{TypeFoldable, TypeVisitor};
|
||||
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
use rustc::ty::subst::Kind;
|
||||
use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
use std::mem;
|
||||
|
||||
pub(super) fn sub_types<'tcx>(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
|
||||
TypeRelating::new(
|
||||
infcx,
|
||||
ty::Variance::Covariant,
|
||||
locations,
|
||||
borrowck_context,
|
||||
ty::Slice::empty(),
|
||||
).relate(&a, &b)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn eq_types<'tcx>(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
|
||||
TypeRelating::new(
|
||||
infcx,
|
||||
ty::Variance::Invariant,
|
||||
locations,
|
||||
borrowck_context,
|
||||
ty::Slice::empty(),
|
||||
).relate(&a, &b)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn eq_canonical_type_and_type<'tcx>(
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
a: CanonicalTy<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
locations: Locations,
|
||||
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
|
||||
) -> Fallible<()> {
|
||||
debug!(
|
||||
"eq_canonical_type_and_type(a={:?}, b={:?}, locations={:?})",
|
||||
a, b, locations
|
||||
);
|
||||
let Canonical {
|
||||
variables: a_variables,
|
||||
value: a_value,
|
||||
} = a;
|
||||
TypeRelating::new(
|
||||
infcx,
|
||||
ty::Variance::Invariant,
|
||||
locations,
|
||||
borrowck_context,
|
||||
a_variables,
|
||||
).relate(&a_value, &b)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
|
||||
/// How are we relating `a` and `b`?
|
||||
///
|
||||
/// - covariant means `a <: b`
|
||||
/// - contravariant means `b <: a`
|
||||
/// - invariant means `a == b
|
||||
/// - bivariant means that it doesn't matter
|
||||
ambient_variance: ty::Variance,
|
||||
|
||||
/// When we pass through a set of binders (e.g., when looking into
|
||||
/// a `fn` type), we push a new bound region scope onto here. This
|
||||
/// will contain the instantiated region for each region in those
|
||||
/// binders. When we then encounter a `ReLateBound(d, br)`, we can
|
||||
/// use the debruijn index `d` to find the right scope, and then
|
||||
/// bound region name `br` to find the specific instantiation from
|
||||
/// within that scope. See `replace_bound_region`.
|
||||
///
|
||||
/// This field stores the instantiations for late-bound regions in
|
||||
/// the `a` type.
|
||||
a_scopes: Vec<BoundRegionScope>,
|
||||
|
||||
/// Same as `a_scopes`, but for the `b` type.
|
||||
b_scopes: Vec<BoundRegionScope>,
|
||||
|
||||
/// Where (and why) is this relation taking place?
|
||||
locations: Locations,
|
||||
|
||||
/// This will be `Some` when we are running the type check as part
|
||||
/// of NLL, and `None` if we are running a "sanity check".
|
||||
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
|
||||
|
||||
/// As we execute, the type on the LHS *may* come from a canonical
|
||||
/// source. In that case, we will sometimes find a constraint like
|
||||
/// `?0 = B`, where `B` is a type from the RHS. The first time we
|
||||
/// find that, we simply record `B` (and the list of scopes that
|
||||
/// tells us how to *interpret* `B`). The next time we encounter
|
||||
/// `?0`, then, we can read this value out and use it.
|
||||
///
|
||||
/// One problem: these variables may be in some other universe,
|
||||
/// how can we enforce that? I guess I could add some kind of
|
||||
/// "minimum universe constraint" that we can feed to the NLL checker.
|
||||
/// --> also, we know this doesn't happen
|
||||
canonical_var_values: IndexVec<CanonicalVar, Option<ScopesAndKind<'tcx>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ScopesAndKind<'tcx> {
|
||||
scopes: Vec<BoundRegionScope>,
|
||||
kind: Kind<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct BoundRegionScope {
|
||||
map: FxHashMap<ty::BoundRegion, RegionVid>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct UniversallyQuantified(bool);
|
||||
|
||||
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
|
||||
fn new(
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
ambient_variance: ty::Variance,
|
||||
locations: Locations,
|
||||
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
|
||||
canonical_var_infos: CanonicalVarInfos<'tcx>,
|
||||
) -> Self {
|
||||
let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len());
|
||||
Self {
|
||||
infcx,
|
||||
ambient_variance,
|
||||
borrowck_context,
|
||||
locations,
|
||||
canonical_var_values,
|
||||
a_scopes: vec![],
|
||||
b_scopes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn ambient_covariance(&self) -> bool {
|
||||
match self.ambient_variance {
|
||||
ty::Variance::Covariant | ty::Variance::Invariant => true,
|
||||
ty::Variance::Contravariant | ty::Variance::Bivariant => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn ambient_contravariance(&self) -> bool {
|
||||
match self.ambient_variance {
|
||||
ty::Variance::Contravariant | ty::Variance::Invariant => true,
|
||||
ty::Variance::Covariant | ty::Variance::Bivariant => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_scope(
|
||||
&mut self,
|
||||
value: &ty::Binder<impl TypeFoldable<'tcx>>,
|
||||
universally_quantified: UniversallyQuantified,
|
||||
) -> BoundRegionScope {
|
||||
let mut scope = BoundRegionScope::default();
|
||||
value.skip_binder().visit_with(&mut ScopeInstantiator {
|
||||
infcx: self.infcx,
|
||||
target_index: ty::INNERMOST,
|
||||
universally_quantified,
|
||||
bound_region_scope: &mut scope,
|
||||
});
|
||||
scope
|
||||
}
|
||||
|
||||
fn replace_bound_region(
|
||||
&self,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
r: ty::Region<'tcx>,
|
||||
scopes: &[BoundRegionScope],
|
||||
) -> RegionVid {
|
||||
match r {
|
||||
ty::ReLateBound(debruijn, br) => {
|
||||
// The debruijn index is a "reverse index" into the
|
||||
// scopes listing. So when we have INNERMOST (0), we
|
||||
// want the *last* scope pushed, and so forth.
|
||||
let debruijn_index = debruijn.index() - ty::INNERMOST.index();
|
||||
let scope = &scopes[scopes.len() - debruijn_index - 1];
|
||||
|
||||
// Find this bound region in that scope to map to a
|
||||
// particular region.
|
||||
scope.map[br]
|
||||
}
|
||||
|
||||
ty::ReVar(v) => *v,
|
||||
|
||||
_ => universal_regions.to_region_vid(r),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) {
|
||||
debug!("push_outlives({:?}: {:?})", sup, sub);
|
||||
|
||||
if let Some(borrowck_context) = &mut self.borrowck_context {
|
||||
borrowck_context
|
||||
.constraints
|
||||
.outlives_constraints
|
||||
.push(OutlivesConstraint {
|
||||
sup,
|
||||
sub,
|
||||
locations: self.locations,
|
||||
});
|
||||
|
||||
// FIXME all facts!
|
||||
}
|
||||
}
|
||||
|
||||
fn equate_var(
|
||||
&mut self,
|
||||
var: CanonicalVar,
|
||||
b_kind: Kind<'tcx>,
|
||||
) -> RelateResult<'tcx, Kind<'tcx>> {
|
||||
debug!("equate_var(var={:?}, b_kind={:?})", var, b_kind);
|
||||
|
||||
// We only encounter canonical variables when equating.
|
||||
assert_eq!(self.ambient_variance, ty::Variance::Invariant);
|
||||
|
||||
// The canonical variable already had a value. Equate that
|
||||
// value with `b`.
|
||||
let old_value = self.canonical_var_values[var].clone();
|
||||
if let Some(ScopesAndKind { scopes, kind }) = old_value {
|
||||
debug!("equate_var: installing kind={:?} scopes={:?}", kind, scopes);
|
||||
let old_a_scopes = mem::replace(&mut self.a_scopes, scopes);
|
||||
let result = self.relate(&kind, &b_kind);
|
||||
self.a_scopes = old_a_scopes;
|
||||
debug!("equate_var: complete, result = {:?}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Not yet. Capture the value from the RHS and carry on.
|
||||
self.canonical_var_values[var] = Some(ScopesAndKind {
|
||||
scopes: self.b_scopes.clone(),
|
||||
kind: b_kind,
|
||||
});
|
||||
debug!(
|
||||
"equate_var: capturing value {:?}",
|
||||
self.canonical_var_values[var]
|
||||
);
|
||||
|
||||
// FIXME -- technically, we should add some sort of
|
||||
// assertion that this value can be named in the universe
|
||||
// of the canonical variable. But in practice these
|
||||
// canonical variables only arise presently in cases where
|
||||
// they are in the root universe and the main typeck has
|
||||
// ensured there are no universe errors. So we just kind
|
||||
// of over look this right now.
|
||||
Ok(b_kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
|
||||
for TypeRelating<'cx, 'bccx, 'gcx, 'tcx>
|
||||
{
|
||||
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn tag(&self) -> &'static str {
|
||||
"nll::subtype"
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn relate_with_variance<T: Relate<'tcx>>(
|
||||
&mut self,
|
||||
variance: ty::Variance,
|
||||
a: &T,
|
||||
b: &T,
|
||||
) -> RelateResult<'tcx, T> {
|
||||
debug!(
|
||||
"relate_with_variance(variance={:?}, a={:?}, b={:?})",
|
||||
variance, a, b
|
||||
);
|
||||
|
||||
let old_ambient_variance = self.ambient_variance;
|
||||
self.ambient_variance = self.ambient_variance.xform(variance);
|
||||
|
||||
debug!(
|
||||
"relate_with_variance: ambient_variance = {:?}",
|
||||
self.ambient_variance
|
||||
);
|
||||
|
||||
let r = self.relate(a, b)?;
|
||||
|
||||
self.ambient_variance = old_ambient_variance;
|
||||
|
||||
debug!("relate_with_variance: r={:?}", r);
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
// Watch out for the case that we are matching a `?T` against the
|
||||
// right-hand side.
|
||||
if let ty::TyInfer(ty::CanonicalTy(var)) = a.sty {
|
||||
self.equate_var(var, b.into())?;
|
||||
Ok(a)
|
||||
} else {
|
||||
debug!(
|
||||
"tys(a={:?}, b={:?}, variance={:?})",
|
||||
a, b, self.ambient_variance
|
||||
);
|
||||
|
||||
relate::super_relate_tys(self, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
fn regions(
|
||||
&mut self,
|
||||
a: ty::Region<'tcx>,
|
||||
b: ty::Region<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Region<'tcx>> {
|
||||
if let Some(&mut BorrowCheckContext {
|
||||
universal_regions, ..
|
||||
}) = self.borrowck_context
|
||||
{
|
||||
if let ty::ReCanonical(var) = a {
|
||||
self.equate_var(*var, b.into())?;
|
||||
return Ok(a);
|
||||
}
|
||||
|
||||
debug!(
|
||||
"regions(a={:?}, b={:?}, variance={:?})",
|
||||
a, b, self.ambient_variance
|
||||
);
|
||||
|
||||
let v_a = self.replace_bound_region(universal_regions, a, &self.a_scopes);
|
||||
let v_b = self.replace_bound_region(universal_regions, b, &self.b_scopes);
|
||||
|
||||
debug!("regions: v_a = {:?}", v_a);
|
||||
debug!("regions: v_b = {:?}", v_b);
|
||||
|
||||
if self.ambient_covariance() {
|
||||
// Covariance: a <= b. Hence, `b: a`.
|
||||
self.push_outlives(v_b, v_a);
|
||||
}
|
||||
|
||||
if self.ambient_contravariance() {
|
||||
// Contravariant: b <= a. Hence, `a: b`.
|
||||
self.push_outlives(v_a, v_b);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
fn binders<T>(
|
||||
&mut self,
|
||||
a: &ty::Binder<T>,
|
||||
b: &ty::Binder<T>,
|
||||
) -> RelateResult<'tcx, ty::Binder<T>>
|
||||
where
|
||||
T: Relate<'tcx>,
|
||||
{
|
||||
// We want that
|
||||
//
|
||||
// ```
|
||||
// for<'a> fn(&'a u32) -> &'a u32 <:
|
||||
// fn(&'b u32) -> &'b u32
|
||||
// ```
|
||||
//
|
||||
// but not
|
||||
//
|
||||
// ```
|
||||
// fn(&'a u32) -> &'a u32 <:
|
||||
// for<'b> fn(&'b u32) -> &'b u32
|
||||
// ```
|
||||
//
|
||||
// We therefore proceed as follows:
|
||||
//
|
||||
// - Instantiate binders on `b` universally, yielding a universe U1.
|
||||
// - Instantiate binders on `a` existentially in U1.
|
||||
|
||||
debug!(
|
||||
"binders({:?}: {:?}, ambient_variance={:?})",
|
||||
a, b, self.ambient_variance
|
||||
);
|
||||
|
||||
if self.ambient_covariance() {
|
||||
// Covariance, so we want `for<'a> A <: for<'b...> B` --
|
||||
// therefore we replace all of the `'b...` regions with
|
||||
// placeholders and then replace `'a...` with
|
||||
// existentials. We then check if any instantiation of A
|
||||
// can match against the placeholders in B.
|
||||
|
||||
let b_scope = self.create_scope(b, UniversallyQuantified(true));
|
||||
let a_scope = self.create_scope(a, UniversallyQuantified(false));
|
||||
|
||||
debug!("binders: a_scope = {:?} (existential)", a_scope);
|
||||
debug!("binders: b_scope = {:?} (universal)", b_scope);
|
||||
|
||||
self.b_scopes.push(b_scope);
|
||||
self.a_scopes.push(a_scope);
|
||||
|
||||
// FIXME -- to be fully correct, we would set the ambient
|
||||
// variance to Covariant here. As is, we will sometimes
|
||||
// propagate down an ambient variance of Equal -- this in
|
||||
// turn causes us to report errors in some cases where
|
||||
// types perhaps *ought* to be equal. See the
|
||||
// `hr-fn-aau-eq-abu.rs` test for an example. Fixing this
|
||||
// though is a bit nontrivial: in particular, it would
|
||||
// require a more involved handling of canonical
|
||||
// variables, since we would no longer be able to rely on
|
||||
// having an `==` relationship for canonical variables.
|
||||
|
||||
self.relate(a.skip_binder(), b.skip_binder())?;
|
||||
|
||||
self.b_scopes.pop().unwrap();
|
||||
self.a_scopes.pop().unwrap();
|
||||
}
|
||||
|
||||
if self.ambient_contravariance() {
|
||||
// Contravariance, so we want `for<'a> A :> for<'b...> B`
|
||||
// -- do the opposite, therefore, of covariant. Replace
|
||||
// the `'a...` placeholders with `'b...` variables.
|
||||
|
||||
let a_scope = self.create_scope(a, UniversallyQuantified(true));
|
||||
let b_scope = self.create_scope(b, UniversallyQuantified(false));
|
||||
|
||||
debug!("binders: a_scope = {:?} (universal)", a_scope);
|
||||
debug!("binders: b_scope = {:?} (existential)", b_scope);
|
||||
|
||||
self.a_scopes.push(a_scope);
|
||||
self.b_scopes.push(b_scope);
|
||||
|
||||
self.relate(a.skip_binder(), b.skip_binder())?;
|
||||
|
||||
self.b_scopes.pop().unwrap();
|
||||
self.a_scopes.pop().unwrap();
|
||||
}
|
||||
|
||||
Ok(a.clone())
|
||||
}
|
||||
}
|
||||
|
||||
struct ScopeInstantiator<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> {
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
// The debruijn index of the scope we are instantiating.
|
||||
target_index: ty::DebruijnIndex,
|
||||
universally_quantified: UniversallyQuantified,
|
||||
bound_region_scope: &'cx mut BoundRegionScope,
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
|
||||
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
|
||||
self.target_index.shift_in(1);
|
||||
t.super_visit_with(self);
|
||||
self.target_index.shift_out(1);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
|
||||
let ScopeInstantiator {
|
||||
infcx,
|
||||
universally_quantified,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
match r {
|
||||
ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
|
||||
self.bound_region_scope.map.entry(*br).or_insert_with(|| {
|
||||
let origin = if universally_quantified.0 {
|
||||
NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse())
|
||||
} else {
|
||||
NLLRegionVariableOrigin::Existential
|
||||
};
|
||||
infcx.next_nll_region_var(origin).to_region_vid()
|
||||
});
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
27
src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs
Normal file
27
src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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() -> for<'a> fn(&'a u32, &'a u32) -> &'a u32 {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
|
||||
//~^ ERROR higher-ranked subtype error
|
||||
drop(a);
|
||||
}
|
8
src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr
Normal file
8
src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: higher-ranked subtype error
|
||||
--> $DIR/hr-fn-aaa-as-aba.rs:24:58
|
||||
|
|
||||
LL | let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
38
src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs
Normal file
38
src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test an interesting corner case: a fn that takes two arguments that
|
||||
// are references with the same lifetime is in fact equivalent to a fn
|
||||
// that takes two references with distinct lifetimes. This is true
|
||||
// because the two functions can call one another -- effectively, the
|
||||
// single lifetime `'a` is just inferred to be the intersection of the
|
||||
// two distinct lifetimes.
|
||||
//
|
||||
// compile-flags:-Zno-leak-check
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
fn make_cell_aa() -> Cell<for<'a> fn(&'a u32, &'a u32)> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn aa_eq_ab() {
|
||||
let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
|
||||
//~^ ERROR higher-ranked subtype error
|
||||
//
|
||||
// FIXME -- this .. arguably ought to be accepted. However, the
|
||||
// current leak check also rejects this example, and it's kind of
|
||||
// a pain to support it. There are some comments in `relate_tys`
|
||||
drop(a);
|
||||
}
|
||||
|
||||
fn main() { }
|
8
src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
Normal file
8
src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: higher-ranked subtype error
|
||||
--> $DIR/hr-fn-aau-eq-abu.rs:29:53
|
||||
|
|
||||
LL | let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
27
src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs
Normal file
27
src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that the NLL `relate_tys` code correctly deduces that a
|
||||
// function returning always its first argument can be upcast to one
|
||||
// that returns either first or second argument.
|
||||
//
|
||||
// compile-pass
|
||||
// compile-flags:-Zno-leak-check
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
fn make_it() -> for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: for<'a> fn(&'a u32, &'a u32) -> &'a u32 = make_it();
|
||||
drop(a);
|
||||
}
|
Loading…
Reference in a new issue