permit ClosureOutlivesRequirement to constrain regions or types

This commit is contained in:
Niko Matsakis 2017-12-04 11:37:34 -05:00
parent c7cfa2367b
commit 5804637a81
6 changed files with 96 additions and 38 deletions

View File

@ -536,14 +536,29 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
impl_stable_hash_for!(struct mir::Location { block, statement_index });
impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> {
num_external_vids,
outlives_requirements
});
impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
free_region,
impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> {
subject,
outlived_free_region,
blame_span
});
impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::ClosureOutlivesSubject<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
mir::ClosureOutlivesSubject::Ty(ref ty) => {
ty.hash_stable(hcx, hasher);
}
mir::ClosureOutlivesSubject::Region(ref region) => {
region.hash_stable(hcx, hasher);
}
}
}
}

View File

@ -1832,7 +1832,7 @@ pub struct GeneratorLayout<'tcx> {
/// can be extracted from its type and constrained to have the given
/// outlives relationship.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ClosureRegionRequirements {
pub struct ClosureRegionRequirements<'gcx> {
/// The number of external regions defined on the closure. In our
/// example above, it would be 3 -- one for `'static`, then `'1`
/// and `'2`. This is just used for a sanity check later on, to
@ -1842,15 +1842,15 @@ pub struct ClosureRegionRequirements {
/// Requirements between the various free regions defined in
/// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
pub outlives_requirements: Vec<ClosureOutlivesRequirement<'gcx>>,
}
/// Indicates an outlives constraint between two free-regions declared
/// on the closure.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
pub struct ClosureOutlivesRequirement {
// This region ...
pub free_region: ty::RegionVid,
/// Indicates an outlives constraint between a type or between two
/// free-regions declared on the closure.
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ClosureOutlivesRequirement<'tcx> {
// This region or type ...
pub subject: ClosureOutlivesSubject<'tcx>,
// .. must outlive this one.
pub outlived_free_region: ty::RegionVid,
@ -1859,6 +1859,23 @@ pub struct ClosureOutlivesRequirement {
pub blame_span: Span,
}
/// The subject of a ClosureOutlivesRequirement -- that is, the thing
/// that must outlive some region.
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum ClosureOutlivesSubject<'tcx> {
/// Subject is a type, typically a type parameter, but could also
/// be a projection. Indicates a requirement like `T: 'a` being
/// passed to the caller, where the type here is `T`.
///
/// The type here is guaranteed not to contain any free regions at
/// present.
Ty(Ty<'tcx>),
/// Subject is a free region from the closure. Indicates a requirement
/// like `'a: 'b` being passed to the caller; the region here is `'a`.
Region(ty::RegionVid),
}
/*
* TypeFoldable implementations for MIR types
*/

View File

@ -193,7 +193,7 @@ define_maps! { <'tcx>
/// Borrow checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements<'tcx>>,
/// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence.

View File

@ -65,7 +65,7 @@ pub fn provide(providers: &mut Providers) {
fn mir_borrowck<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
) -> Option<ClosureRegionRequirements> {
) -> Option<ClosureRegionRequirements<'tcx>> {
let input_mir = tcx.mir_validated(def_id);
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
@ -89,7 +89,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
input_mir: &Mir<'gcx>,
def_id: DefId,
) -> Option<ClosureRegionRequirements> {
) -> Option<ClosureRegionRequirements<'gcx>> {
let tcx = infcx.tcx;
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);

View File

@ -9,11 +9,12 @@
// except according to those terms.
use rustc::hir::def_id::DefId;
use rustc::mir::{ClosureRegionRequirements, Mir};
use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir};
use rustc::infer::InferCtxt;
use rustc::ty::{self, RegionKind, RegionVid};
use rustc::util::nodemap::FxHashMap;
use std::collections::BTreeSet;
use std::fmt::Debug;
use std::io;
use transform::MirSource;
use util::liveness::{LivenessResults, LocalSet};
@ -73,7 +74,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
move_data: &MoveData<'tcx>,
) -> (
RegionInferenceContext<'tcx>,
Option<ClosureRegionRequirements>,
Option<ClosureRegionRequirements<'gcx>>,
) {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
@ -263,9 +264,13 @@ fn for_each_region_constraint(
with_msg: &mut FnMut(&str) -> io::Result<()>,
) -> io::Result<()> {
for req in &closure_region_requirements.outlives_requirements {
let subject: &Debug = match &req.subject {
ClosureOutlivesSubject::Region(subject) => subject,
ClosureOutlivesSubject::Ty(ty) => ty,
};
with_msg(&format!(
"where {:?}: {:?}",
req.free_region,
subject,
req.outlived_free_region,
))?;
}

View File

@ -15,7 +15,8 @@ use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::SubregionOrigin;
use rustc::infer::region_constraints::{GenericKind, VarOrigins};
use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use std::fmt;
@ -339,12 +340,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// Perform region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
pub(super) fn solve(
pub(super) fn solve<'gcx>(
&mut self,
infcx: &InferCtxt<'_, '_, 'tcx>,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mir_def_id: DefId,
) -> Option<ClosureRegionRequirements> {
) -> Option<ClosureRegionRequirements<'gcx>> {
assert!(self.inferred_values.is_none(), "values already inferred");
self.propagate_constraints(mir);
@ -559,10 +560,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// If `propagated_outlives_requirements` is `Some`, then we will
/// push unsatisfied obligations into there. Otherwise, we'll
/// report them as errors.
fn check_universal_regions(
fn check_universal_regions<'gcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement>>,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
) {
// The universal regions are always found in a prefix of the
// full list.
@ -583,9 +584,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
propagated_outlives_requirements.extend(outlives_requirements.drain(..));
} else {
for outlives_requirement in outlives_requirements.drain(..) {
let fr = match outlives_requirement.subject {
ClosureOutlivesSubject::Region(fr) => fr,
_ => span_bug!(
outlives_requirement.blame_span,
"check_universal_region() produced requirement w/ non-region subject"
),
};
self.report_error(
infcx,
outlives_requirement.free_region,
fr,
outlives_requirement.outlived_free_region,
outlives_requirement.blame_span,
);
@ -602,11 +611,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// Things that are to be propagated are accumulated into the
/// `outlives_requirements` vector.
fn check_universal_region(
fn check_universal_region<'gcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
longer_fr: RegionVid,
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'gcx>>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
@ -645,7 +654,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Push the constraint `fr-: shorter_fr+`
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
free_region: fr_minus,
subject: ClosureOutlivesSubject::Region(fr_minus),
outlived_free_region: shorter_fr_plus,
blame_span: blame_span,
});
@ -773,7 +782,7 @@ pub trait ClosureRegionRequirementsExt {
);
}
impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
impl<'gcx> ClosureRegionRequirementsExt for ClosureRegionRequirements<'gcx> {
/// Given an instance T of the closure type, this method
/// instantiates the "extra" requirements that we computed for the
/// closure into the inference context. This has the effect of
@ -815,17 +824,29 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
// Create the predicates.
for outlives_requirement in &self.outlives_requirements {
let region = closure_mapping[outlives_requirement.free_region];
let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
debug!(
"apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}",
region,
outlived_region,
outlives_requirement
);
// FIXME, this origin is not entirely suitable.
let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span);
infcx.sub_regions(origin, outlived_region, region);
match outlives_requirement.subject {
ClosureOutlivesSubject::Region(region) => {
let region = closure_mapping[region];
debug!(
"apply_requirements: region={:?} \
outlived_region={:?} \
outlives_requirements={:?}",
region,
outlived_region,
outlives_requirement
);
infcx.sub_regions(origin, outlived_region, region);
}
ClosureOutlivesSubject::Ty(_ty) => {
bug!("TODO not yet implemented -- closure outlives subject of a type");
}
}
}
}
}