1
Fork 0

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::Location { block, statement_index });
impl_stable_hash_for!(struct mir::ClosureRegionRequirements { impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> {
num_external_vids, num_external_vids,
outlives_requirements outlives_requirements
}); });
impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement { impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement<'tcx> {
free_region, subject,
outlived_free_region, outlived_free_region,
blame_span 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 /// can be extracted from its type and constrained to have the given
/// outlives relationship. /// outlives relationship.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ClosureRegionRequirements { pub struct ClosureRegionRequirements<'gcx> {
/// The number of external regions defined on the closure. In our /// The number of external regions defined on the closure. In our
/// example above, it would be 3 -- one for `'static`, then `'1` /// example above, it would be 3 -- one for `'static`, then `'1`
/// and `'2`. This is just used for a sanity check later on, to /// 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 /// Requirements between the various free regions defined in
/// indices. /// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement>, pub outlives_requirements: Vec<ClosureOutlivesRequirement<'gcx>>,
} }
/// Indicates an outlives constraint between two free-regions declared /// Indicates an outlives constraint between a type or between two
/// on the closure. /// free-regions declared on the closure.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ClosureOutlivesRequirement { pub struct ClosureOutlivesRequirement<'tcx> {
// This region ... // This region or type ...
pub free_region: ty::RegionVid, pub subject: ClosureOutlivesSubject<'tcx>,
// .. must outlive this one. // .. must outlive this one.
pub outlived_free_region: ty::RegionVid, pub outlived_free_region: ty::RegionVid,
@ -1859,6 +1859,23 @@ pub struct ClosureOutlivesRequirement {
pub blame_span: Span, 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 * 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 /// Borrow checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify. /// 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. /// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence. /// 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>( fn mir_borrowck<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId, def_id: DefId,
) -> Option<ClosureRegionRequirements> { ) -> Option<ClosureRegionRequirements<'tcx>> {
let input_mir = tcx.mir_validated(def_id); let input_mir = tcx.mir_validated(def_id);
debug!("run query mir_borrowck: {}", tcx.item_path_str(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>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
input_mir: &Mir<'gcx>, input_mir: &Mir<'gcx>,
def_id: DefId, def_id: DefId,
) -> Option<ClosureRegionRequirements> { ) -> Option<ClosureRegionRequirements<'gcx>> {
let tcx = infcx.tcx; let tcx = infcx.tcx;
let attributes = tcx.get_attrs(def_id); let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id); let param_env = tcx.param_env(def_id);

View file

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

View file

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