From d9afd2bb38eed6f82b2b3b862b83125a44f69044 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 23 Jul 2018 17:30:59 +0300 Subject: [PATCH] introduce new subtyping --- src/librustc/dep_graph/graph.rs | 1 - src/librustc/infer/higher_ranked/mod.rs | 12 + src/librustc/infer/mod.rs | 14 + src/librustc/session/config.rs | 2 + src/librustc/ty/mod.rs | 26 +- .../borrow_check/nll/region_infer/dump_mir.rs | 9 +- .../nll/region_infer/error_reporting/mod.rs | 14 +- .../borrow_check/nll/region_infer/mod.rs | 230 +++++++- .../borrow_check/nll/region_infer/values.rs | 160 +++--- .../borrow_check/nll/type_check/mod.rs | 49 +- .../borrow_check/nll/type_check/relate_tys.rs | 510 ++++++++++++++++++ .../ui/nll/relate_tys/hr-fn-aaa-as-aba.rs | 27 + .../ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr | 8 + .../ui/nll/relate_tys/hr-fn-aau-eq-abu.rs | 38 ++ .../ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr | 8 + .../ui/nll/relate_tys/hr-fn-aba-as-aaa.rs | 27 + 16 files changed, 1015 insertions(+), 120 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs create mode 100644 src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs create mode 100644 src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr create mode 100644 src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs create mode 100644 src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr create mode 100644 src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 2390d7eccce7..1721d1dd0e9c 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -39,7 +39,6 @@ pub struct DepGraph { fingerprints: Lrc>> } - newtype_index!(DepNodeIndex); impl DepNodeIndex { diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 43ed80b474ad..cb4e1ab65e75 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -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 diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4677f6222d44..0b84c6a0aa77 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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> { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 293b5c63cf06..54d9e24bbc0e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -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 { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index bd24b93f0293..d23bd2e51fbd 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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 for UniverseIndex { fn from(index: u32) -> Self { UniverseIndex(index) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs index dfcb185e21d1..88b34c767324 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -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), )?; } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index acc1a33ea698..4472488a7734 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -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 } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 3d830c507f24..2ab72f655352 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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>, + /// 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, + /// 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>, @@ -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, + definitions: &IndexVec>, + ) -> IndexVec { + 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, } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index f6a718fdcf8e..2d7cbfce1a31 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -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, @@ -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 LivenessValues { /// 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 LivenessValues { .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 { elements: Rc, points: SparseBitMatrix, free_regions: SparseBitMatrix, + + /// Placeholders represent bound regions -- so something like `'a` + /// in for<'a> fn(&'a u32)`. + placeholders: SparseBitMatrix, } impl RegionValues { @@ -187,21 +210,20 @@ impl RegionValues { crate fn new( elements: &Rc, 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 RegionValues { /// 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 RegionValues { } /// Returns the locations contained within a given region `r`. - crate fn locations_outlived_by<'a>( - &'a self, - r: N, - ) -> impl Iterator + 'a { + crate fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator + 'a { self.points .row(r) .into_iter() @@ -277,19 +293,32 @@ impl RegionValues { } /// 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 + 'a { - let points_iter = self - .locations_outlived_by(r) - .map(RegionElement::Location); + ) -> impl Iterator + '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 + '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 RegionValues { } crate trait ToElementIndex: Debug + Copy { - fn add_to_row( - self, - values: &mut RegionValues, - row: N, - ) -> bool; + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool; - fn contained_in_row( - self, - values: &RegionValues, - row: N, - ) -> bool; + fn contained_in_row(self, values: &RegionValues, row: N) -> bool; } impl ToElementIndex for Location { - fn add_to_row( - self, - values: &mut RegionValues, - row: N, - ) -> bool { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { let index = values.elements.point_from_location(self); values.points.add(row, index) } - fn contained_in_row( - self, - values: &RegionValues, - row: N, - ) -> bool { + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { let index = values.elements.point_from_location(self); values.points.contains(row, index) } } impl ToElementIndex for RegionVid { - fn add_to_row( - self, - values: &mut RegionValues, - row: N, - ) -> bool { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { values.free_regions.add(row, self) } - fn contained_in_row( - self, - values: &RegionValues, - row: N, - ) -> bool { + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { values.free_regions.contains(row, self) } } -crate fn region_value_str(elements: impl IntoIterator) -> String { +impl ToElementIndex for ty::UniverseIndex { + fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { + let index = PlaceholderIndex::new(self.as_usize() - 1); + values.placeholders.add(row, index) + } + + fn contained_in_row(self, values: &RegionValues, row: N) -> bool { + let index = PlaceholderIndex::new(self.as_usize() - 1); + values.placeholders.contains(row, index) + } +} + +fn region_value_str(elements: impl IntoIterator) -> String { let mut result = String::new(); result.push_str("{"); @@ -394,6 +411,17 @@ crate fn region_value_str(elements: impl IntoIterator) -> 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)); + } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index e990a99f2b41..b67de34593f8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -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 ); diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs new file mode 100644 index 000000000000..095e3cb62960 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -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 or the MIT license +// , 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, + + /// Same as `a_scopes`, but for the `b` type. + b_scopes: Vec, + + /// 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>>, +} + +#[derive(Clone, Debug)] +struct ScopesAndKind<'tcx> { + scopes: Vec, + kind: Kind<'tcx>, +} + +#[derive(Clone, Debug, Default)] +struct BoundRegionScope { + map: FxHashMap, +} + +#[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>, + 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>( + &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( + &mut self, + a: &ty::Binder, + b: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + 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>(&mut self, t: &ty::Binder) -> 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 + } +} diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs new file mode 100644 index 000000000000..84c305f5907d --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr new file mode 100644 index 000000000000..e08d848b4714 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr @@ -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 + diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs new file mode 100644 index 000000000000..d62354fece6d --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs @@ -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 or the MIT license +// , 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 fn(&'a u32, &'a u32)> { + panic!() +} + +fn aa_eq_ab() { + let a: Cell 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() { } diff --git a/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr new file mode 100644 index 000000000000..6ba969ab0ef5 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hr-fn-aau-eq-abu.rs:29:53 + | +LL | let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs new file mode 100644 index 000000000000..4f73ca3a5392 --- /dev/null +++ b/src/test/ui/nll/relate_tys/hr-fn-aba-as-aaa.rs @@ -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 or the MIT license +// , 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); +}