formalize giving ownership of region vars to region inf. context

This commit is contained in:
Niko Matsakis 2017-11-06 05:21:48 -05:00
parent 109c9a79ed
commit 51ce1f9493
7 changed files with 102 additions and 88 deletions

View File

@ -1029,6 +1029,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let var_name = self.tcx.hir.name(var_node_id);
format!(" for capture of `{}` by closure", var_name)
}
infer::NLL(..) => bug!("NLL variable found in lexical phase"),
};
struct_span_err!(self.tcx.sess, var_origin.span(), E0495,

View File

@ -16,7 +16,6 @@ pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*;
pub use ty::IntVarValue;
pub use self::freshen::TypeFreshener;
pub use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData};
use hir::def_id::DefId;
use middle::free_region::{FreeRegionMap, RegionRelations};
@ -25,7 +24,7 @@ use middle::lang_items;
use mir::tcx::LvalueTy;
use ty::subst::{Kind, Subst, Substs};
use ty::{TyVid, IntVid, FloatVid};
use ty::{self, RegionVid, Ty, TyCtxt};
use ty::{self, Ty, TyCtxt};
use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use ty::relate::RelateResult;
@ -42,6 +41,7 @@ use arena::DroplessArena;
use self::combine::CombineFields;
use self::higher_ranked::HrMatchResult;
use self::region_constraints::{RegionConstraintCollector, RegionSnapshot};
use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins};
use self::lexical_region_resolve::LexicalRegionResolutions;
use self::type_variable::TypeVariableOrigin;
use self::unify_key::ToType;
@ -321,7 +321,7 @@ pub enum LateBoundRegionConversionTime {
/// Reasons to create a region inference variable
///
/// See `error_reporting` module for more details
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pub enum RegionVariableOrigin {
// Region variables created for ill-categorized reasons,
// mostly indicates places in need of refactoring
@ -349,6 +349,20 @@ pub enum RegionVariableOrigin {
UpvarRegion(ty::UpvarId, Span),
BoundRegionInCoherence(ast::Name),
// This origin is used for the inference variables that we create
// during NLL region processing.
NLL(NLLRegionVariableOrigin),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum NLLRegionVariableOrigin {
// During NLL region processing, we create variables for free
// regions that we encounter in the function signature and
// elsewhere. This origin indices we've got one of those.
FreeRegion,
Inferred(::mir::visit::TyContext),
}
#[derive(Copy, Clone, Debug)]
@ -1030,11 +1044,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.new_key(None)
}
/// Create a fresh region variable with the next available index.
///
/// # Parameters
///
/// - `origin`: information about why we created this variable, for use
/// during diagnostics / error-reporting.
pub fn next_region_var(&self, origin: RegionVariableOrigin)
-> ty::Region<'tcx> {
self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
}
/// Just a convenient wrapper of `next_region_var` for using during NLL.
pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
-> ty::Region<'tcx> {
self.next_region_var(RegionVariableOrigin::NLL(origin))
}
/// Create a region inference variable for the given
/// region parameter definition.
pub fn region_var_for_def(&self,
@ -1166,19 +1192,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.borrow_region_constraints().take_and_reset_data()
}
/// Returns the number of region variables created thus far.
pub fn num_region_vars(&self) -> usize {
self.borrow_region_constraints().var_origins().len()
}
/// Returns an iterator over all region variables created thus far.
pub fn all_region_vars(&self) -> impl Iterator<Item = RegionVid> {
self.borrow_region_constraints().var_origins().indices()
}
/// Returns the origin of a given region variable.
pub fn region_var_origin(&self, var: RegionVid) -> RegionVariableOrigin {
self.borrow_region_constraints().var_origins()[var].clone()
/// Takes ownership of the list of variable regions. This implies
/// that all the region constriants have already been taken, and
/// hence that `resolve_regions_and_report_errors` can never be
/// called. This is used only during NLL processing to "hand off" ownership
/// of the set of region vairables into the NLL region context.
pub fn take_region_var_origins(&self) -> VarOrigins {
let (var_origins, data) = self.region_constraints.borrow_mut()
.take()
.expect("regions already resolved")
.into_origins_and_data();
assert!(data.is_empty());
var_origins
}
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
@ -1609,7 +1634,8 @@ impl RegionVariableOrigin {
EarlyBoundRegion(a, ..) => a,
LateBoundRegion(a, ..) => a,
BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP,
UpvarRegion(_, a) => a
UpvarRegion(_, a) => a,
NLL(..) => bug!("NLL variable used with `span`"),
}
}
}

View File

@ -811,7 +811,7 @@ make_mir_visitor!(MutVisitor,mut);
/// Extra information passed to `visit_ty` and friends to give context
/// about where the type etc appears.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum TyContext {
LocalDecl {
/// The index of the local variable we are visiting.

View File

@ -64,7 +64,8 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Create the region inference context, generate the constraints,
// and then solve them.
let mut regioncx = RegionInferenceContext::new(infcx, free_regions, mir);
let var_origins = infcx.take_region_var_origins();
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
let param_env = infcx.tcx.param_env(def_id);
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
regioncx.solve(infcx, &mir);

View File

@ -10,6 +10,9 @@
use super::free_regions::FreeRegions;
use rustc::infer::InferCtxt;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::region_constraints::VarOrigins;
use rustc::mir::{Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
@ -25,23 +28,17 @@ pub struct RegionInferenceContext<'tcx> {
/// from as well as its final inferred value.
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// The indices of all "free regions" in scope. These are the
/// lifetime parameters (anonymous and named) declared in the
/// function signature:
///
/// fn foo<'a, 'b>(x: &Foo<'a, 'b>)
/// ^^ ^^ ^
///
/// These indices will be from 0..N, as it happens, but we collect
/// them into a vector for convenience.
free_regions: Vec<RegionVid>,
/// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>,
}
#[derive(Default)]
struct RegionDefinition<'tcx> {
/// Why we created this variable. Mostly these will be
/// `RegionVariableOrigin::NLL`, but some variables get created
/// elsewhere in the code with other causes (e.g., instantiation
/// late-bound-regions).
origin: RegionVariableOrigin,
/// If this is a free-region, then this is `Some(X)` where `X` is
/// the name of the region.
name: Option<ty::Region<'tcx>>,
@ -112,15 +109,16 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
/// `num_region_variables` valid inference variables; the first N
/// of those will be constant regions representing the free
/// regions defined in `free_regions`.
pub fn new(
infcx: &InferCtxt<'_, '_, 'tcx>,
free_regions: &FreeRegions<'tcx>,
mir: &Mir<'tcx>,
) -> Self {
pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self {
// Create a RegionDefinition for each inference variable.
let definitions = var_origins
.into_iter()
.map(|origin| RegionDefinition::new(origin))
.collect();
let mut result = Self {
definitions: infcx.all_region_vars().map(|_| RegionDefinition::default()).collect(),
definitions: definitions,
constraints: Vec::new(),
free_regions: Vec::new(),
};
result.init_free_regions(free_regions, mir);
@ -155,7 +153,11 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
// For each free region X:
for (free_region, &variable) in indices {
self.free_regions.push(variable);
// These should be free-region variables.
assert!(match self.definitions[variable].origin {
RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
_ => false,
});
// Initialize the name and a few other details.
self.definitions[variable].name = Some(free_region);
@ -262,10 +264,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
fn propagate_constraints(
&mut self,
mir: &Mir<'tcx>,
) -> Vec<(RegionVid, Span, RegionVid)> {
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, RegionVid)> {
let mut changed = true;
let mut dfs = Dfs::new(mir);
let mut error_regions = FxHashSet();
@ -393,3 +392,17 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> {
changed
}
}
impl<'tcx> RegionDefinition<'tcx> {
fn new(origin: RegionVariableOrigin) -> Self {
// Create a new region definition. Note that, for free
// regions, these fields get updated later in
// `init_free_regions`.
Self {
origin,
name: None,
constant: false,
value: Region::default(),
}
}
}

View File

@ -9,14 +9,13 @@
// except according to those terms.
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::ty::subst::{Kind, Substs};
use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
use rustc::ty::subst::Substs;
use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP;
use std::collections::HashMap;
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use super::ToRegionVid;
use super::free_regions::FreeRegions;
/// Replaces all free regions appearing in the MIR with fresh
@ -29,14 +28,16 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
// Create inference variables for each of the free regions
// declared on the function signature.
let free_region_inference_vars = (0..free_regions.indices.len())
.map(|_| {
infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
.map(RegionVid::new)
.map(|vid_expected| {
let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
assert_eq!(vid_expected, r.to_region_vid());
r
})
.collect();
let mut visitor = NLLVisitor {
infcx,
lookup_map: HashMap::new(),
free_regions,
free_region_inference_vars,
arg_count: mir.arg_count,
@ -45,7 +46,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
}
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, TyContext>,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &'a FreeRegions<'tcx>,
free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
@ -56,14 +56,15 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
/// Replaces all regions appearing in `value` with fresh inference
/// variables. This is what we do for almost the entire MIR, with
/// the exception of the declared types of our arguments.
fn renumber_regions<T>(&mut self, value: &T) -> T
fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
where
T: TypeFoldable<'tcx>,
{
self.infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
let origin = NLLRegionVariableOrigin::Inferred(ty_context);
self.infcx.next_nll_region_var(origin)
})
}
@ -81,26 +82,6 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
})
}
fn store_region(&mut self, region: &RegionKind, lookup: TyContext) {
if let RegionKind::ReVar(rid) = *region {
self.lookup_map.entry(rid).or_insert(lookup);
}
}
fn store_ty_regions(&mut self, ty: &Ty<'tcx>, ty_context: TyContext) {
for region in ty.regions() {
self.store_region(region, ty_context);
}
}
fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) {
if let Some(ty) = kind.as_type() {
self.store_ty_regions(&ty, ty_context);
} else if let Some(region) = kind.as_region() {
self.store_region(region, ty_context);
}
}
fn is_argument_or_return_slot(&self, local: Local) -> bool {
// The first argument is return slot, next N are arguments.
local.index() <= self.arg_count
@ -118,26 +99,21 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
*ty = if is_arg {
self.renumber_free_regions(&old_ty)
} else {
self.renumber_regions(&old_ty)
self.renumber_regions(ty_context, &old_ty)
};
self.store_ty_regions(ty, ty_context);
}
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
*substs = self.renumber_regions(&{ *substs });
let ty_context = TyContext::Location(location);
for kind in *substs {
self.store_kind_regions(kind, ty_context);
}
*substs = self.renumber_regions(ty_context, &{ *substs });
}
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
match *rvalue {
Rvalue::Ref(ref mut r, _, _) => {
let old_r = *r;
*r = self.renumber_regions(&old_r);
let ty_context = TyContext::Location(location);
self.store_region(r, ty_context);
*r = self.renumber_regions(ty_context, &old_r);
}
Rvalue::Use(..) |
Rvalue::Repeat(..) |
@ -156,11 +132,8 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
}
fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
*substs = self.renumber_regions(substs);
let ty_context = TyContext::Location(location);
for kind in substs.substs {
self.store_kind_regions(kind, ty_context);
}
*substs = self.renumber_regions(ty_context, substs);
}
fn visit_statement(

View File

@ -11,8 +11,8 @@
//! This pass type-checks the MIR to ensure it is not broken.
#![allow(unreachable_code)]
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime,
RegionConstraintData, UnitResult};
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::traits::{self, FulfillmentContext};
use rustc::ty::error::TypeError;
use rustc::ty::fold::TypeFoldable;