introduce new subtyping

This commit is contained in:
Niko Matsakis 2018-07-23 17:30:59 +03:00
parent b913df06f2
commit d9afd2bb38
16 changed files with 1015 additions and 120 deletions

View file

@ -39,7 +39,6 @@ pub struct DepGraph {
fingerprints: Lrc<Lock<IndexVec<DepNodeIndex, Fingerprint>>>
}
newtype_index!(DepNodeIndex);
impl DepNodeIndex {

View file

@ -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

View file

@ -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> {

View file

@ -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 {

View file

@ -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)

View file

@ -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),
)?;
}

View file

@ -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
}

View file

@ -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,
}
}

View file

@ -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));
}
}
}

View file

@ -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
);

View 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
}
}

View 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);
}

View 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

View 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() { }

View 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

View 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);
}